背景:引用的jar包中与项目中同时存在redisConfig情况
最近同事在引用我的jar包的时候,出现了如下的错误:
Annotation-specified bean name 'redisConfig' for bean class [com.xxx.RedisConfig] conflicts with existing, non-compatible bean definition of same name and class [com.xxx1.RedisConfig]
经过交流后得知,因为我这边配置了RedisConfig以方便操作redisTemplate。(之前我未考虑到我提供的RedisConfig是简易配置,用户可能需要扩展的情况)
既然遇到了冲突问题,那么就解决吧!
spring提供了一个注解:@ConditionalOnMissingBean,这个注解的作用是当上下文(可以通过search属性指定上下文范围)中存在相应的bean的时候,不采用当前名称的bean
官方api
@Target(value={TYPE,METHOD})
@Retention(value=RUNTIME)
@Documented
@Conditional(value=org.springframework.boot.autoconfigure.condition.OnBeanCondition.class)
public @interface ConditionalOnMissingBean
Conditional that only matches when no beans of the specified classes and/or with the specified names are already contained in the BeanFactory.
When placed on a @Bean method, the bean class defaults to the return type of the factory method:
@Configuration
public class MyAutoConfiguration {
@ConditionalOnMissingBean
@Bean
public MyService myService() {
...
}
}
In the sample above the condition will match if no bean of type MyService is already contained in the BeanFactory.
The condition can only match the bean definitions that have been processed by the application context so far and, as such, it is strongly recommended to use this condition on auto-configuration classes only. If a candidate bean may be created by another auto-configuration, make sure that the one using this condition runs after.
解决问题,步骤如下
- 首先我修改了自己RedisConfig类的名称,加上我项目的前缀,例:VsRedisConfig
- 因为配置自定义RedisConfig最关键的两个方法是redisTemplate和connectionFactory:
@Bean(name = "rt")
public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
@Bean
public JedisConnectionFactory connectionFactory() {
JedisConnectionFactory factory = new JedisConnectionFactory();
factory.setHostName(host);
factory.setPort(Integer.parseInt(port));
factory.setPassword(password);
factory.setTimeout(5000);
return factory;
}
所以,在两个方法上分别加上:
@ConditionalOnMissingBean(value = RedisTemplate.class) // search策略默认为All,所以可以不写上去
@ConditionalOnMissingBean(value = JedisConnectionFactory.class)
这样,用户在引用我的jar包的时候,就不会访问我的RedisConfig了(因为Springboot在加载类的顺序上,是当前项目优先,其次是jar包中的),而是会去访问使用方的RedisConfig