Spring is autowiring custom bean into its own class - java

I have a bean:
#Bean
public ObjectMapper jsonMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
return objectMapper;
}
The problem is that while it's being injected into my classes, at the same time it's injected into one of Spring classes since autowiring by type is performed. I don't want WRAP_ROOT_VALUE to be set in the bean that Spring class is using. Is there any options to keep Spring from autowiring it into its classes besides not using beans in this case at all?

Use the name attribute in your #Bean annotation. Then it'll only be injected into classes that make reference to this name.

Related

How to avoid Spring Boot uses my MappingJackson2HttpMessageConverter Bean?

I'm using Spring Boot 2. I'd like to define a singleton bean of type MappingJackson2HttpMessageConverter, which will be used only by other beans.
By default, Spring Boot picks up a user defined MappingJackson2HttpMessageConverter and replaces the default instance with the one provided by the user, as stated by official documentation (https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-customize-the-jackson-objectmapper):
If you provide any #Beans of type MappingJackson2HttpMessageConverter, they replace the default value in the MVC configuration. Also, a convenience bean of type HttpMessageConverters is provided (and is always available if you use the default MVC configuration). It has some useful methods to access the default and user-enhanced message converters.
This is my configuration class:
#Bean
public MappingJackson2HttpMessageConverter myJacksonConverter() {
...
}
#Bean
#Scope("prototype")
public MyClient myClient(){
MyClient c = new MyClient();
c.setConverter(myJacksonConverter());
return c;
}
So, I want MappingJackson2HttpMessageConverter as a singleton bean, but I don't want that Spring Boot uses it at global application level.
I'd consider not defining your custom converter as a #Bean. Instead, you could create your custom converter and store it in a field of a #Configuration class and then reference it from there. The #Configuration class will only be created once so you're guaranteed to only get a single instance of your custom converter.
If it is used only inside the same config class, you can avoid to use #Bean annotation.
#Bean
public MyClient myClient(){
MyClient c = new MyClient();
c.setConverter(myJacksonConverter());
return c;
}
private static MappingJackson2HttpMessageConverter myJacksonConverter() {
}
See if this works .. Define a class that extends from MappingJackson2HttpMessageConverter and autowire it.
class MyCustomMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {}
#Bean
public MyCustomMappingJackson2HttpMessageConverter myJacksonConverter() {
...
}
#Bean
public MyClient myClient(){
MyClient c = new MyClient();
c.setConverter(myCustomJacksonConverter());
return c;
}```

Two instances of ObjectMapper

Is there a way to create two instances of ObjectMapper for different purpose.
Modified ObjectMapper
#Component
class MyObjectMapper extends ObjectMapper{
public MyObjectMapper(){
super();
}
public MyObjectMapper(MyObjectMapper om) {
super(om);
}
#Override
public MyObjectMapper copy() {
return new MyObjectMapper(this);
}
}
Now use it as follows
#Autowired ObjectMapper objectMapper; //performs standard serialization
#Autowire MyObjectMapper myMapper; //i can add custom filters in thiis mapper.
I tried a similar setup but the custom mapper actually affects the original mapper all the rest controllers throw JSON parse error: Unrecognized field
Edit: Not sure if this point is very important but still adding it
Am using spring-boot-starter-json
And that's exactly where you should use #Qualifier annotation.
This annotation may be used on a field or parameter as a qualifier for candidate beans when autowiring. It may also be used to annotate other custom annotations that can then in turn be used as qualifiers.
OK. Combining with Answer from Aniket figured out what is wrong and still looking for some more explanation.
Instead of instantiating the ObjectMapper as new ObjectMapper(). Building it with Mapper fixed it.
So, two have multiple instance of ObjectMapper
#Primary
#Bean
public ObjectMapper objectMapper(){
return new Jackson2ObjectMapperBuilder()
.build();
}
#Bean("customMapper")
public ObjectMapper customMapper(){
ObjectMapper customMapper = new Jackson2ObjectMapperBuilder().build();
mapper.<your customization , filters, providers etc;>
return mapper;
}
The #Primary will be used by in all default cases i.e When you simply #Autowire or the default serialization applied to your Response/Request Body by your controller.
To use your Custom Mapper, explicitly use with the Bean ID.
#Autowired #Qualifier("customMapper") ObjectMapper mapper;

How do I convert an object using ObjectMapper and inject dependencies into it

I am trying to convert Object to a concrete class using jackson ObjectMapper and to inject dependencies after I convert it.Here is the example:
public class SimpleClass{
private String parameter;
#JsonIgnore
private SomeService service;
/** getters and setters **/
public void doSomethingFromService(){
//call some methods from the service
}
}
After attempt to deserialize the object(value is map containing parameters) :
ObjectMapper om = new ObjectMapper();
om.convertValue(value,SimpleClass.class).doSomethingFromService();
-> result to a NullPointerException...
How to inject the service?
Can I get the service from the context and inject it by default constructor?
If you were doing the initialization of the SimpleClass object yourself then you can make it work by autowiring the paritcular instance with AutowireCapableBeanFactory in that case the #Autowrire of the service will work since Spring manages also the partical instance of the SomeClass object.
That said, it is not the way to go. Data objects should be isolated from the business logic.
Back to your case, you can't autowire even in the way I mentined because Jackson is the provider of the instance, and Jackson requires the presence of empty constructor.
Since you want some kind of auto-wiring in your SimpleClass bean,
you need to annotate its property SomeService service with #Autowired.
public class SimpleClass {
private String parameter;
#JsonIgnore
#Autowired
private SomeService service;
// getter and setter for parameter (omitted here for brevity)
public void doSomethingFromService(){
//call some methods from the service
}
}
As others already said, ObjectMapper does not do any dependency injection.
But you can combine the #Autowired-ignoring ObjectMapper
with some manually triggered auto-wiring.
For that you need an AutowireCapableBeanFactory which you get by Spring's
normal dependency injection with #Autowired.
Then you use its autowireBean(Object) method
to inject the bean's #Autowired properties.
#Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
public void doSomething(Map<String, Object> value) throws Exception {
ObjectMapper om = new ObjectMapper();
SimpleClass bean = om.convertValue(value, SimpleClass.class);
autowireCapableBeanFactory.autowireBean(bean);
bean.doSomethingFromService();
}
It's not possible. Only objects that have been instanciated with spring can use #Autowired.
It's simple : when you annotate an object with #Service, spring will try to resolve all it's dependency with introspection.
With jackson (or if you try to instanciate an object by yourself) you are totally out of the scope of spring.
And I should had, what you are trying to do (even if it was possible) is not a good practice. You should'n mix your data objects with business processing.
Injection of services is only possible with a framework like Spring, as said previously.
If you are using Spring, annotate your service class with #Service and then as opposed to using #Autowired declare your service private final and inject it using a constructor (IMO I find this works better).
If you aren't using Spring, you'll need to new up an instance of the service and then call that instance - unless of course it's a static...

How can I register and use the jackson AfterburnerModule in Spring Boot?

I am using SpringBoot 1.5.9., Jackson 2.8 and Spring Framework 4.3.13.
I am trying to register and use the AfterburnerModel.
According to the Spring Boot documentation, to configure the ObjectMapper you can either define the bean yourself and annotate it with #Bean and #Primary. In the bean you can register a module. Or you can add a bean of type Jackson2ObjectMapperBuilder where you can customize the ObjectMapper, by adding a module.
I have tried both ways, and during serialization none of my breakpoints in jackson-module-afterburner fire. My customizations are being read, but seem to be being ignored.
By default Spring MVC MappingJackson2HttpMessageConverter will create its own ObjectMapper with default options using Jackson2ObjectMapperBuilder. As per Spring Boot docs 76.3 Customize the Jackson ObjectMapper chapter:
Any beans of type com.fasterxml.jackson.databind.Module are automatically registered with the auto-configured Jackson2ObjectMapperBuilder and are applied to any ObjectMapper instances that it creates. This provides a global mechanism for contributing custom modules when you add new features to your application.
so it should be enough to register your module as a bean:
#Bean
public AfterburnerModule afterburnerModule() {
return new AfterburnerModule();
}
A more detailed setup can be achieved with #Configuration class to customize the MappingJackson2HttpMessageConverter:
#Configuration
public class MyMvcConf extends WebMvcConfigurationSupport {
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(myConverter());
addDefaultHttpMessageConverters(converters);
}
#Bean
public MappingJackson2HttpMessageConverter myConverter() {
return new MappingJackson2HttpMessageConverter(myObjectMapper())
}
#Bean
public ObjectMapper myObjectMapper() {
return new ObjectMapper().registerModule(new AfterburnerModule());
}
}

Jackson2ObjectMapperBuilder enable field visibility ANY

I'm using spring-boot and want to customize the ObjectMapper created.
What I want to do is be able to serialize objects that do not have a getter or setters. Before this could be done by putting JsonAutoDetect.Visibility.ANY on the ObjectMapper.
But how can I enable this feature using the Jackson2ObjectMapperBuilder bean I'm currently exposing ?
You can use a Jackson2ObjectMapperBuilder subclass that overrides the configure(ObjectMapper) method:
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
return new Jackson2ObjectMapperBuilder() {
#Override
public void configure(ObjectMapper objectMapper) {
super.configure(objectMapper);
objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
}
};
}
If you want to keep the ObjectMapper configurable through the spring.jackson.* properties that Spring Boot provides, then you better don't define your own Jackson2ObjectMapperBuilder bean (check JacksonObjectMapperBuilderConfiguration inside JacksonAutoConfiguration class for details).
What you can do instead is this:
#Bean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder mapperBuilder) {
return mapperBuilder.build().setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
}
I spend a half of day to play with different settings.
So I manage to work it (1.3.2.RELEASE) when:
I configure jackson in simple #Configuration annotated config class (not extended from WebMvcConfigurerAdapter)
I have NOT#EnableWebMvc
Then Jackson2ObjectMapperBuilder objectMapperBuilder solution is
work, but spring.jackson.serialization.indent_output: true in properties ignored.
At last I finished with
#Autowired(required = true)
public void configeJackson(ObjectMapper objectMapper) {
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE)
.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
}
But all this is puzzle for me. I wrote a question about any explanation of all this magic in order to have some undestanding and solve problem not by
trial-and-error method: Are there any Spring Boot documentation for understanding how web mvc configuration is work?

Categories