使用自定义ObjectMapper导致原生ObjectMapper配置失效问题
问题
- 当使用自定义的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解决了ObjectMapper和CrudeMapper消失的问题,但是却引出了一个循环依赖的问题:
- 由于新的ObjectMapper是ObjectMapper的子类,而默认的ObjectMapper一旦发现有其余的ObjectMapper的Bean, 就不会注册
- 这样就导致了其余默认的ObjectMapper的Bean对新的ObjectMapper出现了引用.这就导致了循环依赖的问题
-
解决方法:
- 使用泛型作为返回类型,抹除本来的类型签名
- 这样config中就不会存在ObjectMapper的Bean定义,从而使得Jackson默认的ObjectMapper可以加载
- 然后添加 @DependsOn注解在jsonHandler之后,确保在默认的ObjectMapper之后出现
-
注意:
-
在Java的Annotation配置中 ,Bean的默认ID就是使用了 @Bean注解的方法的名字
-
@Autowired注解默认装配是ByType
-
@Resource注解默认装配是ByName
- 通过抹除类型可以防止ByType的装配而引发的循环依赖问题
- 但是抹除类类型之后就无法使用ByType装配
-
抹除了类型之后需要使用 @Resource 来注入新的ObjectMapper
-
总结
- SpringBoot和Bean相关的注解:
| 注解 | 参数 | 说明 |
|---|---|---|
| @ConditionalOnBean | type - Bean的class对象 value - Bean的名称 | 仅当指定的Bean存在时,此注解修饰的内容才会起作用 |
| @ConditionalOnMissingBean | type - Bean的class对象 value - Bean的名称 | 仅当指定的Bean不存在时,此注解修饰的内容才会起作用 |
| @ConditionOnClass | name - 类名 value - 类的class对象 | 仅当class存在时,此注解修饰的内容才会起作用 |
| @ConditionalOnMissingClass | value - 类名 | 仅当class不存在时,此注解修饰的内容才会起作用 |
| @Conditional | value - Condition接口的实现类 | 根据指定的类别判断是否让修饰的内容生效 |
| @DependsOn | value(String[] Bean名称) | 标注当前Bean需要依赖的Bean. 此注解修饰的Bean必须在指定的Bean初始化之后才会起作用 |
| 注解 | 说明 |
|---|---|
| @Primary | 优先使用此注解修饰的Bean |