从 spring-aop 通过getBean(.class) 获取 bean 某种条件下出错而引出对 通过指定bean 类型获取的源码细读--isTypeMatch 方法

先明确(暂不讨论懒加载等特殊):

问题现象

在使用spring-aop的时候 (配置类上面 加 @EnableAspectJAutoProxy 代表支持aop xxx)

  1. 配置类appconfg 上面 使用默认的代理模式即 jdk动态代理的策略后,在使用 ioc.get(bean)的时候
    如果通过的 类型方式获取bean 且 直接通过 子类的类型来获取容器 bean的时候,就会报错
  2. 而采用cglib即 在配置类上面开启支持aop 功能同时 设置 proxyTargetClass = true 的方式
    同样按照1.的方式去或取bean不会出错–
    (因为:即使bean是实现接口的bean,但是采用的代理方式cglib, 是直接对所代理对象即bean 基于它生产的子类 作为代理的)
    (注: 测试类bean是实现接口的bean )

1.一般我们获取bean的两种方式: getBean(“xxbeanName”) 或者 getBean(xxBean.calss)

这两种情况即 a. 一种通过beanName, b.一种是bean类型 ,
其再源码内最终都是通过 先找到 beanName 后去找到对应的 bean 的

2.即 b.种通过类型来获取其实内部是先做了转换的, --> 最终他们都是通过 getBean(String name) 来xx)

.

3. getBean(xxBean.calss)这种获取方式里面核心就是看如何通过Type 转换到 beanName

.
即主要方法 resolveNamedBean(requiredType, args); --> 它里面主要 String[] candidateNames = getBeanNamesForType(requiredType); (返回数组是考虑到一个类型是如接口可能有对应多个bean)里面如何实现,下面就是**聚焦这个转换方法:**
(0.)先从 缓存中是否能直接取到 String[] resolvedBeanNames = cache.get(type); 此缓存是专门用于xx
Map<Class<?>, String[]> allBeanNamesByType
(1.)有直接返回name 了,没取过是肯定拿不到,那么走doGetBeanNamesForType:
先得到 所有beanDefinitionNames–>目标就是从这个集合中能够用 此xxBean.class 匹配到一个所需的beanName,
(2) 循环 beanDefinitionNames 让每个beanNam 都机会去匹配 所传入的 type 的逻辑里(它还兼容处理了 FactoryBean特殊) ,即每个beanName 都会执行下面:
–> 所定义关键的 boolean matchFound ,–> 里面最!关键代码 isTypeMatch (beanName, typeToMatch) -->
从此beanName又先 getSingleton (一般能得到的其 对应bean) --> 然后 typeToMatch.isInstance(beanInstance),(即判断此beanName 所对应取得的bean 是否和 所匹配的 type 是同一类型内部:ClassUtils.isAssignable(clazz, other) – lhsType.isAssignableFrom(resolvedWrapper))
–>

如果类型同,即代表所要找的就是这个beanName,   matchFound= true; 
            并将此 beanName加入resolvedBeanNames 返回 
            注意: 这里就是本场最重要的点了!! --》 有可能出错的原因就是这个 始终再容器里的所有的bean所对应的类型 和 你传入的 bean的类型 没有一个是相同的!! 即最终返回 空,后就会直接因找不到对应 beanName 抛异常!
(3.)最后再通过此返回找到的 beanName 再次调用 getBean(BeanName) 得到此 bean

而现在的讨论的问题的引出就是这种b.通过类型 获取bean 所引发问题的思考

4. 关键方法isTypeMatch (beanName, typeToMatch)的思考

isTypeMatch (beanName, typeToMatch) 这个方法其实就是最核心的一个完成上述功能的,但是它的所作用的地方却不仅在此里,还在其他地方,如解决spring
的循环依赖中发挥了关键作用。
具体是 spring将循环依赖的关系对象都确定完成后,对其第一个所要依赖的对象属性并不是 马上 feild.set(x,y) ,而是 需要经过一次 对得到的属性对象 的一次类型检查匹配 才可以,即会调用到 isTypeMatch (beanName, typeToMatch) ,
这也是循环依赖为啥需要做检查类型的原因了!–> 因为如果依赖的候选bean是被jdk动态代理过的化,就有可能出现类似我上面的问题!!
这里面从那个 earlySingltonxxxMap 里面取出返回的对象 和 本生的对象属性类型
比对,一致后,才进行 feild.set() 操作。 — 具体的循环依赖等有空再写一篇专门分析!!

------------- 下面开始问题现象就很好解释了--------------------------------

在这里插入图片描述

二.而如果再appconfig 开启代理时候 配置了 @EnableAspectJAutoProxy(proxyTargetClass = true)就不一样了:

–>1. 因为 这样之后,就算是所代理的类是实现了接口的,那么它还是会采用 cglib 这种代理策略;
cglib 他是基于 当前这个实现类 去生产出的代理对象是 这个实现类的子类,即所生产的 proxy 和原代理 是子父 关系,即后面你去用 get(这个实现类.class ) 的时候, 再后面执行所以beanName 的匹配阶段,是可以
和 它对应的 代理类对象匹配到,(他们属于同种类型!)
CGLIB是一个代码生成类库,可以在运行时候动态是生成某个类的子类
证明:在这里插入图片描述

2.不像jdk 代理, 它所生产的 代理对象 和 实现类 是平级 关系, 匹配不到, 因为类型不一致,找不到他所想要找的 beanName.
在这里插入图片描述

注意:上述xxx, 其实都是 spring-aop的内容,虽然用到了 aspectj 的注解 配置等等,但是也相当于是只是借用了 aspectj 的技术而已!!, 因为,我们跟源码或是其他,根本没有看到有再编译阶段就 产出了 代理对象的!,即 没有通过它所提供的 ajc 编译过!!,这也是 spring-aop 和 aspectj 的区别和关系。
(只是说这样让spring 和 aspectj 产生了关系!!!,没有用到aspectj 这种代理模式!)

在这里插入图片描述

参考:https://segmentfault.com/a/1190000020621578

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值