Avoiding a common spring annotation configuration mistake

With annotated based configurations, I have seen this common mistake which will cause multiple instances of a singleton being created. When you have two beans and one bean needs the other, it is easy to construct the two beans as follows:

  1. public class Config {
  2.   @Bean
  3.   public MappingJackson2HttpMessageConverter converter() {
  4.     MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
  5.     converter.setObjectMapper(objectMapper());
  6.     return converter;
  7.   }
  8.  
  9.   @Bean
  10.   public ObjectMapper objectMapper() {
  11.     return new JacksonObjectMapper();
  12.   }
  13. }

If you are referencing this configuration through an Import, like the following, two instances of the JacksonObjectMapper to be created.

  1. @ComponentScan("com.objectpartners.buesing")
  2. @Import(Config.class)
  3. public class ApplicationConfiguration {
  4. }

Once when spring calls objectMapper() and once when spring calls converter(). Instead, method arguments should be used when one bean needs to reference another.

  1. public class Config {
  2.   @Bean
  3.   public MappingJackson2HttpMessageConverter converter(final ObjectMapper objectMapper) {
  4.     MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
  5.     converter.setObjectMapper(objectMapper);
  6.     return converter;
  7.   }
  8.  
  9.   @Bean
  10.   public ObjectMapper objectMapper() {
  11.     return new JacksonObjectMapper();
  12.   }
  13. }

Spring will construct objectMapper and provide that single instance when it calls converter to create the other bean. Furthermore, if there are two ObjectMapper instances, you can qualify which ObjectMapper is needed.

  1. public class Config {
  2.   @Bean
  3.   public MappingJackson2HttpMessageConverter converter(@Qualifier("jsonMapper") final ObjectMapper objectMapper) {
  4.     MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
  5.     converter.setObjectMapper(objectMapper);
  6.     return converter;
  7.   }
  8.  
  9.   @Bean
  10.   public ObjectMapper jsonMapper() {
  11.     return new JacksonObjectMapper();
  12.   }
  13.  
  14.   @Bean
  15.   public ObjectMapper xmlMapper() {
  16.     return new XmlMapper();
  17.   }
  18. }

Most of this is avoid when beans are auto-scanned and references are managed through @Autowire or @Resource. But when that is not possible, never call a @Bean method directly.

Update:
Now if all of your configurations are annotated with @Config spring will create only a single instance of jsonMapper. This is something I didn’t notice until after I wrote this; thanks to Jeff Sheets and Aaron Hanson for helping me uncover this subtle difference. It is good to know how spring works, how it creates beans, and the differences between how configuration is done.

About the Author

Neil Buesing profile.

Neil Buesing

VP - Streaming Technologies

Neil has more than twenty-five years of Object-Oriented development experience, with twenty years in Java/J2EE application development. He has successfully delivered in the role of an architect and as lead developer.

Over the past 2 years, he has lead up the Real-Time Data Practice at Object Partners. He is a Kafka Architect, Developer, and Advocate presenting at multiple Kafka Summits and Meetups.

One thought on “Avoiding a common spring annotation configuration mistake

  1. Neil Buesing says:

    Looks like my observations and issues with this unique to how I was creating my spring configurations and profiles (and doing it dynamically). In my coding examples (above) I just used the @Configuration trying to simplify the examples. However, that nuance creates a big different.

    In the code below, the following is printed

    Creating jsonMapper
    Creating xmlMapper
    Creating jsonMapper
    Running.

    If I add @Configuration to the MappingConfiguration, I just get

    Creating jsonMapper
    Creating xmlMapper
    Running.

    public class Bootstrap {
    public static void main(final String args[]) {
    final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(MappingConfiguration.class);
    context.refresh();
    System.out.println(“Running.”);
    }
    }

    package com.objectpartners.buesing.config;

    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.dataformat.xml.XmlMapper;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

    public class MappingConfiguration {

    @Bean
    public MappingJackson2HttpMessageConverter converter(@Qualifier(“jsonMapper”) final ObjectMapper objectMapper) {
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    converter.setObjectMapper(jsonMapper());
    return converter;
    }

    @Bean
    public ObjectMapper jsonMapper() {
    System.out.println(“Creating jsonMapper”);
    return new ObjectMapper();
    }

    @Bean
    public ObjectMapper xmlMapper() {
    System.out.println(“Creating xmlMapper”);
    return new XmlMapper();
    }
    }

    My example is more of nested configuration where I include them, like the following and I will get two instances, however, if I don’t import MappingConfiguration and use the @Component on MappingConfiguration, I will only get one

    @ComponentScan(“com.objectpartners.buesing.config”)
    @Import(MappingConfiguration.class)
    public class ApplicationConfiguration {
    }

    package com.objectpartners.buesing.config;

    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.dataformat.xml.XmlMapper;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

    public class MappingConfiguration {

    @Bean
    public MappingJackson2HttpMessageConverter converter(@Qualifier(“jsonMapper”) final ObjectMapper objectMapper) {
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    converter.setObjectMapper(jsonMapper());
    return converter;
    }

    @Bean
    public ObjectMapper jsonMapper() {
    System.out.println(“Creating jsonMapper”);
    return new ObjectMapper();
    }

    @Bean
    public ObjectMapper xmlMapper() {
    System.out.println(“Creating xmlMapper”);
    return new XmlMapper();
    }
    }

    So while it is possible to use the method with @Configuration the solution to pass the bean in with the method parameter seems to always work (as expected).

  2. Roger Pack says:

    wow it even confuses the people doing the blog posts on it. Go Spring!

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Blog Posts
Using Conftest to Validate Configuration Files
Conftest is a utility within the Open Policy Agent ecosystem that helps simplify writing validation tests against configuration files. In a previous blog post, I wrote about using the Open Policy Agent utility directly to […]
SwiftGen with Image & Color Asset Catalogs
You might remember back in 2015 when iOS 9 was introduced, and we were finally given a way to manage all of our assets in one place with Asset Catalogs. A few years later, support […]
Tracking Original URL Through Authentication
If you read my other post about refreshing AWS tokens, then you probably have a use case for keeping track of the original requested resource while the user goes through authentication so you can route […]
Using Spring Beans in a Kafka Streams ExceptionHandler
There are many things to know before diving into Kafka Streams. If you haven’t already, check out these 5 things as a starting point. Bullet 2 mentions designing for exceptions. Ironically, this seems to be […]