从源码分析@Qualifier,@Primary,@Priority的候选顺序

注解分析

@Qualifier和@Primary都是属于spring框架下的注解,@Priority属于javax.annotation,JSR250规范。

@Primary官方文档的大概意思是,如果在自动注入的多个候选bean中,有一个primary bean的话,那么这个bean就会是自动注入的目标bean。可以作用在类和方法上

@Qualifier官方的大概意思是,自动注入的时候,使用了这个注解,那么在候选bean中,就会用这个注解指定的那个bean。作用在字段,方法,类,参数,注解上。

@Priority大概意思是说明使用的顺序,作用在参数和类上。

代码测试

新建几个类:

待注入的接口:

public interface WhichSelected {
    String beanName();
}

三个实现类:

@Component
@Primary
public class PrimarySelected implements WhichSelected{
    @Override
    public String beanName() {
        return "primarySelected";
    }
}

@Component
@Priority(1)
public class PrioritySelected implements WhichSelected{
    @Override
    public String beanName() {
        return "prioritySelected";
    }
}

@Component
@Priority(2)
public class PriorityNextSelected implements WhichSelected{
    @Override
    public String beanName() {
        return "priorityNextSelected";
    }
}

注入的类:

@Component
public class TargetInject {

    @Autowired
    private WhichSelected whichSelected;

    public void whichBean() {
        System.out.println(whichSelected.beanName());
    }

}

包扫描类:

@Configuration
@ComponentScan("spring.postprocessor.choseorder")
public class PackageScannerConfig {
}

测试类:

public class OrderTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PackageScannerConfig.class);
        TargetInject targetInject = context.getBean(TargetInject.class);
        targetInject.whichBean();
    }

}

运行结果:

primarySelected

修改代码:

    @Autowired
    @Qualifier("priorityNextSelected")
    private WhichSelected whichSelected;
	结果:
	priorityNextSelected

注掉//@Primary

@Component
//@Primary
public class PrimarySelected implements WhichSelected{
    @Override
    public String beanName() {
        return "primarySelected";
    }
}
结果:
prioritySelected

修改代码,去掉所有的@Qualifier,@Priority,@Primary,运行:
很明显会报错,找不到要注入的bean

再次修改:
将private WhichSelected whichSelected;改为private WhichSelected prioritySelected; 运行结果为prioritySelected

代码小结

从上面五种情况可以看出,第一优先候选bean是@Qualifier注解指定的bean,第二优先是@Primary注解的bean,第三是@Priority(1)以及其后面的顺序,最后是bean名称。

源码调试

先将代码恢复到上面最开始的状态。

我们都知道这一行代码:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PackageScannerConfig.class);

是获取IOC容器的,点到AbstractApplicationContext类里面的refresh()方法,可以看到其实创建的是一个ConfigurableListableBeanFactory,
这是一个接口,我们来到DefaultListableBeanFactory,这个类实现了这个接口。

这里讨论的注入是用@Autowired注入的,其实很多注解都是通过后置处理器实现的,@Autowired注解也是,对应的后置处理器是:AutowiredAnnotationBeanPostProcessor,
这个类实现了PriorityOrdered接口,可以自行看一下后置处理器的加载顺序,这里不讨论。注入的时机和其他创建bean初始化属性的时机都是在populateBean的时候进行的。

来到AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues方法

在这里插入图片描述继续F8会来到AbstractAutowireCapableBeanFactory的populateBean方法,可以看到这个方法里面也是一系列的后置处理器的处理。这里只需要看一下就可以,一直按F9,到beanName是targetInject的时候停下
在这里插入图片描述
按F8来到AutowiredAnnotationBeanPostProcessor的inject方法,来看那一下调用栈:
在这里插入图片描述

继续向下走,会来到容器解决依赖这里DefaultListableBeanFactory的doResolveDependency方法,走到这里是关键
在这里插入图片描述
step into进去
在这里插入图片描述
这里就能看到候选bean的名字了,走到下一个for循环
在这里插入图片描述
这里是重点,第一轮淘汰,待会说到@Qualifier再点进去,看字面意思就是判断一下不是自我引用和是候选bean,其实就是这个意思,满足条件就加进去,在上面的代码情况下,会全部加进去
在这里插入图片描述
继续向下走,走到doResolveDependency的这里
在这里插入图片描述
就开始从多个候选bean里面筛选。继续向下
进到determineAutowireCandidate方法里面,其实在这个方法里面就可以看出顺序了
在这里插入图片描述
先是primary,然后priority,最后beanName。

继续进入方法determinePrimaryCandidate
可以看到最主要的判断方法是isPrimary,进去
在这里插入图片描述
点进去isPrimary
在这里插入图片描述
可以看到是AbstractBeanDefinition下面的一个Boolean属性,此时看一下bean定义信息:
在这里插入图片描述
可以看到primarySelected是一个ScannedGenericBeanDefinition,其中ScannedGenericBeanDefinition又是AbstractBeanDefinition的子类
在这里插入图片描述
所以肯定有这个属性信息,果然有,为true
在这里插入图片描述
还可以看到一些常见的属性,单例,懒加载等等。
为true,所以可以通过,来到下一步:
在这里插入图片描述
可以看到一种类型的bean,只能有一个primary。最后返回bean名称,将这个bean作为选中的bean注入。
点进determineHighestPriorityCandidate看是一样的,不能有同等级优先级。

这样候选bean的选取就结束了,现在加一个@Qualifier。
现在来到上面说的DefaultListableBeanFactory-》findAutowireCandidates的这个for循环:
在这里插入图片描述
继续深入:isAutowireCandidate(candidate, descriptor) -》isAutowireCandidate(beanName, descriptor, getAutowireCandidateResolver()) -》isAutowireCandidate(beanName, getMergedLocalBeanDefinition(beanDefinitionName), descriptor, resolver)-》QualifierAnnotationAutowireCandidateResolver的isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor)
进入:checkQualifiers
在这里插入图片描述
这里就是检查@Qualifier这个注解
在这里插入图片描述
如果是@Qualifier,继续下一步
如果beanName和@Qualifier一致,就会返回true,上面的那个for循环就能加进去,最终的result只有一个元素:
在这里插入图片描述
因为只有一个元素,所以就不会来到后面的检查,因为:
在这里插入图片描述
大于1才会后面的检查。
至此,整个过程就结束了。

验证

可以看到容器DefaultListableBeanFactory的determineAutowireCandidate方法是protected,所以我们可以继承这个容器,自己来写选取顺序,来验证上面的调试。

把代码考过来,交换priorityCandidate和primaryCandidate的顺序:

public class MyListableBeanFactory extends DefaultListableBeanFactory {

    @Override
    protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
        Class<?> requiredType = descriptor.getDependencyType();
        String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
        if (priorityCandidate != null) {
            return priorityCandidate;
        }
        String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
        if (primaryCandidate != null) {
            return primaryCandidate;
        }
        return null;
    }
}

去掉@Qualifier,修改测试方法:

public class OrderTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(new MyListableBeanFactory());
        context.register(PackageScannerConfig.class);
        context.refresh();
        TargetInject targetInject = context.getBean(TargetInject.class);
        targetInject.whichBean();
    }

}

运行,可以看到输出的结果是:

prioritySelected

和之前的结果不一样,和我们改完的代码逻辑一致,说明调试都是正确的。

补充一点,AutowiredAnnotationBeanPostProcessor会作用于三种注解:@Autowired,@Value和JSR330的@Inject:

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值