Spring Bean 依赖注入(下)

2024-03-22 1092阅读

温馨提示:这篇文章已超过367天没有更新,请注意相关的内容是否还可用!

Spring Bean 依赖注入(下)

不管是在属性上面还是在方法上面加了@Autowired注解都是根据属性的类型和名字去找bean,

set方法多个形参也是这么找

拿到所有的属性和方法的注入点后,会去调用Inject()方法

Spring Bean 依赖注入(下)

遍历每一个注入点然后进行注入

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

字段的注入

一开始肯定是没有缓存的,直接进到resolveFieldValue()方法,传入当前字段,当前bean对象,bean名字。

Spring Bean 依赖注入(下)

这里需要构造一个对象。【依赖描述】

👆🏻当前属性,和@Autowired注解required的值

Spring Bean 依赖注入(下)

根据传进来的对象(字段信息)去bean工厂找bean【核心方法】

Spring Bean 依赖注入(下)

方法的注入

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

方法里有几个参数就会有几个依赖描述器

Spring Bean 依赖注入(下)

遍历方法里的参数

Spring Bean 依赖注入(下)

方法的参数也构造一个依赖描述器

然后调用resolveDependency()【核心方法】去找到一个bean

Spring Bean 依赖注入(下)

然后再把bean放到arguments集合里(每个形参对应的bean对象都会放到这个数组里)

Spring Bean 依赖注入(下)

最终返回数组,然后执行方法

Spring Bean 依赖注入(下)

核心方法

不管传进来是什么最重要的就两个信息

字段的类型/字段的名字

方法的类型/方法的名字

Spring Bean 依赖注入(下)

初始化参数名字发现器

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

================================================================================

在JDK1.7之前 想要拿到方法参数的名字是比较难的 没有现成的api能用

只能拿到类型

Spring Bean 依赖注入(下)

1.8之后就能拿到参数名字 不过不是我们想要的形参名字"abc"【底层基于字节码 本地变量表】

Spring Bean 依赖注入(下)

再加个配置就可以了

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

=================================================================================

主流程

如果当前的字段类型或者方法参数的类型是Optional/ObjectFactory/ObjectProvider的话单独处理

Spring Bean 依赖注入(下)

如果是一般的类 就会进到else的逻辑里去

Spring Bean 依赖注入(下)

判断属性或者方法参数前有没有加上@Lazy注解

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

有@Lazy注解的话

调用buildLazyResolutionProxy()方法

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

与spring AOP有关 最后返回代理对象

Spring Bean 依赖注入(下)

如果这么写spring一开始给属性赋值是OrderService的代理对象

Spring Bean 依赖注入(下)

假如我要使用orderService的a()方法的时候它会是代理对象执行a()方法

Spring Bean 依赖注入(下)

首先其实会执行这段代码

Spring Bean 依赖注入(下)

只有当使用代理对象的a()方法的时候,也就是真正使用代理对象的时候才会根据字段信息/方法参数信息去bean工厂(容器里)去找到bean对象,再去执行对应的a()方法。这就是@Lazy注解的效果,只有当真正用到这个对象的时候才会去找对应的bean去调用方法。

Spring Bean 依赖注入(下)

如果加在方法参数上,spring一开始也是找的代理对象传给这个参数,只有在真正执行某个方法的时候才会去找bean

Spring Bean 依赖注入(下)

没有@Lazy注解的话

就会返回null,然后调用

doResolveDependency()方法【核心方法 找bean】

Spring Bean 依赖注入(下)

shortcut和缓存有关系

Spring Bean 依赖注入(下)

先把类型找出来,可能是字段的类型也可能是方法参数的类型

Spring Bean 依赖注入(下)

接下来的代码处理@Value注解

拿到@Value注解的值

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

情况1:如果@Value注解拿到的是String

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

需要先把spring的配置文件要提前引入

Spring Bean 依赖注入(下)

占位符填充底层其实是拿zhouyu这个key去Environment中找对应value【-D虚拟机配置优先级高于配置文件】

把Environment对象拿出来去匹配key

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

环境变量中没有找到的话就会把它当做字符串

Spring Bean 依赖注入(下)

解析spring表达式

Spring Bean 依赖注入(下)

#:会去spring容器里找zhouyu这个bean

Spring Bean 依赖注入(下)

如果spring容器里有zhouyu这个bean就不会报错了

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

需要类型转换器 把String类型转成OrderService类型 有的话转化成功直接return

没有就抛异常

Spring Bean 依赖注入(下)

如果没写@Value注解

只有@Autowired注解

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

假如我现在这么定义,然后我的容器里有两个不同名字的OrderService的bean

一个叫zhouyu,一个叫orderService

Spring Bean 依赖注入(下)

打印正常,能找到2个bean

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

