Spring Bean Overriding My ObjectMapper Configuration - spring

Had an issue today where a dependency wired with a configuration kept winning when it came to some ObjectMapper configuration I was trying to do. I added the following to my Spring Boot application.
#Configuration
public class CustomObjectMapperConfig {
#Autowired
public void configureObjectMapper(ObjectMapper objectMapper) {
objectMapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
}
I'd actually rather not use timestamps, but for backwards compatibility I had to do this. I put a debug point at the line in question here and it got hit, but I kept getting dates returned to me in ISO format, which is the default for most or our projects.

I finally figured out that a company dependency that I was bringing in had the following:
#Configuration
public class ObjectMapperPropertiesConfig {
/**
* #deprecated Spring boot jackson properties should be used instead.
*/
#Deprecated
#Autowired
public void setObjectMapper(
final ObjectMapper objectMapper) {
objectMapper
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, !ignoreUnknown);
}
...
This class was getting loaded after my class, so it alwasy won with regards to the WRITE_DATES_AS_TIMESTAMPS feature.
To fix this I ended up adding a #DependsOn annotation. This forced the other bean to load first giving my bean the chance to win the configuration war. Finding the correct name of the bean was difficult. It ended up looking something like this:
#DependsOn("path.to.object.ObjectMapperPropertiesConfig")
Note: The #Deprecated annotation here tell me that this code will be going away in future versions in favor of spring boot properties anyway. For now, my change will work.

Related

Split jackson configuration into separate properties

I'm using Spring Boot 2.2.5.RELEASE and would like to split my application.properties into separate files. There are already similar questions on StackOverflow but none of them seem to work for configuring Jackson.
My current non working solution is the following:
root/
- application.properties (without Jackson configuration)
- jackson-configuration.properties (includes Jackson configuration)
Jackson configuration class:
#Configuration
#PropertySource("/jackson-configuration.properties")
public class JacksonConfiguration {
}
Please note, I've tried different ways to specify the path including:
"/jackson-configuration.properties"
"jackson-configuration.properties"
"classpath:/jackson-configuration.properties"
"classpath:jackson-configuration.properties"
Spring Boot does not seem to use the configuration. If I copy it over into the application.properties - it works.
Content of jackson-configuration.properties:
spring.jackson.property-naming-strategy=SNAKE_CASE
spring.jackson.mapper.sort-properties-alphabetically=true
spring.jackson.deserialization.fail-on-unknown-properties=true
spring.jackson.parser.strict-duplicate-detection=true
spring.jackson.time-zone=Europe/Zurich
My application is annotated with #SpringBootApplication , so it should scan for additional properties.
/edit
I just realized the problem is the testing, not the productive code itself. If I start the application it works. What doess not work is testing with #JsonTest. I can fix this problem by adding the following line to my tests #ContextConfiguration(classes = {JacksonConfiguration.class}). But in turn, this causes the annotation #JsonComponent to stop working but only for the #JsonTest annotated classes.
See the documentation here. Here is an excerpt from the documentation
In order to resolve ${...} placeholders in definitions or
#Value annotations using properties from a PropertySource, you must
ensure that an appropriate embedded value resolver is registered in
the BeanFactory used by the ApplicationContext. This happens
automatically when using in XML. When
using #Configuration classes this can be achieved by explicitly
registering a PropertySourcesPlaceholderConfigurer via a static #Bean
method.
You need to create a bean like this
#Bean
public static PropertySourcesPlaceholderConfigurer devPropertyPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setLocations(new PathMatchingResourcePatternResolver().getResources("file:pathtToFile"));
configurer.setIgnoreUnresolvablePlaceholders(true);
return configurer;
}

Spring MVC #Value/#ConfigurationProperties working on MainConfig but not on SecurityConfig

