spring源码-@Autowired、@Resource注解底层原理
上篇博客说了自动注入模型的使用,这篇笔记打算记录下自动注入模型中,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、这两个注解虽然是由不同的后置处理器处理的,但是处理的思想、步骤大致都是一样的:
- 会先尝试获取bean中添加了该注解的方法(method)和属性(field)
- 找到之后,会根据实现类的优先级、或者是name等从容器中或者beanDefinitionMap中找到对应的属性
- 通过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;
}
通过这两段代码,我觉得可以说明一个问题:
- 对于@Autowired注解,spring在处理的时候:会先解析@Qualifer注解,然后解析@Primary注解,然后再解析@Priority注解
- 如果解析之后,匹配的bean还是有多个,那就根据name来查找,需要注意的是这里的查找都是基于从beanDefinitionMap中找到的符合注入条件的bean;
- 只有在确认要注入的是哪个实现类之后,才会从单实例池中获取实例化之后的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注解
- 首先会根据type,从beanDefinitionMap中获取到所有符合该类型的beanDefinition对应的beanName
- 然后判断对应的@Qualifier注解,优先注入该注解指定的bean,如果在该注解指定了bean,但是没有找到,那就返回null
- 如果没有添加@Qualifer注解,但是找到了多个实现类,那就判断是否有添加@Primary注解,优先注入添加了该注解的bean;如果多个类添加了该注解,spring会报错,因为spring也不知道要注入哪个
- 如果没有添加@Primary注解,就处理@Priority注解,该注解是声明优先级的,值越小,优先级越高
- 如果这三个注解都没有添加,那就根据name,判断从beanDefinitionMap中查找的符合注入类型的所有beanName中,是否有和field对应的name一样的,有,就注入该bean,没有就根据@Autowired注解的required是true还是false,来决定是抛出异常还是返回null
- 如果找到、确定了注入的bean,就会从单实例池中获取beanName对应的完成实例化的bean对象,然后注入
最后一点,需要说明的是:@Autowired注解和@Resource注解都是通过field.set()完成属性填充的
spring源码-@Autowired、@Resource注解底层原理相关推荐
- 转 Spring源码剖析——核心IOC容器原理
Spring源码剖析--核心IOC容器原理 2016年08月05日 15:06:16 阅读数:8312 标签: spring 源码 ioc 编程 bean 更多 个人分类: Java https:// ...
- spring源码学习之整合Mybatis原理分析
本文主要解析spring是如何与mybatis进行整合,整合的过程中需要哪些组件的支持.以前面提到过的配置例子<spring源码学习之aop事物标签解析> 整合的过程中需要使用以下这个依赖 ...
- Spring源码之@Qualified注解
@Qualified 在spring中进行依赖注入的方式有两个注解可以使用,分别是 @Resource.@Autowired 两个注解其对应的功能分别是 Resource:默认按照名称进行装配,可以通 ...
- Spring源码分析之 lazy-init 实现原理
普通的bean的初始化是在容器启动初始化阶段执行的,而被lazy-init修饰的bean 则是在从容器里第一次进行context.getBean("")时进行触发.Spring 启 ...
- Springboot 源码分析 —— @Endpoint 注解生效原理解析
文章目录 1 WebMvcEndpointManagementContextConfiguration 1.1 webEndpointServletHandlerMapping 1.2 Control ...
- 【golang源码分析】chan底层原理——附带读写用户队列的环形缓冲区
1 环形缓冲区 1.1 环形缓冲区结构 环形缓冲区通常有一个读指针和一个写指针.读指针指向环形缓冲区中可读的数据,写指针指向环形缓冲区中可写的缓冲区.通过移动读指针和写指针就可以实现缓冲区的数据读取和 ...
- Spring源码深度解析(郝佳)-学习-ASM 类字节码解析
我们在Java字节码文件结构剖析(二)中己经对MyTest35_1这个类的字节码做了完整的解析,今天,我们来看看Spring的ASM技术是如何来解析Java类字节码的.话不多说,先上实例. MyTes ...
- Spring源码学习(一)--Spring底层核心原理解析
目录 Spring中是如何创建一个对象? Bean的创建过程 推断构造方法 AOP大致流程 Spring事务 最近在跟视频学习spring源码,将每节课记录下来,以后好来复习. 首先把Spring中核 ...
- Spring源码深度解析(郝佳)-学习-源码解析-基于注解切面解析(一)
我们知道,使用面积对象编程(OOP) 有一些弊端,当需要为多个不具有继承关系的对象引入同一个公共的行为时,例如日志,安全检测等,我们只有在每个对象引用公共的行为,这样程序中能产生大量的重复代码,程序就 ...
- 读不懂Spring源码不要紧,今天从架构设计的角度先了解下底层逻辑
前言 为什么需要Spring? 什么是Spring? 对于这样的问题,大部分人都是处于一种朦朦胧胧的状态,说的出来,但又不是完全说的出来,今天我们就以架构设计的角度尝试解开Spring的神秘面纱. 本 ...
最新文章
- LINUX 系统 安装Jexus 5.6和mono3.4 部署.net 环境
- Educational Codeforces Round 66 (Rated for Div. 2)
- 使用日志记录功能查看PHP扩展的执行过程
- //BASE64解码成File文件
- 服务容错、限流、资源隔离、熔断、监控…3天,撸完了!
- WebMvcConfigurerAdapter过时的替换方法
- free释放链表节点崩溃_【链表6】lt;最新gt;初识链表(link list)
- python语言的变量特点随时_python程序设计——基本语言特性
- FlashDevelop 3.0.0 Beta2 released
- 深入剖析引用参数Ref和Out
- 中国自研数据库超越Oracle登顶全球第一
- php cunstruct,php读取二进制流(C语言结构体struct数据文件)
- JMeter压力测试高并发测试
- 阿里 离线数据同步工具 DataX 初试
- 网络加密流量的相关研究
- hahMap的括号中指定了数字表示是什么意思
- Linux查看和结束进程命令详解
- HDU (多校) Kejin Player HDU 6656
- opencv曝光过度_软件开发|使用 OpenCV 进行高动态范围(HDR)成像
- 2022值得入手的运动装备有哪些?618超值得买合集
热门文章
- 算法:First Missing Positive(求缺失的第一个正整数)
- 上验证cudnn是否安装成功_ubuntu18.04 安装cuda、cudnn、tensorflow和pytorch其实很简单...
- java 装饰者模式 替代方案_如何利用装饰者模式在不改变原有对象的基础上扩展功能...
- 利用octave求逆矩阵
- 钉钉微应用怎么进入_海目星激光张荣:激光焊接在锂电池生产应用中越来越多...
- 多面集的表示定理 (Representation / Resolution / Caratheodory theorem of polyhedral Sets)
- 优先经验回放(Prioritized Experience Replay)
- mysql 去重命令_MySQL 命令操作
- css 绝对定位底部居中,css – 在另一个元素的中心下方水平居中绝对定位元素
- mysql 5个约束条件,Mysql入门第五课《外键约束》