如果属性的类型本来就就是个集合,spring就会把OrderService这个类型的所有bean找出来赋值给属性

Spring Bean 依赖注入(下)

如果是个Map,会把Map的key的类型找出来,如果key的类型不是String就返回null

Spring Bean 依赖注入(下)

强制规定

Spring Bean 依赖注入(下)

同样还会找value的类型

Spring Bean 依赖注入(下)

findAutowireCandidates()方法【核心】

根据@Autowired注解传的某个类型去找bean,返回bean的名字和对应符合这个类型的bean对象

Spring Bean 依赖注入(下)

candidateNames会返回bean的名字,也就是那些找到的匹配的那些类型

Spring Bean 依赖注入(下)

先在自己的bean工厂里面根据类型去找bean的名字,

如果还有父bean工厂还会去父bean工厂会根据类型去找bean的名字

Spring Bean 依赖注入(下)

然后把两个集合合并返回

Spring Bean 依赖注入(下)

会把所有的单例bean都找出来

某个类型对应的bean的名字

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

1.首先遍历BeanDefinition,根据BeanName拿到BeanDefinition

这段代码的allowEagerInit基本都是true 比较复杂先跳过不说

Spring Bean 依赖注入(下)

如果不是FactoryBean就会进到isTypeMatch()方法来

Spring Bean 依赖注入(下)

不管是不是FactoryBean都会进入到isTypeMatch()方法里,

同时这个方法还兼容了传的名字有没有带"&"符号

Spring Bean 依赖注入(下)

看name和typeToMatch的类型是否匹配

Spring Bean 依赖注入(下)

如果传的名字带“&”符号会先干掉(先跳过这里不说)

Spring Bean 依赖注入(下)

传进来beanName先看单例池有没有,如果不为空也就是不为NullBean

Spring Bean 依赖注入(下)

接下来就会判断是不是FactoryBean

Spring Bean 依赖注入(下)

在定义FactoryBean的时候不仅要实现getObject还要实现getObjectType()。

所以万一我要判断这个FactoryBean是什么类型,我不可能调用getObject去生成对象

所以我直接调用getObjectType就知道是什么类型

Spring Bean 依赖注入(下)

所以如果是FactoryBean我就直接调用getObjectType()方法就行

Spring Bean 依赖注入(下)

如果是普通bean

直接调用isInstance()方法去匹配,单例池里的对象是不是我这个类型

Spring Bean 依赖注入(下)

如果遍历的beanName在单例池还没有这个对象

拿到对应的BeanDefinition

Spring Bean 依赖注入(下)

如果beanClass还没被加载会先加载(简而言之 拿到BeanDefinition就能判断和当前的类型是否匹配了)

Spring Bean 依赖注入(下)

找到以后就会返回有哪些bean的名字是和这个类型匹配的

Spring Bean 依赖注入(下)

接下来这个map是和spring启动有关的

spring启动就会往这个map里存东西

这个map存的是 某个类型 你对应的bean是什么

Spring Bean 依赖注入(下)

---------------------------------------------------------------------------------

spring启动

左边类型的那个bean对象是右边那个

Spring Bean 依赖注入(下)

---------------------------------------------------------------------------------

如果有一些是直接存到这个map里面来的,也要去遍历

Spring Bean 依赖注入(下)

如果map里面也有我现在要的类型,就会把bean对象加到我的集合里面来

然后还要去生成bean的名字,因为这个map里面的对象是没有beanName的

Spring Bean 依赖注入(下)

接下来继续筛选

Spring Bean 依赖注入(下)

---------

小例子

自己注入自己 那到底会注入谁?

Spring Bean 依赖注入(下)

【BeanNameAware 是一个标记型接口,它让Bean可以感知到自己在Spring容器中的名称。当一个Bean实现了 BeanNameAware 接口,Spring容器在创建该Bean的过程中会自动调用其 setBeanName 方法,传入该Bean的名称】

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

结果:

注入的却是userService1。

---------------------------------------------------------------------------------

在依赖注入的时候是先考虑的是别人而不是自己

不是自己才会执行addCandidateEntry()的逻辑【暂时不会先用自己】

Spring Bean 依赖注入(下)

假如匹配成功了最后就会加到result返回

Spring Bean 依赖注入(下)

假如我有3个,另外两个不是自己的都不符合

就会进入到下面的result的isEmpty的逻辑

Spring Bean 依赖注入(下)

就会把自己加到result里面来。

也就是其他的bean不能用才会用自己。

Spring Bean 依赖注入(下)

isAutowireCandidate()

先配置这么个属性

autowireCandidate = true,当前这个bean可不可以用来依赖注入

Spring Bean 依赖注入(下)

核心都是调用的这个方法

拿到BeanDefinition,然后去判断这个属性是true还是false,true才能依赖注入。

