问题发现:
在一次需求的过程中,发现原本没有动过的逻辑突然报了空指针的错误,导致程序执行不下去,如下
可以看到logger属性对应的值为null,但是这里为什么logger和tableService是null呢,他不应该是初始化的时候属性就会赋值进去吗?带着这个疑惑,我们先来复现一下这个问题。
复现:
由以上两张图片作为对比,能够发现,图一再调用b方法时,使用了代理,走到了target的实体,可以获取到对应的实体属性。但是在图二,能够发现,调用sayTest方法,并没有使用代理方法,而是一种简单的方法调用,而代理类自身包含的属性都时对应的null。这里稍不注意,就会导致NPE。
这里我思考了一下,如果把 @Transactional 注解注释掉,让这个属于一个正常的bean,而不是cglib,那属性还会时null值吗?
这里实验的结果表明,当这个类不是cglib代理类的时候,调用final方法是能够获取对应的属性,不会产生NPE。
这里就抛出三个问题:
1、Spring什么时候会把bean变成代理bean
2、Spring的代理类型实现方式有两种,一种是JDK的动态代理,一种是cglib的动态代理。他什么时候使用JDK代理,什么时候使用cglib代理?
3、代理类,为什么自身的对应属性是null?而不是初始化时的默认赋值?
问题一:
Spring什么时候会把bean变成代理bean?
这里debug到spring的源码发现,再creatBean的时候,会去递归的注入依赖和实例化、初始化,这时候是没有用代理类进行包装的。这里不考虑spring框架初始化时就将bean变成代理类,当然初始化时是可以重写对应的初始化的方法的,这里不做考虑。
spring在初始化bean之后调用的beanPostProcessors,如下图
不同的beanPostProcessors实现的对应不同的 postProcessAfterInitialization,返回的bean对象要决定于其具体的实现。
这里可以看到,当有AspectJ时,会有对应的postProcessor对当前的bean对象代理。
问题二:
Spring的代理类型实现方式有两种,一种是JDK的动态代理,一种是cglib的动态代理。他什么时候使用JDK代理,什么时候使用cglib代理?
是有cglib代理还是jdk代理,主要的判断逻辑就是这,如上图。
问题三:
代理类(cglib),为什么自身的对应属性是null?而不是初始化时的默认赋值?这也是这次研究的重点!!!这里仔细说说我研究的过程。
首先,能够直接很直观通过debug查看到对应的bean的信息
如上图,可以看到,自己自定义的callback中,里面的targetSource才是我们需要的目标实例,而他的最外层也包含了这些属性,值却是null。
然后我苦苦debug CGLIB的底层代码,还是没有发现创建这一步的原理代码。这时,我的同桌周日红他写了一个cglib的demo代码,并且告诉我,他写的cglib最外层的属性值不是null。首先来看看日红同学写的cglib代码
这里就很纳闷,为什么这样写最外层的属性就有对应的值,而spring动态代理出来的却没有。所有翻阅了spring的源码,发现spring源码使用cglib来生成代理类的class,而不是直接生成代理对象
这里我把代码摘出来,spring生成cglib并且实例化的代码流程如下图
spring生成cglib不是通过对应的实例来生成代理类,而是使用了createClass()来生成class,最后通过SpringObjecesis工具来生成对用的代理对象。但是为什么spring用工具初始化时,最外层属性是null呢?这里我把目光放在了SpringObjecesis工具如果初始化上了,看了SpringObjecesis源码发现