前言
@Primary和@Qualifier注解都能解决bean冲突的问题,但是他们的解决方式不同。
@Primary的解决方式偏向于设置一个默认bean,@Qualifier偏向于直接指定一个bean(有点类似于@Resource),本文将带大家一探究竟(侧重在@Primary),@Qualifier注解的解析方式请直接参阅QualifierAnnotationAutowireCandidateResolver.java @Qualifier传送门
分析
@Primary的选定规则
@Primary注解是bean声明阶段的注解,不是bean使用阶段(注入)的注解,这就决定了它的选定规则跟bean的BeanDefinition有关,跟注入阶段的选定规则无关。当我们使用@Autowired注入一个bean,它会尝试获取众多候选bean中的默认(也就是@Primary标注的),源码解析如下:
//step1
//AutowiredAnnotationBeanPostProcessor.java
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
//bean注入
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
//step2
//DefaultListableBeanFactory.java
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
//以上省略若干行代码
//根据类型查找候选的bean,以beanName=>bean Map存储,可能会有多个候选
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(),
descriptor);
}
return null;
}
String autowiredBeanName;
Object instanceCandidate;
if (matchingBeans.size() > 1) {
//多个候选bean时的处理,处理方式为寻找@Primary
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
}
else {
// In case of an optional Collection/Map, silently ignore a non-unique case:
// possibly it was meant to be an empty collection of multiple regular beans
// (before 4.3 in particular when we didn't even look for collection beans).
return null;
}
}
instanceCandidate = matchingBeans.get(autowiredBeanName);
}
}
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
//前置省略若干代码
//从候选中寻找primary bean
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
//后置省略若干代码
}
protected boolean isPrimary(String beanName, Object beanInstance) {
String transformedBeanName = transformedBeanName(beanName);
if (containsBeanDefinition(transformedBeanName)) {
//最终从RootBeanDefinition中获取bean的primary属性
//如果有一个bean的BeanDefinition#primary属性为true,则默认选中
return getMergedLocalBeanDefinition(transformedBeanName).isPrimary();
}
BeanFactory parent = getParentBeanFactory();
return (parent instanceof DefaultListableBeanFactory &&
((DefaultListableBeanFactory) parent).isPrimary(transformedBeanName, beanInstance));
}
通过源码发现,最终是靠解析RootBeanDefinition对象中的属性来确认默认选中的bean,至于
RootBeanDefinition的知识,可以看这一篇 什么是MergedBeanDefinition?
所以,在声明阶段确定bean的primary属性用@Primary注解即可,如果需要动态修改bean的primary,请切记是操作bean的RootBeanDefinition,因为spring有2个获取BeanDefinition的方法,他们获取的也是不同的BeanDefinition,避免入坑。这里给出一个动态修改primary属性的方法。
public static void setPrimary(String beanName,boolean primary){
DefaultListableBeanFactory app = null; //这里你自己获取容器对象即可
//获取合并前的BeanDefinition
BeanDefinition beanDefinition = app.getBeanDefinition(beanName);
beanDefinition.setPrimary(primary);
//获取合并后的BeanDefinition,其实就是RootBeanDefinition
beanDefinition = app.getMergedBeanDefinition(beanName);
beanDefinition.setPrimary(primary);
}