Spring Bean 依赖注入(下)

其实这里用到了责任链模式

会先判断父类是true还是false,如果父类是false就直接返回false【一个和泛型相关,一个和Qualifier相关】

Spring Bean 依赖注入(下)

泛型相关:

只有父类判断出可以依赖注入才会调用

checkGeneric方法来判断可不可以依赖注入

Spring Bean 依赖注入(下)

例子

当前定义了2个泛型,而且没把这个类定义为bean

Spring Bean 依赖注入(下)

创建StockService 定义成为一个bean

Spring Bean 依赖注入(下)

继承BaseService,指定两个泛型

Spring Bean 依赖注入(下)

OrderService只留一个bean

Spring Bean 依赖注入(下)

问题:

UserService需要依赖注入吗?

答:会,它会去找父类的注入点,加了@Autowired注解的属性

Spring Bean 依赖注入(下)

但是这里是泛型,那Spring怎么知道这个类是什么呢?

对应的字段是o但是解析出来的类型是Object

所以一开始会把所有的bean都找出来

Spring Bean 依赖注入(下)

找到一大堆

Spring Bean 依赖注入(下)

然后就来到了解决泛型的这个类

在这里进行筛选的

也就是当前每个bean都回来判断是不是可以要进行依赖注入

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

也就是要基于UserService 这个o才是OrderService

可以通过API来确定你的泛型是什么

Spring Bean 依赖注入(下)

可以拿到UserService你继承的父类长什么样子

Spring Bean 依赖注入(下)

再通过这个API可以拿到o和s,也就是你的父类的泛型是怎么写的

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

然后就能知道O对应的是OrderService,S对应的是StockService

所以这个方法就是

判断当前某个bean的类型和我当前属性的类型是否匹配,就算是泛型通过API就能知道真正的类型是什么

Spring Bean 依赖注入(下)

其实当前依赖注入是有6个判断

第一个:autowireCandidate属性是否为true【是否需要依赖注入】

Spring Bean 依赖注入(下)

第二个:是不是泛型

Spring会检查泛型类型,以确保依赖注入时泛型参数的匹配。比如,如果你有一个 List 的依赖,Spring会试图注入一个带有 String 类型的列表。

第三个:

  1. 限定符注解: 除了 @Primary 外,其他诸如 @Qualifier 注解可以用来指定注入特定的Bean。@Qualifier 注解可以和特定的Bean名称或自定义限定符一起使用。

第四个:Primary

第五个:优先级

第六个:beanName

Qualifier例子:

Spring Bean 依赖注入(下)

属性注入写个a

Spring Bean 依赖注入(下)

所以真正注入的Bean就是哪个限定符上写了"a"的那个bean

使用例子:

自定义注解Random写了个限定符random

Spring Bean 依赖注入(下)

再写一个roundRobin的注解

Spring Bean 依赖注入(下)

再写一个负载均衡的接口

Spring Bean 依赖注入(下)

两个不同的实现类

1.

Spring Bean 依赖注入(下)

2.

Spring Bean 依赖注入(下)

效果演示:

通过注解就能很灵活的切换你想用的负载均衡策略是哪个

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

原理:

其实不管加哪个注解,就相当于在实现类上加了@Qualifier注解

Spring Bean 依赖注入(下)

相当于这样的效果

先根据类型找到bean,然后再根据限定符找到对应的bean

Spring Bean 依赖注入(下)

当前属性上的注解和我当前遍历到的BeanDefinition的Qualifier注解内容是否相同

相同就表示匹配

Spring Bean 依赖注入(下)

找到后返回

Spring Bean 依赖注入(下)

如果是个集合也会调用这个方法,把所有OrderService的bean找出来

Spring Bean 依赖注入(下)

也是存成一个map,把Map的Vaules拿出来作为result返回

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

如果是泛型呢?所有的bean都会导入吗?

Spring Bean 依赖注入(下)

并不会 拿到的是空 null,并没有把Object拿出来

Spring Bean 依赖注入(下)

改成Object才会是所有bean

Spring Bean 依赖注入(下)

如果既不是map也不是List,就会拿当前属性的类型去找bean

不会进这里 进了也没用 这个是找集合的

Spring Bean 依赖注入(下)

会进到这个方法里

Spring Bean 依赖注入(下)

找到bean对象后

key就是beanName,value可能是bean对象也有可能是beanClass。

如果找到是空的且require是true就会抛异常

Spring Bean 依赖注入(下)

假设找到了多个bean

调用determineAutowireCandidate()方法进行过滤

Spring Bean 依赖注入(下)

当前bean的名字和属性的名字或者和方法参数的名字是否相同,相同就返回

Spring Bean 依赖注入(下)

但在名字之前还有其他的判断

=================================================================================

