@Autowired 到底是怎么把变量注入进来的?

在 Spring 容器中,当我们想给某一个属性注入值的时候,有多种不同的方式,例如可以通过构造器注入、可以通过 set 方法注入,也可以使用 @Autowired、@Inject、@Resource 等注解注入。,今天松哥就来和小伙伴们聊一聊,@Autowired 到底是如何把数据注入进来的。,这个问题我们就得从 Bean 的创建开始了,本文主要是和小伙伴们聊 @Autowired,所以 Bean 的创建我就不从第一步开始了,咱们直接来看关键的方法,那就是 AbstractAutowireCapableBeanFactory#doCreateBean 方法:,在这个方法中,首先会创建原始的 Bean 对象,创建出来之后,会调用一个 populateBean 方法,这个方法就是给 Bean 的各个属性赋值的方法,标注了 @Autowired 注解的属性被自动赋值也是在这个方法中完成的。,populateBean 方法内容比较多,我们来看一些关键的地方:,这里松哥贴出来的是部分关键代码。,首先来看上面有一个 if,这个 if 主要是判断是否需要后置处理器进行处理,如果不需要,那么就直接 return 掉了,默认情况下,这里并不会 return 掉,而是会继续走后面的流程,因为 postProcessAfterInstantiation 方法默认返回 true。,接下来第二个 if 就是比较关键的一个地方了,在这里会遍历所有相关的后置处理器,尝试通过这些处理器去获取到需要的 value。,负责处理 @Autowired 注解的后置处理器是 AutowiredAnnotationBeanPostProcessor,所以现在,我们就来到 AutowiredAnnotationBeanPostProcessor#postProcessProperties 方法了。,这个方法其实就两步,第一步 findAutowiringMetadata,第二步 inject,就这两件事。分别来看。,这个方法会先尝试从缓存中获取 metadata,如果能够从缓存中获取到,那就直接返回,缓存中没有的话,那么最终会调用到 buildAutowiringMetadata 方法,去重新构建 metadata,并将构建结果存入到缓存中,以备下一次使用。,那么我们来看下 metadata 到底是如何构建出来的。,这个方法比较长,我来和大家说一下核心逻辑。,首先会调用 isCandidateClass 方法判断当前类是否为一个候选类,判断的依据就是 autowiredAnnotationTypes 变量的值,这个变量在该类的构造方法中进行了初始化,大家来看下这个构造方法:,小伙伴们看到,autowiredAnnotationTypes 集合中有两个注解是固定的:@Autowired 和 @Value,另外就是如果项目引入了 JSR-330 依赖,则 @Inject 注解也会被加入进来,以前 @Inject 存在于 javax 包中,现在最新版 @Inject 注解存在于 jakarta 包中,这里把两种情况都列出来了。,所以,isCandidateClass 方法实际上就是判断当前类在类、属性、方法等层面上是否存在上述三个注解,如果存在,则就是候选类,否则就不是候选类。如果不是候选类则返回一个空的 InjectionMetadata 对象,否则就继续后面的流程。,后面的流程,首先是一个 do{}while() 结构,通过这个循环把当前类以及当前类的父类中的满足条件的注解都找出来。具体的找法就是首先调用 ReflectionUtils.doWithLocalFields 方法,这个方法会遍历当前类的所有属性,找到那些包含了 autowiredAnnotationTypes 中定义的注解的属性,并将之封装为 AutowiredFieldElement 对象,然后存入到集合中,接下来就是调用 ReflectionUtils.doWithLocalMethods,这个是找到当前类中包含了上述三个注解的方法,然后把找到的满足条件的方法封装为 AutowiredMethodElement 然后存入到集合中。,另外大家需要注意,无论是 AutowiredFieldElement 还是 AutowiredMethodElement,都是 InjectionMetadata.InjectedElement 的子类。,这就是 findAutowiringMetadata 方法所做的事情,整体上来看,就是查找到添加了 @Autowired 或者 @Value 或者 @Inject 注解的属性或者方法,并将之存入到集合中。,接下来就该调用 metadata.inject 了,我们来看下该方法:,这里就是遍历刚刚上一步收集到的 InjectedElement,然后挨个调用其 inject 方法进行属性注入。以本文一开始的 demo 为例,@Autowired 注解加在属性上面,所以我们这里实际上调用的是 AutowiredFieldElement#inject 方法:,这段代码首先会调用 resolvedCachedArgument 方法尝试从缓存中获取想要的对象,如果缓存中存在,则可以直接使用,如果缓存中没有,则调用 resolveFieldValue 方法去获取。获取到之后,通过反射调用 set 方法进行赋值就可以了。所以关键步骤又来到了 resolveFieldValue 方法中。,这个方法的核心其实就是通过 beanFactory.resolveDependency 方法获取到想要的 Bean 对象,我们直接来看这个核心方法,由于 BeanFactory 是一个接口,所以这个方法的实现实际上是在 DefaultListableBeanFactory#resolveDependency:,这里一共是四个分支,处理四种不同的情况,分别是 Optional、ObjectFactory、JSR-330 以及其他情况,很明显,文章开头的案例应该属于第四种情况,我们继续来看 doResolveDependency 方法。,这个方法也是比较长,我列出来了一些关键的部分:,首先是调用 resolveMultipleBeans 方法去查找多个 Bean,这是因为我们在注入的时候,可以注入数组、集合和 Map,例如像下面这样:,具体查找方法如下:,这里会首先判断你的数据类型,针对 Stream、数组、集合 以及 Map 分别处理,处理代码都很好懂,以集合为例,首先获取到集合中的泛型,然后调用 findAutowireCandidates 方法根据泛型去查找到 Bean,处理一下返回就行了,其他几种数据类型也都差不多。,至于 findAutowireCandidates 方法的逻辑,我们就不去细看了,我大概和小伙伴们说一下,就是先根据 Bean 的类型,调用 BeanFactoryUtils.beanNamesForTypeIncludingAncestors 方法去当前容器连同父容器中,查找到所有满足条件的 Bean,处理之后返回。,接下来回到本小节一开始的源码中,处理完集合之后,接下来也是调用 findAutowireCandidates 方法去查找满足条件的 Bean,但是这个方法查找出来的 Bean 可能有多个,如果存在多个,则要通过 @Primary 注解或者其他优先级顺序,去确定到底使用哪一个(执行 determineAutowireCandidate 方法),如果查找到一个 Bean,那就把找到的 Bean 返回即可。,这就是 @Autowired 一个完整的解析过程。,最后,结合如下时序图,我再和小伙伴们梳理一下上面的过程。,图片图片,图片图片,图片图片,整体上的流程就是这样,细节小伙伴们参考第二小节内容。,本文转载自微信公众号「江南一点雨 」,可以通过以下二维码关注。转载本文请联系江南一点雨公众号。,@Autowired 到底是怎么把变量注入进来的?,

文章版权声明

 1 原创文章作者:cmcc,如若转载,请注明出处: https://www.52hwl.com/26323.html

 2 温馨提示:软件侵权请联系469472785#qq.com(三天内删除相关链接)资源失效请留言反馈

 3 下载提示:如遇蓝奏云无法访问,请修改lanzous(把s修改成x)

 免责声明:本站为个人博客,所有软件信息均来自网络 修改版软件,加群广告提示为修改者自留,非本站信息,注意鉴别

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023年6月23日
下一篇 2023年7月15日