Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't inject multiple ObjectMapper beans in Spring Boot application

I am defining beans using @Bean annotation and trying to wire them using name , but receiving an exception.

Complete Example

package com.example;

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

@SpringBootApplication
@ComponentScan({"com.example"})
public class SampleSpringBootApplication {

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


    @Bean
    public ObjectMapper scmsObjectMapper() {
        com.fasterxml.jackson.databind.ObjectMapper responseMapper = new com.fasterxml.jackson.databind.ObjectMapper();
        return responseMapper;
    }

    @Bean
    public ObjectMapper scmsWriteObjectMapper() {
        com.fasterxml.jackson.databind.ObjectMapper responseMapper = new com.fasterxml.jackson.databind.ObjectMapper();
        return responseMapper;
    }
}

Controller

package com.example;

import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SampleController {

    @RequestMapping(method={RequestMethod.GET}, value="sample/hello", produces=MediaType.APPLICATION_JSON_VALUE)
    @ResponseStatus(HttpStatus.OK)
    public @ResponseBody ResponseEntity<String> getCart() {
        return new ResponseEntity<String>("Hello", HttpStatus.OK);
    }


}

build.gradle

buildscript {
    ext {
        springBootVersion = '1.2.7.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 
        classpath('io.spring.gradle:dependency-management-plugin:0.5.2.RELEASE')
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot' 
apply plugin: 'io.spring.dependency-management' 

jar {
    baseName = 'Sample-SpringBoot'
    version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.boot:spring-boot-starter')
    testCompile('org.springframework.boot:spring-boot-starter-test') 
    compile("org.springframework.boot:spring-boot-starter-web:1.2.3.RELEASE")
    compile('com.fasterxml.jackson.core:jackson-databind:2.5.1')
}


eclipse {
    classpath {
         containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
         containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'
    }
}

task wrapper(type: Wrapper) {
    gradleVersion = '2.7'
}

Exception

 Caused by:
 org.springframework.beans.factory.NoUniqueBeanDefinitionException: No
 qualifying bean of type [com.fasterxml.jackson.databind.ObjectMapper]
 is defined: expected single matching bean but found 2:
 scmsObjectMapper,scmsWriteObjectMapper
like image 380
Punter Vicky Avatar asked Oct 19 '15 21:10

Punter Vicky


1 Answers

By default, Spring Boot defines a bean of type MappingJackson2HttpMessageConverter and tries to inject an ObjectMapper into it. This typically allows you to simply declare an ObjectMapper @Bean, configure it any way you need to, and have Spring Boot do the rest for you.

Here, this has backfired because you declare two of them.

One solution, as described in the documentation, is to annotate the @Bean definition of the one you want injected as @Primary.

If you want to replace the default ObjectMapper completely, define a @Bean of that type and mark it as @Primary.

For example,

@Bean
@Primary
public ObjectMapper scmsObjectMapper() {
    com.fasterxml.jackson.databind.ObjectMapper responseMapper = new com.fasterxml.jackson.databind.ObjectMapper();
    return responseMapper;
}

Spring Boot will use that one.

Alternatively, you can declare your own MappingJackson2HttpMessageConverter bean definition and configure everything internally. From the same documentation

Finally, if you provide any @Beans of type MappingJackson2HttpMessageConverter then they will replace the default value in the MVC configuration.

For example (taken from here)

@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
    MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    jsonConverter.setObjectMapper(objectMapper);
    return jsonConverter;
}
like image 52
Sotirios Delimanolis Avatar answered Sep 28 '22 10:09

Sotirios Delimanolis