上篇博客说了自动注入模型的使用,这篇笔记打算记录下自动注入模型中,autowireMode为0的情况,也就是我们经常用到的@Autowired、@Resource注解的原理

首先对于这两个注解,都是由后置处理器来完成解析的,解析的时机是一样的,在第六个后置处理器执行的时候;但是是由不同的后置处理器来完成解析的
@Autowired注解:

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues

@Resource注解:

org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessPropertyValues

前言

1、这两个注解,都是在第六个后置处理器执行的时候,进行解析的,但是在第三个处理器

org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition

执行的时候,会先解析一下当前bean中有哪些属性要进行注入,在第六个后置处理器执行的时候,就无须进行解析,直接从map中获取要注入的属性即可

2、这两个注解虽然是由不同的后置处理器处理的,但是处理的思想、步骤大致都是一样的:

  1. 会先尝试获取bean中添加了该注解的方法(method)和属性(field)
  2. 找到之后,会根据实现类的优先级、或者是name等从容器中或者beanDefinitionMap中找到对应的属性
  3. 通过filed.set()完成属性的注入

3、这里有几个点要先行说明:

  • 对于@Resource注解,会根据要注入属性的name,从单实例池中获取bean,完成属性注入
  • 对于@Autowired注解,我们通常说是现根据类型注入,如果找到多个或者没有找到,再根据name进行注入;这里要说明的是:spring在解析@Autowired注解的时候,并不是直接从单实例池中查找bean,而是先从beanDefinitionMap中,根据type查找,如果找到多个,再根据name从单实例池中查找,只有找到确定要注入哪个类,才会从单实例池中找到对应的bean,完成注入
  • 当然,和@Autowired注解配合使用的注解有好几个,在下面会一一解析

@Autowired


这是@Autowired注解解析的整个流程,其中,和@Autowired注解配合使用的有几下几个注解:
@Qualifier
@Primary
@Priority

第一个注解使用在field上,在根据类型从beanDefinitionMap中找到bean之后,会判断哪个beanName是否是合格的;如果要注入的bean有多个实现类,会优先注入@Qualifier注解指定的实现类

第二个直接是在类上使用的,在未添加@Qualifier注解的场景下,如果有多个实现类,会判断哪个实现类上添加了@Primary注解,添加了,就注入该实现类

第三个注解也是加载类上,如果既没有添加@Qualifer、也没有添加@Priority注解,那么,会判断哪个实现类的优先级比较高,值越小,优先级越高

这是

org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency

的部分代码

