首先说明一下,@Autowired这个注解与所谓的自动注入模式并没关系,如果你觉得有关系,那可能是从效果上来说它是实现了自动注入这个模式的效果(自动注入相对手动注入的区别就是不用手动地去配置bean之间的依赖注入关系,这点在xml配置中能够体现出来),但是从源码角度来看说@Autowired是自动注入的这是不可取的,因为通过debug我们可以发现使用了@Autowired注解的类它的注入模式是no(spring中有4种注入模式,分别为no,byName,byType,constructor,其中no为手动注入),那么这就可以说明@Autowired是属于手动注入的,其底层的原理是通过反射去给属性赋值的,而要是属于自动注入比如byType的话spring是会去找setXXX(注入的对象)方法并且去调用的,所以说@Autowired和byType或者byName完全就是不同的技术实现

@Autowired注解实现依赖注入原理

其实@Autowired实现依赖注入的原理就是使用了spring中BeanPostProcessor组件去实现的,在spring实例化好一个对象之后(此时并不能称为bean,因为还没有经历完bean的生命周期),spring会对这个对象进行初始化,这个过程就包括了对对象的属性赋值,而此时能够对对象进行插手的BeanPostProcessor就能够派上用场了。而针对处理@Autowired注解的BeanPostProcessor就是AutowiredAnnotationBeanPostProcessor,它能够找出所有加了@Autowired注解的属性或者方法,然后在容器中找出需要注入的对象通过反射进行赋值,那么找出加了@Autowired注解的属性或者方法这个过程是怎样的呢,下面就来通过源码去具体进行分析一下。

源码解析

首先我们来到整个bean的生命周期第三次调用后置处理器的地方:

org.springframework.context.support.AbstractApplicationContext#refresh
org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors

来到这个方法可以看到spring调用后置处理器的惯用套路了,拿到所有的后置处理器一个个地去遍历执行目标方法,我们这里主要看AutowiredAnnotationBeanPostProcesser的postProcessMergedBeanDefinition方法

进去findAutowiredMetadata方法

可以看到这里先判断beanName是否为空,为空的话就取class的名字作为一个map的key,而这个map就是一个缓存,buildAutowiredMetadata这句代码返回了一个InjectionMetadata对象,这个对象是什么意思?我们点进去看看

可以看到InjectionMetadata这个对象里面有Class对象属性,还有一个InjectedElement对象集合属性,而这个InjectedElement里面就是封装了Member对象,这个Member是什么?

其实它是属于jdk里面的一个接口,我们反射的Method,Field等类都继承了这个接口,所以InjectedElement这个类的作用其实就是封装了Field或者Method,那么综上所述InjectionMetadata就是封装着一个类里面对应的Filed或者Method集合。那么上面的那个InjectionMetadataCache缓存里面存的就是以该对象的beanName为key,以该对象加了@Autowired的属性或者方法封装成的InjectionMetadata为value,那么buildAutowiredMetadata这个方法应该就是找出加了@Autowired的属性或者方法的核心方法了。

 private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();Class<?> targetClass = clazz;do {final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();ReflectionUtils.doWithLocalFields(targetClass, field -> {AnnotationAttributes ann = findAutowiredAnnotation(field);if (ann != null) {if (Modifier.isStatic(field.getModifiers())) {if (logger.isInfoEnabled()) {logger.info("Autowired annotation is not supported on static fields: " + field);}return;}boolean required = determineRequiredStatus(ann);currElements.add(new AutowiredFieldElement(field, required));}});ReflectionUtils.doWithLocalMethods(targetClass, method -> {Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {return;}AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {if (Modifier.isStatic(method.getModifiers())) {if (logger.isInfoEnabled()) {logger.info("Autowired annotation is not supported on static methods: " + method);}return;}if (method.getParameterCount() == 0) {if (logger.isInfoEnabled()) {logger.info("Autowired annotation should only be used on methods with parameters: " +method);}}boolean required = determineRequiredStatus(ann);PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);currElements.add(new AutowiredMethodElement(method, required, pd));}});elements.addAll(0, currElements);targetClass = targetClass.getSuperclass();}while (targetClass != null && targetClass != Object.class);return new InjectionMetadata(clazz, elements);}

我们可以看到这段代码主要有一个ReflectionUtils.doWithLocalFields方法,我们点进去看看这个方法是干嘛的

这个方法就是根据传入的Class拿到这个Class的所有Field然后进行遍历去调用fc.doWith方法,这个doWith方法就是外面的那段lambda表达式

点进去findAutowiredAnnotation方法

这个方法很简单,就是拿到传进来的Field然后判断是否有autowiredAnnotationTypes这个集合里面包含的注解,这个集合里面只有两个注解分别是@Autowired和@Value,如果有的话就返回解析到的注解信息,没有的话就返回null

如果不返回null的话,再进行判断这个Field是否是static修饰了,如果是的话则打印日志并且并进行return不进行下面的属性注入,然后determineRequiredStatus这个方法再去判断注解上是否有required属性而且这个属性是否为true,然后把判断结果和这个Field封装成了AutowiredFieldElement对象,该类继承了上面的InjectedElement,然后把这个对象加到了currElements这个集合中。

下面同样的也是调用了ReflectionUtils.doWithLocalFields这个方法,但是传入的lambda表达式不一样,这段lambda表达式里面的逻辑是处理加了@Autowired注解的Method的,这里和上面处理差不多,就不再细说了。但是要注意的是,此时这段逻辑并没有结束,因为最外层加了一个do while循坏,而循坏的条件就是target.getSuperclass()!=null&&target.getSuperclass!=Object.class,很明显就是这个类的父类不能为空而且不能为Object类,所以执行一遍循环只是拿到了当前类加了@Autowired的Field或者Method,下一遍循环还要去找出父类加了@Autowired的Field或者Method,所以一个加入到了容器中的类其父类(不用在容器中)使用了@Autowired注解去依赖注入容器中的bean也是可以的。最后把收集到的所有符合条件的Field或者Method加入到了一个新的集合中,再把Class和这个集合封装到InjectionMetadata中并且返回出去。

