先明确(暂不讨论懒加载等特殊):
问题现象
在使用spring-aop的时候 (配置类上面 加 @EnableAspectJAutoProxy 代表支持aop xxx)
- 配置类appconfg 上面 使用默认的代理模式即 jdk动态代理的策略后,在使用 ioc.get(bean)的时候
如果通过的 类型方式获取bean 且 直接通过 子类的类型来获取容器 bean的时候,就会报错 - 而采用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