Spring BeanDefinition primary属性有多重要?

前言

@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);

        

    }

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值