PropertySource in spring boot doesn't seem to work - spring

I cannot load StringDeserializer and StringSerializer with PropertySource, but all other spring.kafka related things get loaded.
Is it a bug in spring PropertySource?
My common application look like this:
#Configuration
#PropertySource("classpath:/config/kafka.properties")
public class CommonKafkaAutoConfiguration {
}
Below configuration doesn't get loaded when filename kafka.properties but okay when application.properties
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
Funny fact is following get loaded in both cases:
spring.kafka.consumer.max-poll-records=20
Update I can see that its get overriden by kafkaBinderDefaultProperties

This works fine for me...
#SpringBootApplication
#PropertySource("classpath:/config/kafka.properties")
public class So65311053Application {
public static void main(String[] args) {
SpringApplication.run(So65311053Application.class, args);
}
#Bean
public ApplicationRunner runner(KafkaTemplate<String, String> template, ConsumerFactory<?, ?> cf) {
return args -> {
template.send("so65311053", "foo");
System.out.println(template.getProducerFactory()
.getConfigurationProperties()
.get(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG));
System.out.println(cf.getConfigurationProperties()
.get(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG));
};
}
#Bean
public NewTopic topic() {
return TopicBuilder.name("so65311053").partitions(1).replicas(1).build();
}
#KafkaListener(id = "so65311053", topics = "so65311053")
void listen(String in) {
System.out.println(in);
}
}
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.IntegerDeserializer
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.IntegerSerializer
spring.kafka.consumer.auto-offset-reset=earliest
2020-12-15 13:38:49.154 INFO 4612 --- [ main] o.a.k.clients.consumer.ConsumerConfig : ConsumerConfig values:
allow.auto.create.topics = true
auto.commit.interval.ms = 5000
auto.offset.reset = earliest
...
key.deserializer = class org.apache.kafka.common.serialization.IntegerDeserializer
...
2020-12-15 13:38:49.211 INFO 4612 --- [ main] o.a.k.clients.producer.ProducerConfig : ProducerConfig values:
...
key.serializer = class org.apache.kafka.common.serialization.IntegerSerializer
class org.apache.kafka.common.serialization.IntegerSerializer
class org.apache.kafka.common.serialization.IntegerDeserializer
2020-12-15 13:38:49.349 INFO 4612 --- [o65311053-0-C-1] o.s.k.l.KafkaMessageListenerContainer : so65311053: partitions assigned: [so65311053-0]
foo

The error occur because of KafkaBinderEnvironmentPostProcessor
I solved it by creating my own EnvironmentPostProcessor

Related

How to add a Property Source to a Spring Cloud Stream Binder's Environment

