#Bean vs #Autowired cyclic dependency - spring

I came across a strange behaviour with respect to #Bean and #Autowired in my #Configuration class. My web project structure is like
Controller → Service → Repository
In my Service I have a dependency on ObjectMapper
#Autowired
public ServiceClass(final ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
Since I want to use a Java 8 optional class while deserializing, I wanted to register Jdk8Module. So I created a configuration class like this:
#Configuration
public class JacksonConfig {
#Bean
public ObjectMapper objectMapper(final ObjectMapper objectMapper) {
objectMapper.registerModule(new Jdk8Module());
return objectMapper;
}
}
I initially thought that Spring will inject the objectMapper instance which it has, which I can manipulate and return it, so that when I autowire it in my service class, I get the updated instance of ObjectMapper.
But I get a cyclic dependency error. This is understandable because, my bean configuration depends on objectmapper and returns an objectmapper.
But it is surprising, if I change the method to have #Autowired instead of #Bean, Spring doesn't complain and works as expected.
#Autowired
public ObjectMapper objectMapper(final ObjectMapper objectMapper) {
objectMapper.registerModule(new Jdk8Module());
return objectMapper;
}
Why is that?

The #Bean annotation goal is to provide beans from the Spring BeanFactory. When a method is annotated #Bean, it is supposed to return a new instance of an object. It can use paramaters if Spring is able to instantiate them too.
In your example, when you declare
#Bean
public ObjectMapper objectMapper(final ObjectMapper objectMapper) {
objectMapper.registerModule(new Jdk8Module());
return objectMapper;
}
It means your ObjectMapper is a bean (of course) that takes a parameter which is an instance of ObjectMapper and Spring knows ObjectMapper so it can instantiate it using... the very same method. Here is your cyclic dependency.
In the case of #Autowired, Spring uses an ObjectMapper as a parameter of your method that is already in the BeanFactory. That's why it removes the cyclic dependency.
I hope I'm clear enough.
Little reference here : https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html

In
#Autowired
public ObjectMapper objectMapper(final ObjectMapper objectMapper) {
objectMapper.registerModule(new Jdk8Module());
return objectMapper;
}
Someone else is injecting an ObjectMapper (maybe Spring) to this method. I think the Autowired annotation is not necessary in this case, Spring knows you want to inject a bean here.
You aren't creating new beans in this case so you will not have a cyclic dependency error here.
If you want to create a new one, maybe you should play with #Qualifier.
More info: https://dzone.com/articles/spring-configuration-and

Related

How to configure spring controller to use different objectMapper for the response (serialization)

I configured my own ObjectMapper for my SpringBoot application, Let say the object mapper is something like below (simplified):
#Bean
#Primary
public ObjectMapper objectMapper() {
return new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
Since the ObjectMapper is a singleton, all my REST Controller use that same ObjectMapper.
But, there is some controller that I want to ignore the null value, but I want to keep the rest of my controller to send the null value response.
Is there a way to configure it? So the best result is I can configure something like this:
Controller A,B,C -> ObjectMapper X (ignore null value)
Controller D -> ObjectMapper Y (ignore empty value)
Default (all other controller) -> ObjectMapper Z (return null value)
Requirement Note:
I can't change the POJO since it's autogenerated, and I don't want to update the codegen lib or the mustache template for this.
Specific content negotiation is not an option
In addition to your ObjectMapper annotated with #Primary
you can configure more ObjectMappers qualified with bean names of your choice.
#Bean
#Primary
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return objectMapper;
}
#Bean("mySpecialObjectMapper")
public ObjectMapper anotherObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper()
/* do some other configuration */;
return objectMapper;
}
Then you can refer to them in your controllers like this:
Just giving #Autowired will inject the primary ObjectMapper.
Giving #Autowired together with #Qualifier("anyName") will inject
the ObjectMapper configured with #Bean("anyName")
#Autowired
private ObjectMapper objectMapper;
#Autowired
#Qualifier("mySpecialObjectMapper")
private ObjectMapper otherObjectMapper;
Maybe you just need to use #JsonInclude(JsonInclude.Include.NON_NULL). Try to fix it in the response model level.