I have a simple Spring MVC 5 project, with security layer enabled. Everything works good except the properties loading, only on Security Config.
I let you the scenario so you can see it.
application.properties (located at src/main/resources)
com.company.myapp.prop=myprop
MainConfig.java
#Configuration
public class MainConfig implements WebMvcConfigurer {
#Value("${com.company.myapp.prop}")
private String prop;
#Bean
public MySpecialBean mySpecialBean() {
System.out.println(prop); // output > myprop
return new MySpecialBean();
}
}
SecurityConfig.java
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${com.company.myapp.prop}")
private String prop;
#Bean
public MySpecialSecurityBean mySpecialSecurityBean() {
System.out.println(prop); // output > null
return new MySpecialSecurityBean();
}
}
I don't understand why it's happening. I already switched the #EnableWebSecurity annotation to the app class, try to set the PropertySourcesPlaceholderConfigurer myself, but nothing works.
Do you have any idea what's going on?
From official docs about #PropertySource:
Resolving ${...} placeholders in <bean> and #Value annotations
In order to resolve ${...} placeholders in definitions or #Value annotations using properties from a PropertySource, you must ensure that an appropriate embedded value resolver is registered in the BeanFactory used by the ApplicationContext. This happens automatically when using in XML. When using #Configuration classes this can be achieved by explicitly registering a PropertySourcesPlaceholderConfigurer via a static #Bean method. Note, however, that explicit registration of a PropertySourcesPlaceholderConfigurer via a static #Bean method is typically only required if you need to customize configuration such as the placeholder syntax, etc. See the "Working with externalized values" section of #Configuration's javadocs and "a note on BeanFactoryPostProcessor-returning #Bean methods" of #Bean's javadocs for details and examples.
You should try to add annotation #PropertySource into the your config class.
#Configuration
#PropertySource("classpath:my.properties")
public class MainConfig implements WebMvcConfigurer {}
and then try to access your property in SecurityConfig class
To get full information see official docs
I hope it will help you
This works for me.
I guess you have another class that triggers the application and that is annotated with #SpringBootApplication
Also, your methods mySpecialBean do not return a MySpecialBean instance, so this probably does not even compile.
Is there any other class that you are using? Please advice
Finally got it!
The problem was related with some dependency priorities and unnecessary beans declarations. Getting into details, I'm working with OAuht2 and I started with this tutorial. In the end I've made a mix with this one too (more recent). The problem was related with these #Bean's that don't really need to be declared as beans:
ClientRegistrationRepository
ClientRegistration
OAuth2AuthorizedClientService
Spring was calling these beans before any other configuration, so any properties was not loaded yet. Maybe changing the priority, dependence or even the order would resolve the issue, but as I was analysing the code I found that these methods are only used on security configuration and not really needed along any other part of the app. So I removed the #Bean declaration and all works nice now! At the time these methods are called inside security config the properties are already loaded.
Hope to help someone out there.

Resolving bean conflicts in third-party packages

I have an application, let's call it MyApp. An example of the build.gradle looks like this:
compile group: 'com.example', name: 'library'
compile .. spring-heatoas ..
Now, the way this library works is that it expects the host to give it a bean of type ObjectMapper. It by itself does not define an ObjectMapper as the configuration of the same is completely open to the host library. So basically, in the host MyApp I have this config:
#Configuration
public class SpringConfig {
#Bean
public ObjectMapper objectMapper() { ... }
}
Everything is fine and working, till I had the spring-hateos dependency. spring-hateos defines it's own ObjectMapper which goes by the name _hal_objectMapper. So, after adding it, I get a conflict between the two beans. I tried:
#Bean
#Order(Ordered.HIGHEST_PRECEDENCE)
public ObjectMapper objectMapper() { ... }
Not only does this not work, but even if it did, I guess it has the potential to break functionality as the host application can no longer configure the ObjectMapper. A straightforward solution obviously is to make the library accept a named-bean and then declare it in my host application with a #Qualifier annotation. But getting the library to change (and there are a couple of libraries like that) company-wide is going to be a major pain. While we are considering that solution, is there a way to solve this?
The annotation #Primary advises spring to use that annotated bean if many beans of the same type are available. This could resolve your issue.
Your code should look like this :
#Configuration
public class SpringConfig {
#Bean
#Primary
public ObjectMapper objectMapper() { ... }
}
The problem may be now, that spring-hateos also uses this objectMapper, which is not configured as expected.
A complete solution could be, to create a child spring context.
Only for your ObjectMapper and the 3rd party bean, so that the objectMapper is not visible for the rest of you application.
How this is done depends on how you instantiate the 3rd party bean, and also where it will be used. With the provided information I cannot describe that more detailed.
You can find a good entry here :
modularizing-configurations
(see chapter 'Nesting #Configuration classes' or search for 'child')

