Spring源码学习(四)在单值注入时如何按类型查找匹配的Bean

这是我学习Spring源码之后的第四篇文章,如果你想了解,之前的3篇文章请您查阅:

前3篇blog的地址:
1.Spring源码学习(-)别怕,老外点中餐与AbstractBeanFactory.getBean的主流程差不多
2.Spring源码学习(二)哎呦,按菜谱做菜与AbstractAutowireCapableBeanFactory.createBean流程差不多
3.pring源码学习(三)炒鸡丁与populateBean没区别

引言

我经常写如下代码:

@Autowired private AService aservice;

不知你是否也好奇,Spring是如果找到AService类型的Bean的呢?,此文,我们就聊聊这个->单值注入时如何按类型查找匹配的Bean.

单值注入时如何按类型查找匹配的Bean

很简单,核心就3步。

1.找到所有与类型匹配的bean,如果只有一个直接返回。

Spring在DefaultListableBeanFactory.findAutowireCandidates方法中实现。 其部分源码如下:

1String[] candidateNames = 2 BeanFactoryUtils .beanNamesForTypeIncludingAncestors 3 ( this, requiredType, true, descriptor.isEager());

这个beanNamesForTypeIncludingAncestors的作用就是,获取requiredType(AService)类型所有匹配的beanName(包含先祖BeanFactory)。

beanNamesForTypeIncludingAncestors内部是如果实现的呢?我概括了下简要逻辑如下:

  • 遍历所有的BeanDefinition,获得所有的BeanName.

  • 针对所有的BeanName,先尝试获取单例进行匹配,若未匹配上再以Bean Definition进行匹配。

  • 匹配时,如果Bean是FactoryBean,先尝试FactoryBean生产的实际Bean进行匹配,若未匹配上再以FactoryBean 进行匹配。

2.多个Bean匹配时,有首选,返回首选的bean。

DefaultListableBeanFactory.determinePrimaryCandidate实现了筛选首选Bean的逻辑, 其中的核心方法是isPrimary,该方法是判断当前Bean是否是首选Bean的。源码如下:

1protected boolean isPrimary(String beanName, Object beanInstance) { 2if (containsBeanDefinition(beanName)) { 3 return getMergedLocalBeanDefinition(beanName).isPrimary(); 4} 5BeanFactory parent = getParentBeanFactory(); 6 return (parent instanceof DefaultListableBeanFactory && ((DefaultListableBeanFactory) parent).isPrimary(beanName,beanInstance)); 7}

getMergedLocalBeanDefinition(beanName).isPrimary()方法,对应AbstractBeanDefinition的primary属性,该属性被赋值的地方是在AnnotatedBeanDefinitionReader.doRegisterBean方法中。有如下逻辑。

1//省略甚多代码...... 2for (Class<? extends Annotation> qualifier : qualifiers) { 3if (Primary.class == qualifier) { 4 abd.setPrimary(true); 5} 6//省略很多代码....

看到这,我们可以得出一个结论:

被@Primary注解的bean,单值注入时会作为首选。

3.没有首选,按优先级选择,返回优选的Bean。

Spring是如何确定Bean的优先级的呢?

在DefaultListableBeanFactory.determineHighestPriorityCandidate中,实现按优先级选择Bean 其中,获取Bean的优先级的逻辑在getPriority方法中,如下:

1protected Integer getPriority(Object beanInstance) { 2Comparator<Object> comparator = getDependencyComparator(); 3if (comparator instanceof OrderComparator) { 4 return ((OrderComparator) comparator).getPriority(beanInstance); 5} 6 return null; 7}

查看OrderComparator的实现类AnnotationAwareOrderComparator中的源码发现, 获取优先级的逻辑实际在在OrderUtils.getPriority 中

1public static Integer getPriority(Class<?> type) { 2if (priorityAnnotationType == null) { 3 return null; 4} 5Object cached = priorityCache.get(type); 6if (cached != null) { 7 return (cached instanceof Integer ? (Integer) cached : null); 8} 9Annotation priority = AnnotationUtils.findAnnotation(type, priorityAnnotationType); 10Integer result = null; 11if (priority != null) { 12 result = (Integer) AnnotationUtils.getValue(priority); 13} 14priorityCache.put(type, (result != null ? result : NOT_ANNOTATED)); 15return result; 16}

在OrderUtils 向上查找发现 priorityAnnotationType的值为:

priorityAnnotationType = (Class<? extends Annotation>) ClassUtils.forName("javax.annotation.Priority", OrderUtils.class.getClassLoader());

被@Priority注解的类,其值越小,在单值注入时,越优先选择。

Spring的源码非常多,仅有这3步当然是不行的,我准备了流程图,梳理了Spring单值注入时查找匹配Bean的流程。

单值注入时如何按类型查找匹配的Bean的流程图

下一篇想尝试写后处理,预计最晚10月13日!!,祝大家节日快乐!

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

spring源码

SpringIOC容器源码解析系列,建议大家按顺序阅读,欢迎讨论(_spring源码均为4.1.6.RELEASE版本_)1.Spring源码IOC容器(一)构建简单IOC容器(https://my.oschina.net/u/2377110/blog/902073)2.Spring源码IOC容器(二)Bean的定位解析注

Spring源码学习(四)在单值注入时如何按类型查找匹配的Bean - HelloWorld