问题排查修复记录!自定义ObjectMapper导致SpringBoot中的ObjectMapper失效问题修复

949 阅读3分钟

这是我参与2022首次更文挑战的第9天,活动详情查看:2022首次更文挑战

问题

  • 当使用自定义的ObjectMapper注册为Bean之后,这样导致了SpringBoot原生的ObjectMapper的配置失效

原因

  • 因为自定义的ObjectMapper覆盖了SpringBoot中原生的ObjectMapper配置,导致只有自定义的ObjectMapper生效

解决

方法一

  • 根据原生ObjectMapper中的源码里的 @ConditionalOnMissingBean(ObjectMapper.class) 可以知道:
    • 原生的ObjectMapper配置是要在项目中没有其余的ObjectMapper出现时才会生效
  • 所以可以在自定义ObjectMapper中添加 @ConditionalOnBean注解:
    • 使得自定义的ObjectMapper在原生的ObjectMapper出现之后才会注册
    • 由于原生的ObjectMapper带有 @Primary, 所以会优先使用原生的ObjectMapper

方法二

  • 通过使用 @ConditionOnBean注解可以使得ObjectMapper不会消失,但是却引出了一个CrudMapper消失的问题:
    • 可以使用 @DependsOn来控制Bean的初始化顺序.使得自定义的ObjectMapper在原生的jsonHandler加载之后再进行初始化,因为这个时候默认的ObjectMapper已经加载

方法三

  • 通过使用 @DependsOn解决了ObjectMapperCrudeMapper消失的问题,但是却引出了一个循环依赖的问题:
    • 由于新的ObjectMapperObjectMapper的子类,而默认的ObjectMapper一旦发现有其余的ObjectMapperBean, 就不会注册
    • 这样就导致了其余默认的ObjectMapperBean对新的ObjectMapper出现了引用.这就导致了循环依赖的问题
  • 解决方法:
    • 使用泛型作为返回类型,抹除本来的类型签名
    • 这样config中就不会存在ObjectMapperBean定义,从而使得Jackson默认的ObjectMapper可以加载
    • 然后添加 @DependsOn注解在jsonHandler之后,确保在默认的ObjectMapper之后出现
  • 注意:
    • JavaAnnotation配置中 ,Bean的默认ID就是使用了 @Bean注解的方法的名字
    • @Autowired注解默认装配是ByType
    • @Resource注解默认装配是ByName
      • 通过抹除类型可以防止ByType的装配而引发的循环依赖问题
      • 但是抹除类类型之后就无法使用ByType装配
    • 抹除了类型之后需要使用 @Resource 来注入新的ObjectMapper

总结

  • SpringBootBean相关的注解:
注解参数说明
@ConditionalOnBeantype - Bean的class对象
value - Bean的名称
仅当指定的Bean存在时,此注解修饰的内容才会起作用
@ConditionalOnMissingBeantype - Bean的class对象
value - Bean的名称
仅当指定的Bean不存在时,此注解修饰的内容才会起作用
@ConditionOnClassname - 类名
value - 类的class对象
仅当class存在时,此注解修饰的内容才会起作用
@ConditionalOnMissingClassvalue - 类名仅当class不存在时,此注解修饰的内容才会起作用
@Conditionalvalue - Condition接口的实现类根据指定的类别判断是否让修饰的内容生效
@DependsOnvalue(String[] Bean名称)标注当前Bean需要依赖的Bean. 此注解修饰的Bean必须在指定的Bean初始化之后才会起作用
注解说明
@Primary优先使用此注解修饰的Bean