How to configure HttpMessageConverter for specified controller in SpringMvc

As we know, we can configure the global HttpMessageConverter by configureMessageConverters method in WebMvcConfigurer.
see https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-config-message-converters
But I want to configure a HttpMessageConverter for specified Controller to override the global configuration to implement the function different from the global.
How should I configure it? Can any friends give me pointers?
You can put below code in any of your configuration classes. and you have to autowire this specific objectmapper in that class where you need.Even you can create multiple objectmappers to serve different purposes.
#Bean
#Qualifier("customForController")
public ObjectMapper getObjectMapper() {
ObjectMapper mapper=new ObjectMapper();
return mapper;
}
#Bean
#Qualifier("customMessageConverter")
public MappingJackson2HttpMessageConverter converter() {
MappingJackson2HttpMessageConverter httConverter = new MappingJackson2HttpMessageConverter();
httConverter.setObjectMapper(getObjectMapper());
//others configuration goes here
return httConverter;
}

Configure a Jackson's DeserializationProblemHandler in Spring environment [duplicate]

This question already has answers here:
Can't set ProblemHandler to ObjectMapper in Spring Boot
(2 answers)
Closed 4 years ago.
As I understood, Spring is already providing a bean for Jackson ObjectMapper. Therefore, instead of creating a new bean, I'm trying to customize this bean.
From this blog post, and then this Github project I used Jackson2ObjectMapperBuilder bean to achieve this customization.
#Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder(ApplicationContext context) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.findModulesViaServiceLoader(true);
return builder;
}
Then, I was trying to customize the deserializer in order to make it lenient: if an exception is raised when deserializing a property, I want the result object's property to be null and let the deserialization continue (default is to fail on first property that cannot be deserialized).
I've been able to achieve that with a class NullableFieldsDeserializationProblemHandler that extends DeserializationProblemHandler (I do not think the code is relevant but if needed, I can share it).
The simplest way to register this handler is to use the .addHandler() method of ObjectMapper. But of course, doing like this, I would need to set that every time I inject and use the ObjectMapper. I'd like to be able to configure handler so that every time the ObjectMapper is auto-wired, the handler is already present.
The best solution I came up with so far is to use a #PostConstruct annotation only to register the problem handler.
#Configuration
public class JacksonConfiguration implements InitializingBean {
#Autowired private ObjectMapper objectMapper;
#Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder(ApplicationContext context) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.findModulesViaServiceLoader(true);
return builder;
}
#Override
public void afterPropertiesSet() {
objectMapper.addHandler(new NullableFieldsDeserializationProblemHandler());
}
}
But the problem of this solution is that it seems I can still access an autowired ObjectMapper that doesn't have yet registered the problem handler (I can see it happening after when I need it in debug mode).
Any idea how I should register this handler? I've noticed Jackson2ObjectMapperBuilder has a .handlerInstantiator() but I couldn't figure out how to use it.
Note I've also tried with Jackson2ObjectMapperBuilderCustomizer since I'm using Spring Boot but had no better results.
It's not possible to directly add a DeserializationProblemHandler to the ObjectMapper via a Jackson2ObjectMapperBuilder or Jackson2ObjectMapperBuilderCustomizer. The handlerInstanciator() method is for something else.
However, it's possible to do so by registering a Jackson module:
the builder has a modules() method
the module has access via setupModule() to a SetupContext instance, which has a addDeserializationProblemHandler() method
This works:
#Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
return new Jackson2ObjectMapperBuilderCustomizer() {
#Override
public void customize(Jackson2ObjectMapperBuilder builder) {
builder.modules(new MyModule());
}
};
}
private static class MyModule extends SimpleModule {
#Override
public void setupModule(SetupContext context) {
// Required, as documented in the Javadoc of SimpleModule
super.setupModule(context);
context.addDeserializationProblemHandler(new NullableFieldsDeserializationProblemHandler());
}
}
What about writing a bean like this:
#Configuration
public class ObjectMapperConfiguration {
#Bean
ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
// jackson 1.9 and before
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// or jackson 2.0
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return objectMapper;
}
}
This is for global configuration. If, instead, what you want to do is to configure the feature for specific a class, use this annotation above the class definition:
#JsonIgnoreProperties(ignoreUnknown = true)

