这是我参与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解决了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 |