I have written a PropertySource that enables classpath: prefix for spring.kafka.properties.ssl.truststore.location (which out-of-the-box is not supported).
Essentially, this lets me place a truststore.jks inside my Spring Boot application's src/main/resources folder and reference it from inside the .jar file.
This works nicely for plain Spring Kafka configurations, like these:
spring:
kafka:
properties:
ssl.truststore.location: classpath:myTruststore.jks
It currently fails when the same configurations are given in the context of a Spring Cloud Stream Binder:
spring:
cloud:
stream:
binders:
my-binder:
type: kafka
environment:
spring:
kafka:
properties:
ssl.truststore.location: classpath:myTruststore.jks
My PropertySource is not even called back, when I would have expected it to be called with a poperty name of spring.cloud.stream.binders.my-binder.environment.spring.kafka.properties.ssl.truststore.location.
I think my PropertySource that would do the classpath: resolution is not part of the Environment of the given Spring Cloud Stream binder.
Question: how can one add PropertySources to a specific Binder's environment (or to all of them)?
Thanks!
EDIT
I add my PropertySource in a Spring Boot auto-configuration like this:
#Configuration
#AutoConfigureBefore(KafkaAutoConfiguration.class)
#ConditionalOnProperty(name = "com.acme.kafka.enabled", matchIfMissing = true)
#EnableConfigurationPropertiesAcmeKafkaConfigurations.class)
public class AcmeKafkaAutoConfiguration {
#Bean
ClasspathResourceSupportEnablingPropertySource acmeKafkaClasspathResourceEnablingPropertySource(ConfigurableEnvironment environment) throws IOException {
ClasspathResourcesSupport classpathResourcesSupport = new ClasspathResourcesSupport(Files.createTempDirectory(ACME_KAFKA_PREFIX));
ClasspathResourceSupportEnablingPropertySource propertySource
= new ClasspathResourceSupportEnablingPropertySource(ClasspathResourceSupportEnablingPropertySource.NAME, environment, classpathResourcesSupport);
environment.getPropertySources().addFirst(propertySource);
return propertySource;
}
}
EDIT NO.2: I tried out what Gary Russel suggested below (using a Bean Post Processor declared as a static bean method).
It works but in my case I get a lot of additional warning logs at startup of the form:
Bean '...' of type [...] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
According to this post this can cause some really nasty side effects.
Here is the code I was using (which caused the warnings above):
#Configuration
#AutoConfigureBefore(KafkaAutoConfiguration.class)
#ConditionalOnProperty(name = "com.acme.kafka.enabled", matchIfMissing = true)
#EnableConfigurationProperties(AcmeKafkaConfigurations.class)
public class AcmeKafkaAutoConfiguration {
private static final String ACME_KAFKA_PREFIX = "acme.kafka.";
#Bean
#ConditionalOnMissingBean
public static List<ConnectivityConfigurationsProvider> acmeKafkaTokenProviders(OAuth2TokenClient oAuthClient, AcmeKafkaConfigurations configuration) {
List<ConnectivityConfigurationsProvider> connectivityConfigurationsProviders = new ArrayList<>();
configuration.getInstances().forEach(serviceInstanceConfiguration -> {
TokenProvider tokenProvider = new DefaultOAuth2TokenProvider(oAuthClient, serviceInstanceConfiguration);
ConnectivityConfigurationsProvider connectivityConfigurationsProvider = new ConnectivityConfigurationsProvider(serviceInstanceConfiguration, tokenProvider);
connectivityConfigurationsProviders.add(connectivityConfigurationsProvider);
});
return connectivityConfigurationsProviders;
}
#Bean
#ConditionalOnMissingBean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static OAuth2TokenClient acmeKafkaOAuth2TokenClient() {
return new DefaultOAuth2TokenClient(new DefaultClientCredentialsTokenResponseClient());
}
#Bean
public static ConnectivityConfigurationsProviders acmeKafkaConnectivityConfigurationsProviders(AcmeKafkaConfigurations configuration, List<ConnectivityConfigurationsProvider> connectivityConfigurationsProviders) {
return new ConnectivityConfigurationsProviders(connectivityConfigurationsProviders);
}
#Bean
static NoOpBeanPostProcessor springKafkaConfigurationsPropertySource(ConfigurableEnvironment environment, ConnectivityConfigurationsProviders connectivityConfigurationsProviders) {
SpringKafkaConfigurationsPropertySource propertySource = new SpringKafkaConfigurationsPropertySource(SpringKafkaConfigurationsPropertySource.NAME, connectivityConfigurationsProviders);
environment.getPropertySources().addLast(propertySource);
return new NoOpBeanPostProcessor();
}
#Bean
#ConditionalOnClass(name = "org.springframework.cloud.stream.binder.BinderConfiguration")
static NoOpBeanPostProcessor springCloudStreamKafkaConfigurationsPropertySource(ConfigurableEnvironment environment, ConnectivityConfigurationsProviders connectivityConfigurationsProviders) {
SpringCloudStreamKafkaConfigurationsPropertySource propertySource = new SpringCloudStreamKafkaConfigurationsPropertySource(SpringCloudStreamKafkaConfigurationsPropertySource.NAME, connectivityConfigurationsProviders);
environment.getPropertySources().addLast(propertySource);
return new NoOpBeanPostProcessor();
}
#Bean
static NoOpBeanPostProcessor acmeKafkaConnectivityConfigurationsPropertySource(ConfigurableEnvironment environment, ConnectivityConfigurationsProviders connectivityConfigurationsProviders) {
AcmeKafkaConnectivityConfigurationsPropertySource propertySource = new AcmeKafkaConnectivityConfigurationsPropertySource(AcmeKafkaConnectivityConfigurationsPropertySource.NAME, connectivityConfigurationsProviders);
environment.getPropertySources().addLast(propertySource);
return new NoOpBeanPostProcessor();
}
#Bean
static NoOpBeanPostProcessor acmeKafkaClasspathResourceEnablingPropertySource(ConfigurableEnvironment environment) throws IOException {
ClasspathResourcesSupport classpathResourcesSupport = new ClasspathResourcesSupport(Files.createTempDirectory(ACME_KAFKA_PREFIX));
ClasspathResourceSupportEnablingPropertySource propertySource
= new ClasspathResourceSupportEnablingPropertySource(ClasspathResourceSupportEnablingPropertySource.NAME, environment, classpathResourcesSupport);
environment.getPropertySources().addFirst(propertySource);
return new NoOpBeanPostProcessor();
}
/**
* This BeanPostProcessor does not really post-process any beans.
* It is a way of getting the bean methods that add the property sources
* above to be called early enough in the lifecycle of Spring ApplicationContext
* creation.
*
* BeanPostProcessors are instantiated by Spring extremely early.
* #Bean methods providing them should be declared as static methods.
* See: https://stackoverflow.com/questions/30874244/bean-annotation-on-a-static-method
*/
static class NoOpBeanPostProcessor implements BeanPostProcessor, Ordered {
#Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
}
EDIT
This seems to work:
#SpringBootApplication
public class So61826877Application {
private static final String KEY = "spring.cloud.stream.binders.my-binder.environment."
+ "spring.kafka.properties.ssl.truststore.location";
public static void main(String[] args) {
SpringApplication.run(So61826877Application.class, args);
}
#Bean
public static BeanPostProcessor configureSource(ConfigurableEnvironment env) {
Properties source = new Properties();
System.out.println(env.getProperty(KEY));
source.setProperty(KEY, "/path/to/myTruststore.jks");
env.getPropertySources().addFirst(new PropertiesPropertySource("cp", source));
return new MyBpp();
}
#Bean
Consumer<String> input() {
return System.out::println;
}
}
class MyBpp implements BeanPostProcessor, Ordered {
#Override
public int getOrder() {
return Integer.MAX_VALUE;
}
}
classpath:myTruststore.jks
...
ConsumerConfig values:
allow.auto.create.topics = true
auto.commit.interval.ms = 100
...
ssl.truststore.location = /path/to/myTruststore.jks
...