Spring JavaConfig + WebMvcConfigurerAdapter + #Autowired => NPE

I have an application with 2 Contexts. Parent for web agnostic business logic and ChildContext (implicitly created by dispatcher servlet) for web logic.
My setup loks like
#Configuration
public class BusinessConfig {
#Bean
public ObjectMapper jacksonMapper() { return new ObjectMapper() }
}
and
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired
private ObjectMapper objectMapper; // <- is null for some reason
#Override
public configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper); // <- bang!
messageConverters.add(converter);
}
}
I need the the object mapper in the parent context, as I use it also in security configuration. But can someone explain me, why the #Autowired objectMapper is null? Its created in the parent context (the fact that the parent exists is even logged by spring at startup). Also #Autowired has required=true by default, so it should not blow up in the configure method (it should have blown up in construction of the context, if the bean wasn't there for some reason).
It seems to me that there might be some lifecycle problem in spring - in a sense that it calls the overridden methods first, and then #Autowires the dependencies... I have also tried to #Autowire the BusinessConfig (should be perfectly legal according to documentation - the result was the same (null)).
What should I do to make this working?
Thanks in advance!
EDIT - ISSUE FOUND
I found the issue. Unfortunately it had nothing to do with WebMvcConfigurerAdapter nor #Configuration. It was caused by premature initialization of context triggered by missing static modifier for propertyPlaceholderConfigurer... I have created issue in Spring core jira (https://jira.spring.io/browse/SPR-14382)
What about simply renaming the bean declaration method to match with the autowired bean?
#Configuration
public class BusinessConfig {
#Bean
public ObjectMapper objectMapper() { return new ObjectMapper() }
}
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired
private ObjectMapper objectMapper;
[...]
}

How can you extend behavior of the spring boot autoconfiguration?

I am looking to extend JacksonAutoConfiguration specifically when the builder is created, i would like to set that ObjectMapper to a Util class which has static setter for the ObjectMapper. Look at the line before returning builder where I would like to set ObjectMpper to a static class.
#Bean
#ConditionalOnMissingBean(Jackson2ObjectMapperBuilder.class)
public Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.applicationContext(this.applicationContext);
if (this.jacksonProperties.getSerializationInclusion() != null) {
builder.serializationInclusion(this.jacksonProperties
.getSerializationInclusion());
}
configureFeatures(builder, this.jacksonProperties.getDeserialization());
configureFeatures(builder, this.jacksonProperties.getSerialization());
configureFeatures(builder, this.jacksonProperties.getMapper());
configureFeatures(builder, this.jacksonProperties.getParser());
configureFeatures(builder, this.jacksonProperties.getGenerator());
configureDateFormat(builder);
configurePropertyNamingStrategy(builder);
configureModules(builder);
**ObjectMapperUtils.setObjectMapper( builder.build() );**
return builder;
}
The ObjectMapper created from the auto-configured Jackson2ObjectMapperBuilder is exposed as a bean in the JacksonAutoConfiguration. You could simply create another #Configuration class, get a reference to the ObjectMapper (via auto-wiring) and use an #PostConstruct method to set the ObjectMapper in your ObjectMapperUtils class.
Another suggestion would be to refactor ObjectMapperUtils so that it is created as a Spring bean itself, then you can auto-wire ObjectMapper directly into it.

Resources