Is constructor injection possible in Spring configuration classes?

If I have a Spring configuration class (i.e. a class annotated with #Configuration) can I use constructor injection ?
As it stands if I add one I get a no default constructor message, and if I add a default constructor it uses that rather than the overloaded one, which doesn't really help.
There is a bug report about this limitation. It will be fixed with Spring 4.3.
Please note that another bug report (not fixed yet today fixed in 4.3-RC1) report a problem when using this very new feature and injecting generics in constructor of a #Configuration class.
In Spring 4.3, you can use org.springframework.beans.factory.ObjectProvider in #Configuration annotated class constructors to inject beans. for example:
#Configuration
public class SimpleBean {
private final InnerBean prop1;
public Simple Bean(ObjectProvider<InnerBean> innerBeanProvider) {
prop1 = innerBeanProvider.getObject();
}
}

Spring: how AnnotationConfigWebApplicationContext could not overwrite later beans?

I have a web application that use Sring IoC framework.
I use the Java configuration for Spring, and I only use #Configuration annoted module definition (no DI related tags elsewhere in the code).
The Spring registry is built on web application start-up thanks to (a bit modified version of) Spring context loader listener, and the contextConfigLocation param in web.xml configured to point to the #Configuration annotated class.
All that is good and I get a AnnotationConfigWebApplicationContext.
Now, I want to have plugins in my application, that will have their own #Configuration annotated configuration classes, and will use some of the main application services. BUT I don't want to have main application to be modified to load these new modules.
So, I thought that I could simply use the package searching of annotated class for that, but now, it seems that I can use two beans with the same type, even if they have different ids, and clearly AnnotationConfigWebApplicationContext doc states that:
Note: In case of multiple #Configuration classes, later #Bean definitions will override ones defined in earlier loaded files. This can be leveraged to deliberately override certain bean definitions via an extra Configuration class.
I don't want that, because modules should be able to contribute alternative version of services, not (alwways) override existing one - especcially if I want to have a "moduleDef" bean.
I tried to use differents approach on that, but the hierachy of Context and related services is just to big for me.
So, does anybody know how I could reach my goal ?
Thanks
You can have multiple beans of the same type, but You cannot have 2 or more beans with the same ID in a single Spring ApplicationContext - no matter if You use XML or JavaConfig.
The overriding mechanism matches the bean ID's, so all You need to do is to ensure unique ID, i.e.: coreModuleDef, someOtherModuleDef, anotherModuleDef. I don't think You need the ID of each module definition to be identical? What should be sufficient is the type to be the same, but not ID.
You can also turn off the overriding mechanism by setting allowBeanDefinitionOverriding to false on Your AnnotationConfigWebApplicationContext to get an exception if You accidentally override a bean:
public class MyDispatcherServlet extends DispatcherServlet {
#Override
protected void postProcessWebApplicationContext(
ConfigurableWebApplicationContext wac) {
((AnnotationConfigWebApplicationContext) wac)
.setAllowBeanDefinitionOverriding(false);
}
}
or:
public class MyContextLoaderListener extends ContextLoaderListener {
#Override
protected void customizeContext(
ServletContext servletContext,
ConfigurableWebApplicationContext applicationContext) {
((AnnotationConfigWebApplicationContext) wac)
.setAllowBeanDefinitionOverriding(false);
}
}

Resources