今日分享开始啦,请大家多多指教~
spring aop使得我们的aop开发工作变得简单,这是众所周知的。今天我们一起揭秘spring aop底层原理及实现吧!
AOP面向切面编程:主要是通过切面类来提高代码的复用,降低业务代码的耦合性,从而提高开发效率。主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。
- AOP实现原理:aop是通过cglib的动态代理实现的。
- jdk动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
- cglib动态代理:将代理对象类的class文件加载进来,通过ASM字节码技术修改其字节码生成子类来处理。
区别:JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 。因为是继承,所以该类或方法最好不要声明成final ,final可以阻止继承和多态。
使用
要分析spring aop的底层原理,首先要会使用,先创建一个普通maven webapp项目,引入spring-context依赖,版本为5.1.1RELEASE。
然后我使用aspectj作为aop的语法实现,和spring整合起来。
接下来我全称用注解的形式来模拟spring aop的使用,先创建一个配置类去扫描包,开启aspectJ的自动代理支持。
然后新建一个接口和接口的实现类:
创建切面:
创建测试方法:
执行方法,可以看到在打印query…之前打印了before———-
这个时候我们很想知道为什么这句before会打印在query之前呢,稍微对spring aop有所了解的人应该知道,spring是通过动态代理和字节码技术来实现aop操作的,也就是经常说的jdk动态代理和cglib动态代理两种模式,那么,spring究竟是怎么创建的代理对象,又是什么时候产生的代理对象呢,下面我们来一起探究一下源码,来揭开这个谜底。
源码分析
首先我们透过现象看本质,我先把断点打在测试方法的最后一行,我们来看这个时候的dao对象。
那么接下来我们就要去找到什么时候这个dao对象变成了动态代理对象的,既然在最后一行的时候对象已经变成了代理对象,那么我门自然就猜想是在上一句代码的位置spring执行了创建代理对象的操作,我们把断点移到上一行,debug进去。
这行代码我看方法名觉得应该是有用的代码,方法意思应该是spring处理好的bean,跟进去看看。
执行完getBeanNamesForType(requiredType)后,我们看idea的变量显示,果然有一个bean,name是IndexDao。
那么接下来自然会进到length==1的那个代码块,这时候我再debug进入,这里还是一个getBean方法。
在spring容器中还有一些没有name的其他的bean需要被创建,所以这里我用上了条件断点,当beanName等于indexDao的时候,才会进入断点,但是当我F8跑完这行代码的时候,出乎意料的事情发生了。
惊不惊喜,意不意外,getSingleton这行代码执行结束之后,代理对象就已经被创建了,所以需要debug进入这行代码去看。
但是我在这里只看到了get方法,那么这些bean是什么时候放到singletonObjects里的呢,我来找找。
在
DefaultSingletonBeanRegistry注册器中,我找到了singletonObjects.put方法,代表bean是这个时候被放到这个map里去的,接下来我在这行代码上进行条件断点,然后我们来看它的调用链,找出是什么时候执行的addSingleton方法,其实从这个时候我已经知道,断点打在测试方法的倒数第二行是不对的,在getBean之前其实代理对象已经产生了。
在createBean方法上,我也加上条件断点,然后debug进入。
接下来我debug进入doCreateBean方法
debug跟进initializeBean方法,条件断点在两个初始化处理器上,我隐约觉得代理对象就是从这两个方法中产生的,我们拭目以待。
执行完
applyBeanPostProcessorsBeforeInitialization方法,这个时候我们看到warppedBean还是indexDao,并没有产生代理对象。
我猜想在下一个后置处理器中,代理对象将被创建,我debug进去。
看到这个处理器,我豁然开朗,应该就是经过这个处理器的处理产生的代理对象了,跑完这段代码来验证一下我的猜想。
可以看到我的猜想被证明是正确的,运行完这个后置处理器,代理对象就被创建出来了。 到了这里我们知道了代理对象是从哪里来的了,但是还是没搞清楚代理对象是怎么创建出来的,这时候我们就需要debug进入到这个处理器内部去瞧瞧了。
于是乎我又进到了wrapIfNecessary这个方法内部
我们看到这里有一个if语句,当config中的isOptimize和isProxyTargetClass还有
hasNoUserSuppliedProxyInterfaces三个判断条件只要有一个满足的话,spring就会选择cglib的方式进行动态代理,而config中的两个boolean变量的默认值都是false,而我们的被代理对象又是实现接口的,所以spring会选择jdk动态代理的实现形式来完成动态代理。
当然,我们也可以在这种情况下手动的配置config值来让spring选择cglib作为动态代理的实现方式,稍后会演示。
来演示一下怎么修改config让spring在有接口的情况下选择cglib作为动态代理的实现方式,其实很简单,在配置类的这个注解后加上proxyTargetClass=true就可以了。
@EnableAspectJAutoProxy(proxyTargetClass = true)
总结
我以spring aop实现的调用链图来结束这次的总结
今日份分享已结束,请大家多多包涵和指点!
2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/15381.html