/*** @Autowired注解源码 三*  查找要注入的bean,按照type来查找,如果匹配到一个,就获取到这个实例,然后返回,返回之后,会调用field.set();注入进去*  如果找到了多个,那就先判断哪个实现类有@primary注解,如果都没有注解,那就判断priority的优先级**  这个方法中,对Qualifier注解进行了处理;如果一个接口有多个实现类,但是没有加@Qualifier注解,那么就会返回多个**  如果@Qualifier指定的value在spring容器中没有,这里返回的map集合就是空**/
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {if (isRequired(descriptor)) {// 如果匹配到的要注入的bean是null,就报错raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);}return null;
}String autowiredBeanName;
Object instanceCandidate;if (matchingBeans.size() > 1) {/*** 如果根据类型,匹配到有多个bean,那就判断@Primary注解和@Priority注解;*  所以:可以得出一个结论:*    当一个接口有多个实现类的时候,添加了@Qualifier注解,同时在一个类上添加了@Primary注解,那么会注入@Qualifier注解对应的bean**    在下面的这个推断方法中:*     1.会先判断@Primary注解*      2.再判断@Priority注解声明的优先级*    3.如果两个注解都没有,那就根据controller中注入的service的name和matchingBeans中的beanName进行匹配*        如果匹配上,就注入匹配上的beanName对应的beanClass*       这里需要注意的是:根据name查询的时候,会先根据type从beanDefinitionMap中查询到符合类型的beanDefinitionNames*      然后从beanDefinitionNames中依次匹配,是否和name一致*/autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);if (autowiredBeanName == null) {if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {return descriptor.resolveNotUnique(type, 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;}}/*** 如果能找到一个,那就根据name获取到对应的instance实例,然后再field.set();*/instanceCandidate = matchingBeans.get(autowiredBeanName);
}
else {// We have exactly one match.Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();autowiredBeanName = entry.getKey();instanceCandidate = entry.getValue();
}if (autowiredBeanNames != null) {autowiredBeanNames.add(autowiredBeanName);
}
if (instanceCandidate instanceof Class) {/*** 根据获取到的beanName和type从容器中查找。如果没有找到,就创建* 这里才是真正的从单实例池中获取对象,如果对象没有,就创建*/instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}

在注释中,详细注释了哪行代码解析了哪个注解,我想着重看下
determineAutowireCandidate这个方法

/*** 如果根据type获取到有多个符合的bean,那就来这个方法中,推断出其中的一个* @param candidates:推断出来的多个bean* @param descriptor:要注入的属性对应的类型,如果是接口,那这里就是接口的类型* @return*/
@Nullable
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {Class<?> requiredType = descriptor.getDependencyType();/*** 判断哪个bean有@Primary注解,返回注解对应的beanName*/String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);if (primaryCandidate != null) {return primaryCandidate;}/*** 如果都没有添加@Primary注解,那就判断bean是否有添加@Priority注解,注解值越小,优先级越高;返回优先级高的beanName*/String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);if (priorityCandidate != null) {return priorityCandidate;}// Fallback/*** 这里的descriptor中保存的是注入的bean对应的名称(在controller注入service,这里就是controller中的service属性名)* 判断 从容器中拿出来的beanName是否和controller中的service属性名一致,如果一致,就注入一致的这个*  这也就是在controller注入service的时候,如果service有多个实现类,那么,如果注入的service属性名和实现类的beanName一致,也可以注入成功的原因**  class Controller{* @Autowired* Service service01*  }*   如果service有多个实现类,其实注入的是service01,就是这里判断的**   这里我觉得,也是我们平常所说的,如果根据type找到多个,或者没有找到,就根据name来注入,这里就是根据name来注入的这一步*   candidates:是根据类型从beanDefinitionMap中获取到的符合类型的beanDefinitionNames*   descriptor.getDependencyName():是@Autowired注解属性注入的属性name*/for (Map.Entry<String, Object> entry : candidates.entrySet()) {String candidateName = entry.getKey();Object beanInstance = entry.getValue();if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||matchesBeanName(candidateName, descriptor.getDependencyName())) {return candidateName;}}return null;
}

通过这两段代码,我觉得可以说明一个问题:

  1. 对于@Autowired注解,spring在处理的时候:会先解析@Qualifer注解,然后解析@Primary注解,然后再解析@Priority注解
  2. 如果解析之后,匹配的bean还是有多个,那就根据name来查找,需要注意的是这里的查找都是基于从beanDefinitionMap中找到的符合注入条件的bean;
  3. 只有在确认要注入的是哪个实现类之后,才会从单实例池中获取实例化之后的bean

这里有一个点再补充一下,我前面一直在说的这个点:所有的查找都是基于从beanDefinitionMap中查找到符合注入条件的bean:下面这段代码可以证实这个观点:

这里的代码,我就不再追进去了,因为这里的底层,可以看到是遍历beanDefinitionNames这个集合,依次根据beanDefinitionName获取到beanDefinition,然后判断beanDefinition是否是非抽象类,是否是非懒加载的、是否是单实例的,是否是type类型的等等,如果满足,就把当前beanName返回

@Resource


这是一个简单的流程图,图里面基本上就把@Resource注解的解析说明白了,我们看为什么说@Resource是根据name来注入属性的,只需要看图中的最后一个方法

protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)throws NoSuchBeanDefinitionException {Object resource;Set<String> autowiredBeanNames;String name = element.name;if (this.fallbackToDefaultTypeMatch && element.isDefaultName &&factory instanceof AutowireCapableBeanFactory && !factory.containsBean(name)) {autowiredBeanNames = new LinkedHashSet<>();/*** 这里也可以佐证@Resource是根据name来进行注入的,这里代码追进去,可以看到,是也是调用的doGetBean()* 所以:无论是走这里,还是下面的getBean,都是从单实例池中尝试获取bean的*/resource = ((AutowireCapableBeanFactory) factory).resolveDependency(element.getDependencyDescriptor(), requestingBeanName, autowiredBeanNames, null);if (resource == null) {throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");}}else {/*** 之所以说@Resource是按照name来注入的,就是这里的代码可以佐证这个观点* 这里,直接根据name从单实例池中获取bean,这里是从单实例池中获取,如果存在就返回,不存在就初始化* 这里的getBean()调用的就是org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean*/resource = factory.getBean(name, element.lookupType);autowiredBeanNames = Collections.singleton(name);}if (factory instanceof ConfigurableBeanFactory) {ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;for (String autowiredBeanName : autowiredBeanNames) {if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);}}}return resource;
}

所以:对于@Resource注解,就是根据在bean中设置的name,从单实例池中查找,如果找到就注入,没找到,就是null

总结

@Resource注解没什么可说的,就是根据name从单实例池中,获取能匹配上的beanDefinition

对于@Autowired注解

  1. 首先会根据type,从beanDefinitionMap中获取到所有符合该类型的beanDefinition对应的beanName
  2. 然后判断对应的@Qualifier注解,优先注入该注解指定的bean,如果在该注解指定了bean,但是没有找到,那就返回null
  3. 如果没有添加@Qualifer注解,但是找到了多个实现类,那就判断是否有添加@Primary注解,优先注入添加了该注解的bean;如果多个类添加了该注解,spring会报错,因为spring也不知道要注入哪个
  4. 如果没有添加@Primary注解,就处理@Priority注解,该注解是声明优先级的,值越小,优先级越高
  5. 如果这三个注解都没有添加,那就根据name,判断从beanDefinitionMap中查找的符合注入类型的所有beanName中,是否有和field对应的name一样的,有,就注入该bean,没有就根据@Autowired注解的required是true还是false,来决定是抛出异常还是返回null
  6. 如果找到、确定了注入的bean,就会从单实例池中获取beanName对应的完成实例化的bean对象,然后注入

最后一点,需要说明的是:@Autowired注解和@Resource注解都是通过field.set()完成属性填充的

spring源码-@Autowired、@Resource注解底层原理相关推荐

  1. 转 Spring源码剖析——核心IOC容器原理

    Spring源码剖析--核心IOC容器原理 2016年08月05日 15:06:16 阅读数:8312 标签: spring 源码 ioc 编程 bean 更多 个人分类: Java https:// ...

  2. spring源码学习之整合Mybatis原理分析

    本文主要解析spring是如何与mybatis进行整合,整合的过程中需要哪些组件的支持.以前面提到过的配置例子<spring源码学习之aop事物标签解析> 整合的过程中需要使用以下这个依赖 ...

  3. Spring源码之@Qualified注解

    @Qualified 在spring中进行依赖注入的方式有两个注解可以使用,分别是 @Resource.@Autowired 两个注解其对应的功能分别是 Resource:默认按照名称进行装配,可以通 ...

  4. Spring源码分析之 lazy-init 实现原理

    普通的bean的初始化是在容器启动初始化阶段执行的,而被lazy-init修饰的bean 则是在从容器里第一次进行context.getBean("")时进行触发.Spring 启 ...

  5. Springboot 源码分析 —— @Endpoint 注解生效原理解析

    文章目录 1 WebMvcEndpointManagementContextConfiguration 1.1 webEndpointServletHandlerMapping 1.2 Control ...

  6. 【golang源码分析】chan底层原理——附带读写用户队列的环形缓冲区

    1 环形缓冲区 1.1 环形缓冲区结构 环形缓冲区通常有一个读指针和一个写指针.读指针指向环形缓冲区中可读的数据,写指针指向环形缓冲区中可写的缓冲区.通过移动读指针和写指针就可以实现缓冲区的数据读取和 ...

  7. Spring源码深度解析(郝佳)-学习-ASM 类字节码解析

    我们在Java字节码文件结构剖析(二)中己经对MyTest35_1这个类的字节码做了完整的解析,今天,我们来看看Spring的ASM技术是如何来解析Java类字节码的.话不多说,先上实例. MyTes ...

  8. Spring源码学习(一)--Spring底层核心原理解析

    目录 Spring中是如何创建一个对象? Bean的创建过程 推断构造方法 AOP大致流程 Spring事务 最近在跟视频学习spring源码,将每节课记录下来,以后好来复习. 首先把Spring中核 ...

  9. Spring源码深度解析(郝佳)-学习-源码解析-基于注解切面解析(一)

    我们知道,使用面积对象编程(OOP) 有一些弊端,当需要为多个不具有继承关系的对象引入同一个公共的行为时,例如日志,安全检测等,我们只有在每个对象引用公共的行为,这样程序中能产生大量的重复代码,程序就 ...

  10. 读不懂Spring源码不要紧,今天从架构设计的角度先了解下底层逻辑

    前言 为什么需要Spring? 什么是Spring? 对于这样的问题,大部分人都是处于一种朦朦胧胧的状态,说的出来,但又不是完全说的出来,今天我们就以架构设计的角度尝试解开Spring的神秘面纱. 本 ...

最新文章

  1. LINUX 系统 安装Jexus 5.6和mono3.4 部署.net 环境
  2. Educational Codeforces Round 66 (Rated for Div. 2)
  3. 使用日志记录功能查看PHP扩展的执行过程
  4. //BASE64解码成File文件
  5. 服务容错、限流、资源隔离、熔断、监控…3天,撸完了!
  6. WebMvcConfigurerAdapter过时的替换方法
  7. free释放链表节点崩溃_【链表6】lt;最新gt;初识链表(link list)
  8. python语言的变量特点随时_python程序设计——基本语言特性
  9. FlashDevelop 3.0.0 Beta2 released
  10. 深入剖析引用参数Ref和Out
  11. 中国自研数据库超越Oracle登顶全球第一
  12. php cunstruct,php读取二进制流(C语言结构体struct数据文件)
  13. JMeter压力测试高并发测试
  14. 阿里 离线数据同步工具 DataX 初试
  15. 网络加密流量的相关研究
  16. hahMap的括号中指定了数字表示是什么意思
  17. Linux查看和结束进程命令详解
  18. HDU (多校) Kejin Player HDU 6656
  19. opencv曝光过度_软件开发|使用 OpenCV 进行高动态范围(HDR)成像
  20. 2022值得入手的运动装备有哪些?618超值得买合集

热门文章

  1. 算法:First Missing Positive(求缺失的第一个正整数)
  2. 上验证cudnn是否安装成功_ubuntu18.04 安装cuda、cudnn、tensorflow和pytorch其实很简单...
  3. java 装饰者模式 替代方案_如何利用装饰者模式在不改变原有对象的基础上扩展功能...
  4. 利用octave求逆矩阵
  5. 钉钉微应用怎么进入_海目星激光张荣:激光焊接在锂电池生产应用中越来越多...
  6. 多面集的表示定理 (Representation / Resolution / Caratheodory theorem of polyhedral Sets)
  7. 优先经验回放(Prioritized Experience Replay)
  8. mysql 去重命令_MySQL 命令操作
  9. css 绝对定位底部居中,css – 在另一个元素的中心下方水平居中绝对定位元素
  10. mysql 5个约束条件,Mysql入门第五课《外键约束》