该方法完成之后就会把得到的InjectionMetadata对象放到缓存中,可能有人问这个缓存到底有什么用,我们把东西存了进去,下次还会用到这个缓存吗,不然下次用不到的话这个缓存没意义呀,当然用到了,我们这里只是把符合条件的Field或者Method找出来,单单是找出来,并没有去给属性赋值,而在对属性赋值的时候就会再次从这个缓存中拿出我们找出的Field或者Method了,下一篇文章我们再来讲属性赋值的过程吧。

Spring依赖注入@Autowired原理解析(一)之寻找需要注入的属性相关推荐

  1. Spring Boot(18)---启动原理解析

    Spring Boot(18)---启动原理解析 前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会 ...

  2. Spring bean注入之注解注入-- @Autowired原理

    之前我们已经讲述过bean注入是什么了,也使用了xml的配置文件进行bean注入,这也是Spring的最原始的注入方式(xml注入). 本节课就讲注解注入. 主要讲解的注解有以下几个: @Autowi ...

  3. Spring Boot: SpringFox Swagger原理解析及简单实用

    文章目录 简介 一.Swagger简单使用 二.Swagger原理 三.Swagger架构分析及组成 其他 简介 API的全称是应用编程接口(Application Programming Inter ...

  4. 【Spring】1.核心原理解析

      目录 概况 Bean的生命周期 推断构造方法 AOP流程 事务 概况 核心知识点串讲,对Spring有整体的了解 比如: 1. Bean的生命周期原理 2. 依赖注入原理 3. 初始化原理 4. ...

  5. 细看CRLF注入***的原理和其防范措施

    细看crlf注入***的原理和其防范措施 ------------------------------------------------------------------------------- ...

  6. Spring第七弹—依赖注入之注解方式注入及编码解析@Resource原理

        注入依赖对象可以采用手工装配或自动装配,在实际应用中建议使用手工装配,因为自动装配会产生未知情况,开发人员无法预见最终的装配结果. 手工装配依赖对象  手工装配依赖对象,在这种方式中又有两种编 ...

  7. Spring源码分析(十二)autowire和@Autowired 依赖注入源码解析总结

    XML的autowire自动注入 在XML中,我们可以在定义一个Bean时去指定这个Bean的自动注入模式: byType byName constructor default no 比如: < ...

  8. spring源码分析03-spring依赖注入源码解析

    依赖注入流程图: 1. Spring中有几种依赖注入的方式? 1.1手动注入 在XML中定义Bean时,就是手动注入,因为是程序员手动给某个属性指定了值. 下面这种底层是通过set方法进行注入. &l ...

  9. Spring之依赖注入源码解析

    依赖注入底层原理流程图:Spring中Bean的依赖注入原理 | ProcessOn免费在线作图,在线流程图,在线思维导图 | 1.Spring中到底有几种依赖注入方式 手动注入和自动注入 1.手动注 ...

  10. Java程序员进阶——Spring依赖注入原理分析

    Spring依赖注入原理分析 下面谈谈Spring是如何实现反转模式IOC或依赖注入模式DI: 平时,我们需要生成一个对象,使用new语法,如一个类为A public class A{public v ...

最新文章

  1. ios 应用和电脑共享文件夹_堪比AirDrop,苹果 iPhone与Windows电脑互传文件的三种方式...
  2. linux安全体系分析与编程pdf下载,linux内核printk调试(摘录《Linux安全体系分析与编程》)...
  3. python json数据格式数组内元素递增赋值_利用Python实现JSON格式数据的编码与解码操作...
  4. Exchange 日志/存储路径
  5. owncloud搭建过程
  6. 数据预处理—4.为什么要趋近于正态分布?详解
  7. 为什么说Mac比windows更安全?
  8. LINUX下载编译libx264
  9. 网易云 计算机,网易云音乐电脑版2.7.4.198336_网易云音乐 - 系统之家
  10. MPEG4 协议详解
  11. 浏览器-Charset:Chrome浏览器升级后右键工具中的编码修改功能没有了的解决工具
  12. JavaScript编写答题评分功能页面
  13. QQ拼音截取屏幕局部放大问题解决
  14. 勒索病毒爆发波及中石油:2万座加油站断网
  15. ios python 越狱_如何使用Frida绕过iOS应用程序中的越狱检测!!!
  16. 更改微信浏览器页面标题
  17. Xcode14 build WebDriverAgent提示“Cannot link directly with dylib/framework“的解决方法
  18. ST表 详解(C语言描述)
  19. 虚拟机与虚拟主机 区别
  20. 《UE4游戏开发》之 《画质控制》

热门文章

  1. android ram压力测试,android用memtester内存压力测试
  2. 2021-08-30
  3. 592. 分数加减运算
  4. 382.链表随机节点
  5. C语言不支持函数重载的原因
  6. mysql多线程复制binlog_MySQL 不同复制模式下,如何忽略binlog事件
  7. 分布式检测与数据融合:贝叶斯检测理论
  8. 线性判别分析LDA解析2
  9. 刷题记录 kuangbin带你飞专题四:最短路练习
  10. cas 4.0 mysql 验证_在Ja-sig CAS中使用MySQL数据库进行身份验证