Spring Boot 2.x multiple datasource and jdbctemplate

I would like to use Multiple Datasources and JdbcTemplates in my Spring Boot 2.1.3 Application.
I tried all solution advices on Stackoverflow and other web sites. But in my application;
when I try to create a Jdbctemplate instance with specified datasource, it sets null for datasources.
I couldn't understand where the problem is? Hikari Pool starts after application ready event. How to set a datasource of jdbcTemplate after initializations of datasources ?
Here is my code examples:
#Bean(name = "iybsDS")
#ConfigurationProperties(prefix = "datasource.iybs")
public DataSource iybsDSdataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "diyihDS")
#ConfigurationProperties(prefix = "datasource.diyih")
public DataSource diyihDSdataSource() {
return DataSourceBuilder.create().build();
}
JdbcTemplate Config:
#Configuration
public class JdbcTemplateConfig {
#Bean(name = "diyihJdbcTemplate")
public JdbcTemplate CreateDiyihJdbcTemplate(#Qualifier("diyihDS") DataSource diyihDS) {
return new JdbcTemplate(diyihDS);
}
}
Trying for print datasource information on console:
#SpringBootApplication
public class WebservicesApplication implements ApplicationRunner {
#Autowired
#Qualifier("diyihDS")
DataSource diyihDS;
public static void main(String[] args) {
SpringApplication.run(WebservicesApplication.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
System.out.println(diyihDS);
}
}
Output is :
2019-03-07 14:27:20.026 INFO 20100 --- [ main] t.g.a.WebservicesApplication : Started WebservicesApplication in 4.724 seconds (JVM running for 5.594)
HikariDataSource (null)
...
2019-03-07 14:27:20.739 INFO 20100 --- [on(4)-127.0.0.1] com.zaxxer.hikari.HikariDataSource : DiyihPool - Starting...
2019-03-07 14:27:20.817 INFO 20100 --- [on(4)-127.0.0.1] com.zaxxer.hikari.HikariDataSource : DiyihPool - Start completed.
When I tried to send a request to web service in my application, I got error because datasource of JdbcTemplate is wrong or null.
I GOT MY MISTAKE:
I was trying just one JdbcTemplate instance. I created all needed jdbcTemplates and set a Primary one of them. Then each service had own JdbcTemplate and Datasource.
Try adding DataSourceProperties Bean and use it:
#Bean
#Primary
#ConfigurationProperties("datasource.diyih")
public DataSourceProperties diyihDataSourceProperties() {
return new DataSourceProperties();
}
#Bean(name="diyihDS")
#Primary
#ConfigurationProperties("spring.datasource.diyih")
public DataSource diyihDataSource() {
return diyihDataSourceProperties().initializeDataSourceBuilder().build();
}

Configuring Camel for Spring with MINA 2

I want to configure Camel for Spring with MINA 2.
I did the following configuration code:
#Configuration
public class SpringConfiguration {
public static final String THREADPOOL_ID = "poolId";
#Bean
CamelContextConfiguration contextConfiguration() {
return new CamelContextConfiguration() {
#Override
public void beforeApplicationStart(CamelContext context) {
context.addComponent("mina2", new Mina2Component());
}
#Override
public void afterApplicationStart(CamelContext arg0) {
}
};
}
}
But when I wrote the router code like below. But it is not working:
#Component
public class RouteConfiguration extends RouteBuilder {
#Value("${app.collectorStringInput}")
private String collectorStringInput;
#Value("${app.mapOutputQueue}")
private String mapOutputQueue;
private final SiemParserProcessor parserProcessor;
public RouteConfiguration(SiemParserProcessor parser) {
this.parserProcessor = parser;
}
#Override
public void configure() throws Exception {
from("mina2:udp://10.31.0.32:514?disconnectOnNoReply=false&sync=false").to("log:edu.accs.siem.collector?level=DEBUG");
}
}
However, I can see this lines in the log:
2018-06-30 11:37:14.270 INFO 480 --- [ restartedMain] o.a.camel.spring.SpringCamelContext : Route: route1 started and consuming from: mina2://udp://10.31.0.32:514?disconnectOnNoReply=false&sync=false
2018-06-30 11:37:14.270 INFO 480 --- [ restartedMain] o.a.camel.spring.SpringCamelContext : Total 1 routes, of which 1 are started
2018-06-30 11:37:14.271 INFO 480 --- [ restartedMain] o.a.camel.spring.SpringCamelContext : Apache Camel 2.21.1 (CamelContext: camel-1) started in 0.185 seconds
It is working without using Spring. So I guess there is some configuration issue.
Can anybody tell me what I am missing?
PS: I checked out netty, but it seems not working even when not using Spring.
I got it working.
Actually, this was processing,
But somehow the logging wasn't displaying.

Custom RequestMappingHandlerMapping causes an error with Swagger

I have created a custom RequestMappingHandlerMapping with the latest version of Spring as follows so I can define a custom version annotation.
WebConfiguration,
#Configuration
#ConditionalOnClass({ ApiVersionedResource.class })
public class WebConfiguration {
#Bean
#Primary
public ContentNegotiationManager contentNegotiationManager() {
ContentNegotiationManagerFactoryBean contentNegotiationManagerFactoryBean = new ContentNegotiationManagerFactoryBean();
Map<String, MediaType> mediaTypes = new HashMap<>();
mediaTypes.put("json", MediaType.APPLICATION_JSON);
contentNegotiationManagerFactoryBean.addMediaTypes(mediaTypes);
contentNegotiationManagerFactoryBean.afterPropertiesSet();
return contentNegotiationManagerFactoryBean.getObject();
}
#Bean
public WebMvcRegistrationsAdapter webMvcRegistrationsHandlerMapping() {
return new WebMvcRegistrationsAdapter() {
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
CustomRequestMappingHandlerMapping handlerMapping = new CustomRequestMappingHandlerMapping();
handlerMapping.setOrder(0);
handlerMapping.setRemoveSemicolonContent(false);
handlerMapping.setContentNegotiationManager(contentNegotiationManager());
return handlerMapping;
}
};
}
}
CustomRequestMappingHandlerMapping,
public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
#Override
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
ApiVersionedResource typeAnnotation = AnnotationUtils.findAnnotation(handlerType, ApiVersionedResource.class);
return createCondition(typeAnnotation);
}
#Override
protected RequestCondition<?> getCustomMethodCondition(Method method) {
ApiVersionedResource methodAnnotation = AnnotationUtils.findAnnotation(method, ApiVersionedResource.class);
return createCondition(methodAnnotation);
}
#Override
protected boolean isHandler(Class<?> beanType) {
return super.isHandler(beanType) && (AnnotationUtils.findAnnotation(beanType, ApiVersionedResource.class) != null);
}
private RequestCondition<?> createCondition(ApiVersionedResource versionMapping) {
if (versionMapping != null) {
return new ApiVersionedResourceRequestCondition(versionMapping.media(), versionMapping.version());
}
return null;
}
}
However, this is causing the following error during compilation and I have no idea what is causing it.
2017-03-15 16:44:58.215 DEBUG 63518 --- [ restartedMain] r.h.v.CustomRequestMappingHandlerMapping : 6 request handler methods found on class com.test.web.rest.OrganizationResource: {public org.springframework.http.ResponseEntity com.test.web.rest.OrganizationResource.createOrganization(com.test.service.dto.OrgDetailsDTO) throws java.net.URISyntaxException={[/organizations],methods=[POST],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}, public org.springframework.http.ResponseEntity com.test.web.rest.OrganizationResource.updateOrganization(com.test.service.dto.OrgDetailsDTO) throws java.net.URISyntaxException={[/organizations],methods=[PUT],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}, public org.springframework.http.ResponseEntity com.test.web.rest.OrganizationResource.getAllOrganizations(org.springframework.data.domain.Pageable) throws java.net.URISyntaxException={[/organizations],methods=[GET],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}, public org.springframework.http.ResponseEntity com.test.web.rest.OrganizationResource.deleteOrganization(java.lang.Long)={[/organizations/{id}],methods=[DELETE],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}, public org.springframework.http.ResponseEntity com.test.web.rest.OrganizationResource.searchOrganizations(java.lang.String,org.springframework.data.domain.Pageable) throws java.net.URISyntaxException={[/search/organizations],methods=[GET],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}, public org.springframework.http.ResponseEntity com.test.web.rest.OrganizationResource.getOrganization(java.lang.Long)={[/organizations/{id}],methods=[GET],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}}
2017-03-15 16:44:58.238 INFO 63518 --- [ restartedMain] r.h.v.CustomRequestMappingHandlerMapping : Mapped "{[/organizations],methods=[POST],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}" onto public org.springframework.http.ResponseEntity<com.test.service.dto.OrgDetailsDTO> com.test.web.rest.OrganizationResource.createOrganization(com.test.service.dto.OrgDetailsDTO) throws java.net.URISyntaxException
2017-03-15 16:44:58.239 INFO 63518 --- [ restartedMain] r.h.v.CustomRequestMappingHandlerMapping : Mapped "{[/organizations],methods=[PUT],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}" onto public org.springframework.http.ResponseEntity<com.test.service.dto.OrgDetailsDTO> com.test.web.rest.OrganizationResource.updateOrganization(com.test.service.dto.OrgDetailsDTO) throws java.net.URISyntaxException
2017-03-15 16:44:58.239 INFO 63518 --- [ restartedMain] r.h.v.CustomRequestMappingHandlerMapping : Mapped "{[/organizations],methods=[GET],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}" onto public org.springframework.http.ResponseEntity<java.util.List<com.test.service.dto.OrgDetailsDTO>> com.test.web.rest.OrganizationResource.getAllOrganizations(org.springframework.data.domain.Pageable) throws java.net.URISyntaxException
2017-03-15 16:44:58.239 INFO 63518 --- [ restartedMain] r.h.v.CustomRequestMappingHandlerMapping : Mapped "{[/organizations/{id}],methods=[DELETE],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}" onto public org.springframework.http.ResponseEntity<java.lang.Void> com.test.web.rest.OrganizationResource.deleteOrganization(java.lang.Long)
2017-03-15 16:44:58.240 INFO 63518 --- [ restartedMain] r.h.v.CustomRequestMappingHandlerMapping : Mapped "{[/search/organizations],methods=[GET],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}" onto public org.springframework.http.ResponseEntity<java.util.List<com.test.service.dto.OrgDetailsDTO>> com.test.web.rest.OrganizationResource.searchOrganizations(java.lang.String,org.springframework.data.domain.Pageable) throws java.net.URISyntaxException
2017-03-15 16:44:58.240 INFO 63518 --- [ restartedMain] r.h.v.CustomRequestMappingHandlerMapping : Mapped "{[/organizations/{id}],methods=[GET],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}" onto public org.springframework.http.ResponseEntity<com.test.service.dto.OrgDetailsDTO> com.test.web.rest.OrganizationResource.getOrganization(java.lang.Long)
2017-03-15 16:44:58.314 WARN 63518 --- [ restartedMain] ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'documentationPluginsBootstrapper' defined in URL [jar:file:/Users/kasunt/.m2/repository/io/springfox/springfox-spring-web/2.6.1/springfox-spring-web-2.6.1.jar!/springfox/documentation/spring/web/plugins/DocumentationPluginsBootstrapper.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'webMvcRequestHandlerProvider' defined in URL [jar:file:/Users/kasunt/.m2/repository/io/springfox/springfox-spring-web/2.6.1/springfox-spring-web-2.6.1.jar!/springfox/documentation/spring/web/plugins/WebMvcRequestHandlerProvider.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'requestMappingHandlerMapping' is expected to be of type 'org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping' but was actually of type 'com.sun.proxy.$Proxy232'
Specially this right at the end,
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'requestMappingHandlerMapping' could not be injected as a 'org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping' because it is a JDK dynamic proxy that implements:
Action:
Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on #EnableAsync and/or #EnableCaching.
I seem to have fixed the problem by refactoring WebConfiguration as follows. Im not entirely sure why instantiating CustomRequestMappingHandlerMapping as a public class didn't work. Id be happy to listen to anyone who can think of a reason why.
#Configuration
#ConditionalOnClass({ ApiVersionedResource.class })
public class WebConfiguration {
#Bean
public WebMvcRegistrationsAdapter webMvcRegistrationsHandlerMapping() {
return new WebMvcRegistrationsAdapter() {
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new CustomRequestMappingHandlerMapping();
}
};
}
private final class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
#Override
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
ApiVersionedResource typeAnnotation = AnnotationUtils.findAnnotation(handlerType, ApiVersionedResource.class);
return createCondition(typeAnnotation);
}
#Override
protected RequestCondition<?> getCustomMethodCondition(Method method) {
ApiVersionedResource methodAnnotation = AnnotationUtils.findAnnotation(method, ApiVersionedResource.class);
return createCondition(methodAnnotation);
}
#Override
protected boolean isHandler(Class<?> beanType) {
return super.isHandler(beanType) && (AnnotationUtils.findAnnotation(beanType, ApiVersionedResource.class) != null);
}
private RequestCondition<?> createCondition(ApiVersionedResource versionMapping) {
if (versionMapping != null) {
return new ApiVersionedResourceRequestCondition(versionMapping.media(), versionMapping.version());
}
return null;
}
}
}
In Spring Boot 2.0.0, there is a simpler way to achieve this.
Create an instance of WebMvcRegistrations interface as a bean and override appropriate method to return the customized version of that object. Spring boot will read and use that instance.
In this case only the getRequestMappingHandlerMapping() needs to be overridden and a custom implementation returned

