上一章我们生动形象的讲了什么是AOP面向切面编程以及代码示例,有不明白的小伙伴可以去看看(https://blog.csdn.net/qq_32317661/article/details/82878679),承上启下,这一篇讲一下Spring AOP代理的两种方式和它的大致原理。
一、AOP代理
AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ;而动态代理则以Spring AOP为代表。
使用AspectJ的编译时增强实现AOP
之前提到,AspectJ是静态代理的增强,所谓的静态代理就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强。
举个实例的例子来说。首先我们有一个普通的Hello
类
1 2 3 4 5 6 7 8 9 10 |
|
使用AspectJ编写一个Aspect
1 2 3 4 5 6 7 |
|
这里模拟了一个事务的场景,类似于Spring的声明式事务。使用AspectJ的编译器编译
1 |
|
编译完成之后再运行这个Hello
类,可以看到以下输出
1 2 3 |
|
显然,AOP已经生效了,那么究竟AspectJ是如何在没有修改Hello类的情况下为Hello类增加新功能的呢?
查看一下编译后的Hello.class
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
可以看到,这个类比原来的Hello.java
多了一些代码,这就是AspectJ的静态代理,它会在编译阶段将Aspect织入Java字节码中, 运行的时候就是经过增强之后的AOP对象。
1 2 3 4 5 |
|
从Aspect编译后的class文件可以更明显的看出执行的逻辑。proceed
方法就是回调执行被代理类中的方法。
使用Spring AOP(重点掌握)
与AspectJ的静态代理不同,Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是在内存中临时为方法生成一个AOP对象(我们可以把它称之为‘代理对象’),这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。下面我们来依次讲解:
一、JDK动态代理
JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler
接口和Proxy
类。现在都推荐面向接口编程,我们做的项目都是各种接口+实现类,所以是不是觉得这种代理方式和现在的接口编程很符合呢!
所以一个spring项目有接口和实现类,如果不在spring配置文件中特殊配置的话(就是默认配置),默认的动态代理方式就是JDK动态代理。但是,如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。
二、CGLIB动态代理
CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final
,那么它是无法使用CGLIB做动态代理的。
根据上述,应该能了解两种动态代理的原理了吧,我再举个例子:
JDK动态代理:
目标类(实现类)可以认作为厨师张三,张三能够具体实现接口的所有功能,比如老板让张三做饭,当AOP代理的时候,会根据张三所实现的接口,再创造一个张三A(代理对象)作为张三的分身,这时候张三和张三A具有相同的接口(多态的体现),两者长得一模一样,这时候张三A就可以在张三做饭之前把菜给洗干净了,然后张三本人来做饭。。但是在老板(调用者)看来,自始至终都是张三(目标类)一个人在洗菜做饭。
但是张三(目标类)知道,在他动手做饭之前他的代理对象帮他做了一些事情,代理对象也可以在他做饭之后帮他洗碗等等。
所以目标类要是没有实现接口,程序就不能根据接口再实现一个代理对象,也就不能代替目标类(实现类)去做一些事情。
这种代理方式,只有在通过接口调用方法的时候才会有效!
CGLIB代理:
我们把目标类比作李刚(化名),代理的时候,程序会根据制造一个子类来继承目标类,那么这个子类就是代理对象(李刚的儿子),所以李刚的儿子就可以能替他爸收钱(因为他爸是李刚哈哈),因为多态,所以程序识别不出来,然后目标类再替人办事,在外人看来,就是李刚在收钱办事。但是李刚有很多特权他儿子是没权限的,也就是目标类中有final方法,子类是无法继承的,那么这个代理对象就不能代理这部分功能。
三、如何选择spring动态代理的方式
上一章讲了如何配置springAOP,在spring配置文件中,有个配置如下
<aop:aspectj-autoproxy proxy-target-class="true" />
proxy-target-class默认是false,也就是默认使用JDK动态代理,但是如果目标类没有实现接口,会自动转为CGLIB代理;
设置为true,说明使用CGLIB代理!
本次讲解完了,下一章我们讲一下AOP代理和spring事务的联系,以及很重要的同一service中不同方法调用事务失效的问题!!
地址:https://blog.csdn.net/qq_32317661/article/details/82909045