【问题标题】:How to inject different ObjectMappers having different configurations through Autowired?如何通过 Autowired 注入具有不同配置的不同 ObjectMapper?
【发布时间】:2019-12-22 14:06:02
【问题描述】:

我正在尝试使用 2 个不同的 Jackson ObjectMappers,以便我可以在代码中使用其中任何一个进行切换。 2 个ObjectMappers 的配置会有一些细微差别。

在我的配置文件中,我将它们作为 2 种不同的方式:

@Configuration
public class ApplicationConfiguration {
    private ObjectMapper createMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
        // And a few more other common configurations
        return mapper;
    }

    @Bean
    @Qualifier("standardObjectMapper")
    public ObjectMapper standardObjectMapper() {
        ObjectMapper mapper = createMapper();
        SimpleModule module = new SimpleModule();
        module.addSerializer(Product.class, new StandardProductSerializer());
        mapper.registerModule(module);
        return mapper;
    }

    @Bean
    @Qualifier("specialObjectMapper")
    public ObjectMapper specialObjectMapper() {
        ObjectMapper mapper = createMapper();
        SimpleModule module = new SimpleModule();
        // In this mapper, I have a different serializer for the Product class
        module.addSerializer(Product.class, new SpecialProductSerializer());
        mapper.registerModule(module);
        return mapper;
    }
}

我的计划是在需要时注入并使用其中一个映射器。所以在我的测试中,我有这样的东西:

class SerializationTest {
    @Autowired
    @Qualifier("standardObjectMapper")
    private ObjectMapper standardObjectMapper;

    @Autowired
    @Qualifier("specialObjectMapper")
    private ObjectMapper specialObjectMapper;

    @Test
    void testSerialization() throws JsonProcessingException {
       Product myProduct = new Product("Test Product");

      String stdJson = objectMapper.writeValueAsString(myProduct);
      String specialJson = specialObjectMapper.writeValueAsString(myProduct);

      // Both stdJson and specialJson are of the same value even though they should be different because the mappers have different serializers!
    }
}

但是,2 个ObjectMappers、standardObjectMapperspecialObjectMapper 似乎都使用相同的StandardProductSerializer

我希望 specialObjectMapper 使用 SpecialProductSerializer,但事实并非如此。

这 2 个 ObjectMapper 不应该不同吗?我假设注入将基于他们各自的名字,因为他们的类型是相同的?

我应该如何解决这个问题,以便 2 个 ObjectMapper 可以使用不同的序列化器?


更新:

我尝试过使用 Qualifiers,但我收到一个错误,即找不到 bean:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.site.SerializationTest': Unsatisfied dependency expressed through field 'standardObjectMapper'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.fasterxml.jackson.databind.ObjectMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value="standardObjectMapper")}

【问题讨论】:

  • 你试过用Qualifiers来区分bean吗?顺便说一句:我建议使用构造函数或设置器注入。您可以使用@Mock@InjectMocks 轻松注入字段,但这会增加测试的执行时间。
  • @Turing85 我尝试使用限定符,但出现以下错误:Unsatisfied dependency expressed through field 'standardObjectMapper'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.fasterxml.jackson.databind.ObjectMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value="standardObjectMapper")}
  • 您是否使用@Qualifier(...) 注释了注入点 bean-method?
  • @Turing85 是的,我已经使用限定符更新了我的问题。这两个地方我都放了,名字都一样。
  • 你能确认beans已经创建好了,即这两个方法是在启动的时候执行的吗?

标签: java spring spring-boot jackson


【解决方案1】:

添加@Qualifier 应该可以解决您的问题

@Bean
@Qualifier("standardMapper")
public ObjectMapper standardObjectMapper() {
    ObjectMapper mapper = createMapper();
    SimpleModule module = new SimpleModule();
    module.addSerializer(Product.class, new StandardProductSerializer());
    mapper.registerModule(module);
    return mapper;
}

@Bean
@Qualifier("specialMapper")
public ObjectMapper specialObjectMapper() {
    ObjectMapper mapper = createMapper();
    SimpleModule module = new SimpleModule();
    // In this mapper, I have a different serializer for the Product class
    module.addSerializer(Product.class, new SpecialProductSerializer());
    mapper.registerModule(module);
    return mapper;
}

然后添加@Qualifier 和您的@Autowired

@Autowired
@Qualifier("standardMapper")
private ObjectMapper standardObjectMapper;

@Autowired
@Qualifier("specialMapper")
private ObjectMapper specialObjectMapper;

【讨论】:

  • 我已经尝试过了,它给了我这个错误:Unsatisfied dependency expressed through field 'standardObjectMapper'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.fasterxml.jackson.databind.ObjectMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value="standardObjectMapper")}
  • 是的,我已经使用限定符更新了我的问题。这两个地方我都放了,名字都一样。
  • @Carven 也许你的配置类没有被spring扫描并且没有创建bean,将你的类放在与你的引导类相同级别的配置文件夹中(@SpringBootApplication)
  • 我认为原因可能是我在单元测试文件中运行测试。由于单元测试文件默认位于不同的包中,因此组件扫描可能无法找到 bean。
  • 如果你创建了一个测试类,用@RunWith(SpringRunner.class) 注释它,@SpringBootTest 应该可以解决问题
【解决方案2】:

你可以使用@Qualifier注解来区分不同的ObjectMapper。 (见:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-autowired-annotation-qualifiers

@Qualifier("objectMapper1") 
@Autowired
private ObjectMapper objMapper1;

【讨论】:

    【解决方案3】:

    你能试试这个吗,它会根据名字注入bean

    @Autowired
    @Qualifier("standardObjectMapper")
    private ObjectMapper standardObjectMapper;
    
    @Autowired
    @Qualifier("specialObjectMapper")
    private ObjectMapper specialObjectMapper;
    

    希望这对你有用

    【讨论】:

      【解决方案4】:

      可以使用@Qualifier注解:

      @Qualifier("specialObjectMapper") 
      @Autowired
      private ObjectMapper specialObjectMapper;
      
      @Qualifier("standardMapper")
      @Autowired
      private ObjectMapper standardObjectMapper;
      

      【讨论】:

        【解决方案5】:

        问题是@Configuration-class 在 DI 启动期间没有被扫描。通常,spring会从Application类(注解@SpringBootApplication)所在的包中寻找配置。

        我建议将@Configuration 类移到@SpringBootApplication 所在的子包中。

        还有其他方法(例如this one described at baelung)来更改组件扫描的根包。但是,除非已用尽所有其他选项,否则我不建议这样做。

        【讨论】:

        • 由于我当前在测试文件中运行该文件,因此测试文件与配置文件位于不同的包中。我尝试在运行主应用程序时设置断点并且确实创建了 bean。但是当我运行测试文件时,没有命中 2 个 bean 中的断点。
        • 还有其他方法(例如this one described at baelung)可以让您的测试正常工作。但是,我不建议使用这种方法。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-09
        • 1970-01-01
        • 2018-01-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多