#Value is always null

I have a situation where my attempt to use a #Value annotation results in the value being a null.
This is part of a large project and I'm not sure which parts of it are needed. I am using Java anotations (no xml file) and Spring boot.
#Configuration
#EnableAutoConfiguration
#EnableConfigurationProperties
#ComponentScan
public class RESTApplication {
public static void main(String[] args) {
SpringApplication.run(RESTApplication.class, args);
}
}
application.properties contains:
maxuploadfilesize=925000000
I did try to create a PropertySourcesPlaceholderConfigurer as some websites mentioned to do so.
#Configuration
public class AppConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer properties() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Here is the class attempting to use it:
#Component
public class MyClass {
#Value("${maxuploadfilesize}")
String maxFileUploadSize;
public String getMaxFileUploadSize() {
return maxFileUploadSize;
}
public void setMaxFileUploadSize(String maxFileUploadSize) {
this.maxFileUploadSize = maxFileUploadSize;
}
}
However at runtime, maxFileUploadSize is always null. Note the below debug comment where PropertySourcesPropertyResolver seemed to find its correct value within the application.properties file.
2015-06-10 13:50:20.906 DEBUG 21108 --- [ main] o.s.c.e.PropertySourcesPropertyResolver : Searching for key 'maxuploadfilesize' in [applicationConfig: [classpath:/application.properties]]
2015-06-10 13:50:20.906 DEBUG 21108 --- [ main] o.s.c.e.PropertySourcesPropertyResolver : Found key 'maxuploadfilesize' in [applicationConfig: [classpath:/application.properties]] with type [String] and value '925000000'
It looks like MyClass was not processed as SpringBean, which would mean, that the #Value-annotation was not processed.
You could check that with providing a default value, like #Value("${maxuploadfilesize:'100'}"). If the value is still null, then you know, that MyClass is not instantiated as a SpringBean.
Since it is annotated with #Component, you should be able to simply inject it with
#Autowired private MyClass myclass;

Resources