2

I am having a problem with a newly created Spring Boot application. For some reason something is adding two MappingJackson2HttpMessageConverter instances to the return value processing of Spring Web MVC (I found this out using the debugger). One of them uses the ObjectMapper instance I create via a @Bean method in my configuration class, the other one uses some random other instance. This 2nd ObjectMapper is therefor completely unconfigured and my ObjectMapper configurations are not applied to the JSON serialization used by @ResponseBody.

How can I stop this duplication? You can find all of my code below.

Edit: This problem is not related to my custom ObjectMapper bean. Even if I remove it (completely empty configuration except @SpringBootApplication), I still get two MappingJackson2HttpMessageConverter instances, one of which uses a completely unconfigured and unreachable (as far as I can tell) ObjectMapper.

Edit²: This is not an issue of duplicate beans. ApplicationContext.getBeansOfType returns only one instance for both ObjectMapper and MappingJackson2HttpMessageConverter no matter if I create the ObjectMapper myself or let it be created by spring autoconfiguration.

build.gradle:

buildscript {
    ext.springBootVersion = '2.0.0.RC1'

    repositories {
        mavenCentral()
        maven { url 'http://repo.spring.io/snapshot' }
        maven { url 'http://repo.spring.io/milestone' }
        maven { url 'http://repo.spring.io/libs-snapshot' }
    }
    dependencies {
        classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
    }
}

group 'de.takeweiland.springtest'
version '1.0-SNAPSHOT'

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

repositories {
    mavenCentral()
    maven { url 'http://repo.spring.io/snapshot' }
    maven { url 'http://repo.spring.io/milestone' }
    maven { url 'http://repo.spring.io/libs-snapshot' }
}

dependencies {
    compile "org.springframework.boot:spring-boot-starter-web"
}

Configuration class:

package main;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Main {

    @Bean
    public ObjectMapper createObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        return mapper;
    }

    public static void main(String[] args) {
        SpringApplication.run(Main.class);
    }
}

Controller:

package main;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MyController {

    @GetMapping("**")
    @ResponseBody
    public String test() {
        return "Hello World";
    }
}
diesieben07
  • 1,487
  • 1
  • 14
  • 25
  • just so you know, if you use the annotation RestController, it combines Controller & ResponseBody so you can just use RestController & omit ResponseBody. –  Jan 31 '18 at 13:07
  • Thanks, I know about that. However I was keeping this example to an absolute minimum to make it easier to figure out where this problem is coming from. – diesieben07 Jan 31 '18 at 13:09
  • well. 1 annotation instead of 2 would be minimal... just sayin' –  Jan 31 '18 at 13:10
  • why you instantiate ObjectMapper class? – andolsi zied Jan 31 '18 at 13:12
  • Because I want to configure the `ObjectMapper` by, for example, adding mixin annotations, etc. Even if I take the alternative approach of just registering a `com.fasterxml.jackson.databind.Module` as a Spring `@Component`, which will automatically register it to the standard `ObjectMapper`, only _one_ of the created `ObjectMapper` instances will have those modules. – diesieben07 Jan 31 '18 at 13:14
  • For customizing the objectmapper, don't create a new object Mapper in your application. Instead you can customize the objectmapper provided by spring. Check my answer here https://stackoverflow.com/questions/48519772/how-to-configure-jackson-in-spring-boot-application-without-overriding-springs-d/48519868#48519868 – pvpkiran Jan 31 '18 at 13:18
  • This way you can get rid of multiple Objectmapper issue – pvpkiran Jan 31 '18 at 13:18
  • No, I cannot. Even if I remove my custom `ObjectMapper` bean (no configuration at all except `@SpringBootApplication`) I still get _two_ `MappingJackson2HttpMessageConverter` instances. One of them uses the `ObjectMapper` that is available via Spring DI, which is therefor configurable, the other uses _some other mapper_, which I do not know where it comes from. – diesieben07 Jan 31 '18 at 13:20
  • Hmm, the problem can be that `org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration` has a `@Primary @ConditionalOnMissingBean(ObjectMapper.class)` marked ObjectMapper. Check that your configuration is loaded before this autoconfiguration. If the name of second `ObjectMapper` bean is 'jacksonObjectMapper' - load a configuration before autoconfiguration. Also that mapper is marked as `@Primary` so it is injected with DI by default. – Bogdan Oros Jan 31 '18 at 13:20
  • @BogdanOros Does that apply even if my configuration is completely empty (see above)? How can I load my configuration before said autoconfiguration? – diesieben07 Jan 31 '18 at 13:22
  • Try to create a configuration class with that bean with `@Order(Ordered.HIGHEST_PRECEDENCE)` annotation. – Bogdan Oros Jan 31 '18 at 13:25
  • @BogdanOros That made absolutely no difference. I still get the two converters. – diesieben07 Jan 31 '18 at 13:27
  • Try to mark you `ObjectMapper` with `@Primary` annotation and check which one is injected by DI. Also can you paste the second mappers name? – Bogdan Oros Jan 31 '18 at 13:32
  • Please read the edit to my question. This even happens if I do not make my own `ObjectMapper` at all! I also verified using `ApplicationContext.getBeansOfType`: There is only _one_ `ObjectMapper` in the IoC container (whether created by me or spring boot autoconfiguration). But there are _still_ two `MappingJackson2HttpMessageConverter` instances in the return value mapper. – diesieben07 Jan 31 '18 at 13:39
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/164267/discussion-between-bogdan-oros-and-diesieben07). – Bogdan Oros Jan 31 '18 at 13:40

2 Answers2

0

After further investigation I have found the issue and it was simply an issue with my Jackson configuration not working as I thought it would. Having two MappingJackson2HttpMessageConverter is not a problem.

diesieben07
  • 1,487
  • 1
  • 14
  • 25
  • So what was the misconfiguration on your side...? We are likely facing similar problem, but no solution is provided here :) – Michal Hosala Feb 06 '23 at 15:25
0

The reason of two jackson converters with spring boot and web mvc:

HttpMessageConvertersAutoConfiguration creates the stringHttpMessageConverter and a mappingJackson2HttpMessageConverter (with configurable object mapper) beans. HttpMessageConvertersAutoConfiguration also creates a HttpMessageConverters bean and takes the previously mentioned converters as a constructor param with the name "additionalConverters". The used constructor has an add default converters flag with true value as well. It gets the default converters by instantiating a WebMvcConfigurationSupport class and calling its getDefaultConverters method. That method creates a second mappingJackson2HttpMessageConverter (with a default, non configurable object mapper). HttpMessageConverters concats the converters passed through the constructor with default ones from WebMvcConfigurationSupport instance.

The HttpMessageConverters containing the duplicate json converter is used in example by RestTemplateAutoConfiguration hence by RestTemplateBuilder.

zolee
  • 429
  • 4
  • 19