Spring Bean 依赖注入(下)

找到了多个bean

会先遍历判断某个bean上面有没有@Primary注解

Spring Bean 依赖注入(下)

对应BeanDefinition的isPrimary属性是不是true,是的话直接返回,多个bean加了@Primary注解就报错。

Spring Bean 依赖注入(下)

如果没加@Primary注解,就会去找优先级最高的bean

但是这里用的不是@Order注解,而是@Priority,但这个注解只能加在类的上面

Spring Bean 依赖注入(下)

数字越小优先级越高,就算没有orderService123也能正确返回bean,如果配置一样就抛异常

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

================================================================================

此时我只会需要1个bean对象,上面的按优先级找到以后

其他的bean对象用不到的就只会存beanClass,就没必要把bean加载出来

Spring Bean 依赖注入(下)

回到一开始,这里的Object就会有可能是存的beanClass了

Spring Bean 依赖注入(下)

如果找到的那个bean就是要注入的那个bean在这里就会把bean对象实例化

Spring Bean 依赖注入(下)

如果不是class那就直接返回

Spring Bean 依赖注入(下)

单例池里的value不会有null的情况,会用一个叫做NullBean的对象来代替,然后require为true

这里就会抛异常。【spring防止出问题 就搞了个NullBean】

Spring Bean 依赖注入(下)

假设只找到了1个bean

就会把map里唯一的元素拿出来

Spring Bean 依赖注入(下)

如果拿到的value是class

Spring Bean 依赖注入(下)

最后

都只会找到一个要注入的bean,

然后构造一个shortcutDependencyDescriptor对象(依赖描述器的子类)作为缓存。

缓存的是字段的信息,字段的类型和bean的名字。

Spring Bean 依赖注入(下)

如果第二次来注入,直接把缓存好的对象传到方法里

Spring Bean 依赖注入(下)

直接拿缓存好的beanName去得到bean对象

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

例子1

这里会用到缓存不?

不会,这里其实有2个注入点,其次字段和方法实现的对象都不是同一个cache标志,所以触发不了缓存。

Spring Bean 依赖注入(下)

例子2

如果把原型bean改成多例(单例的场景基本用不到 不会一个字段注入多次的)

Spring Bean 依赖注入(下)

这里调用2次就会创建2次,因为是多例

但是基于的类UserService和字段都是同一个,所以这里就会用到缓存

Spring Bean 依赖注入(下)

那为什么缓存的是beanName而不缓存bean对象呢?

如果OrderService也是原型bean呢?

那他们两个UserService得到的OrderService属性应该是不同的,如果把bean对象缓存起来,那第二次来的时候就会找错了,注入的就是同一个OrderService了。

Spring Bean 依赖注入(下)

@Resource注解【指定name也就是beanName和type】

Spring Bean 依赖注入(下)

这个注解是由CommonAnnotationBeanPostProcessor这个类来处理的

Spring Bean 依赖注入(下)

找注入点【加了@Resource的方法和属性就是注入点】,遍历正在创建的那个bean对象的时候,如果字段是static就会抛异常,如果是@Autowired注解不会抛异常,直接跳过这个注入点

最终构造出ResourceElement对象【根据字段或者方法的Member对象生成的,构造时会看有没有指定name和type 如果没指定name就用属性名或者set方法后面的名字 也就是注入点的那个beanName 如果写了type会检查和方法参数类型 或 属性的类型是否匹配】

同时构造注入点的时候还会去判断有没有lazy注解

Spring Bean 依赖注入(下)

找到注入点后调用inject方法进行注入

Spring Bean 依赖注入(下)

如果找到多个注入点,遍历调用inject方法注入,调用的其实是父类的父类【InjectElement】的inject方法,

@Autowired的Inject方法是在他们自己的类里实现的

Spring Bean 依赖注入(下)

如果lazy是true,直接构造个代理对象 赋值、注入给那个属性

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

核心方法就if-else

如果没指定name的值,我就会用属性名去看bean工厂有没有bean

如果没有就会调用resolveDependency()方法

会根据属性的信息(根据类型先去找)

所以@Resource会先找name再找type

fallbackToDefaultTypeMatch()======>>>>>当根据名字去bean工厂找不到bean失败的时候,进行类型的匹配

Spring Bean 依赖注入(下)

如果自己指定了名字 isDefaultName就不是true 也进入else逻辑里

如果bean工厂有这个bean就会进入到else的逻辑里

Spring Bean 依赖注入(下)

直接拿名字去getBean就完事了,如果拿不到那就报错

Spring Bean 依赖注入(下)

@Resource注解并不是Spring提供的而是java规范层面提供的

总结

@Value下面还有一步,根据类型把所有的beanName找出来

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

Spring Bean 依赖注入(下)

VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]