![Spring 5企业级开发实战](https://wfqqreader-1252317822.image.myqcloud.com/cover/807/26542807/b_26542807.jpg)
3.3 Spring AOP实现
3.3.1 基于JDK动态代理实现
Spring AOP的实现方式有两种,分别是基于JDK动态代理的实现和基于CGLIB的动态代理实现。本节将讲解基于JDK动态代理的方式实现Spring AOP。
基于JDK动态代理的方式实现Spring AOP有两种方式,分别是基于XML配置的方式和注解的方式,下面将先以XML配置的方式讲解。
Spring AOP的一个特点是被代理的对象需要实现一个接口。下面以一个Fruit接口为例,验证基于XML配置的Spring AOP实现,Fruit接口的定义如下:
![](https://epubservercos.yuewen.com/45D01C/15056703904176006/epubprivate/OEBPS/Images/Figure-P75_94648.jpg?sign=1738889165-ePZYpUgYrPdPUmhrUEtcpD0id0BeRnVt-0-ff9090104b36e3f42e13accdd2ec8586)
接下来,实现被代理的对象,分别用Apple类和Banana类实现这个接口,这两个类的实现如下:
![](https://epubservercos.yuewen.com/45D01C/15056703904176006/epubprivate/OEBPS/Images/Figure-P75_94649.jpg?sign=1738889165-NrR4aEO27ietBsJRbcXEIAllvHjHOKjc-0-84c3717c14ed44d90b44f045215cd95d)
以下代码是对以上两个被代理对象加的横切关注点逻辑,横切逻辑打印吃水果的时间和水果吃完的时间:
![](https://epubservercos.yuewen.com/45D01C/15056703904176006/epubprivate/OEBPS/Images/Figure-P76_94651.jpg?sign=1738889165-wE9dJlRWXMr9oMvFESPeTeyKrmWd9ElO-0-ae70a0175753f81883535f750b301d7b)
验证方式是通过从Spring IoC容器中获取Apple和Banana对象,分别调用对象的eat()方法,测试代码如下:
![](https://epubservercos.yuewen.com/45D01C/15056703904176006/epubprivate/OEBPS/Images/Figure-P77_94653.jpg?sign=1738889165-JYudnB0gUl1i5VGbAfwLO8TOEuaJ4EE6-0-466206855a66fb2ff27e5d57986ce252)
测试类中使用的配置文件是spring-chapter3-xmlaop.xml,具体配置如下:
![](https://epubservercos.yuewen.com/45D01C/15056703904176006/epubprivate/OEBPS/Images/Figure-P77_94654.jpg?sign=1738889165-ofPic4hLfYg8R2S0KPFl6vxEWvYuiGrY-0-0286322a27ecf8f3c2a3d49dbcb3b194)
执行测试代码,可以看到运行结果如图3-4所示。
![](https://epubservercos.yuewen.com/45D01C/15056703904176006/epubprivate/OEBPS/Images/Figure-P78_27678.jpg?sign=1738889165-QsAP2FaQ1InroBN6G1plUqFNyBJp09pj-0-2edfdb5f29709cef2b02d5ceb6e737a2)
图3-4 基于JDK动态代理的方式实现的Spring AOP测试结果
从图3-4执行结果可以发现,在没有修改Apple类和Banana类代码的情况下,每次执行eat()方法,都会输出“开始吃水果的时间”和“结束吃水果的时间”这样的日志记录逻辑,验证了Spring AOP是可以正常使用的。
下面将讲解基于注解方式实现Spring AOP。
还是以上例中的Fruit为例,此时横切关注点修改如下:
![](https://epubservercos.yuewen.com/45D01C/15056703904176006/epubprivate/OEBPS/Images/Figure-P78_94656.jpg?sign=1738889165-pEri34j2lrvw3MPNYVh7wpipZe2WvwPo-0-bf2d183b2dbd76b27365379dd604d6cb)
通过注解“@Aspect”用来声明其是切面,注解“@Before”用来表明前置通知,“@After”用来表明后置通知。
![](https://epubservercos.yuewen.com/45D01C/15056703904176006/epubprivate/OEBPS/Images/Figure-P79_94658.jpg?sign=1738889165-C0zWy91eIJyaXGZ8aNpE7cHz8b2w2Bx7-0-c2c09c3668823344542e4a7fcf0d7f8e)
测试代码配置文件更改为spring-chapter3-annotationaop.xml。具体配置如下:
![](https://epubservercos.yuewen.com/45D01C/15056703904176006/epubprivate/OEBPS/Images/Figure-P80_94659.jpg?sign=1738889165-wqrLIj38lJhq5JJ8zw6EnooAUsdk4FpV-0-571f739315c49bb8dddead005c10ede8)
运行测试代码,其效果如图3-4所示。
3.3.2 基于CGLIB动态代理实现
3.3.1小节已经阐述了基于JDK动态代理实现的Spring AOP,可以发现,JDK动态代理的一个缺点是被代理对象必须实现一个接口。这种严苛的条件并不能满足日常开发的全部需求,毕竟Java中并不是所有的类都必须继承接口。那么有没有方法实现对没有实现接口的类进行代理呢?这就是本节将要介绍的基于CGLIB方式实现的Spring AOP编程。
下面将以XML的形式介绍基于CGLIB的方式实现的Spring AOP,这个例子中有Desk和Table两个类,分别打印各自的位置,代码如下:
![](https://epubservercos.yuewen.com/45D01C/15056703904176006/epubprivate/OEBPS/Images/Figure-P80_94660.jpg?sign=1738889165-izjaV0CQpKPEbvJrCw2jeiaIO1tYrtIl-0-5beeb928d8818f98f09731be965c3417)
下面定义一个横切关注点CglibXmlAspect,在Desk和Table两个类前后分别打印时间,以观察两个类在执行location()方法的时间,具体代码如下:
![](https://epubservercos.yuewen.com/45D01C/15056703904176006/epubprivate/OEBPS/Images/Figure-P81_94662.jpg?sign=1738889165-rGXvxJ4m7Ri50DGDhk1vkP6AjE59e4BY-0-4c6c1c60cc79b5428fe775d964c8c1e4)
相关的Bean全部通过XML的方式进行配置,配置如下:
![](https://epubservercos.yuewen.com/45D01C/15056703904176006/epubprivate/OEBPS/Images/Figure-P81_94663.jpg?sign=1738889165-on9PtrHhLmLTTH4inO4NRIlS2b0Fg7Ll-0-f1d9068bfc182635c4cf838da12baa71)
接下来的测试代码中,从Spring IoC容器中获取Desk和Table类的对象,分别调用对象的location()方法,测试代码如下:
![](https://epubservercos.yuewen.com/45D01C/15056703904176006/epubprivate/OEBPS/Images/Figure-P82_94665.jpg?sign=1738889165-UmdqisK9Ui2dZ7uw8oRdOelVeQkaF8Id-0-1e304132fe378b69ae4316d5534a6fe4)
运行测试代码,测试效果如图3-5所示。
![](https://epubservercos.yuewen.com/45D01C/15056703904176006/epubprivate/OEBPS/Images/Figure-P82_28657.jpg?sign=1738889165-n8U9DJwjzPiYQFyNYKitVLldrydKZZLo-0-140fde1a276d3a1ae0c3a0fd71e023fc)
图3-5 基于CGLIB方式实现的Spring AOP测试结果
下面是用注解方式演示基于CGLIB方式实现的Spring AOP。Desk和Table分别用@Component注解修饰,代码如下:
![](https://epubservercos.yuewen.com/45D01C/15056703904176006/epubprivate/OEBPS/Images/Figure-P83_94666.jpg?sign=1738889165-2EOdSmRE1nrwY3WhVOrsTAD3pDmH07UX-0-4dfe230566cad64a48e70f386102095f)
用注解配置横切关注点,“@Aspect”用来声明切面,“@Pointcut”用来定义切点,“@Before”用来设置前置通知,“@After”用来设置后置通知,具体代码如下:
![](https://epubservercos.yuewen.com/45D01C/15056703904176006/epubprivate/OEBPS/Images/Figure-P83_94667.jpg?sign=1738889165-A9mANfUqmfYXZ0Dr7h8WSk83QFx6LRKq-0-1ae474200aaf574de1ba543ab5abeced)
配置文件修改为spring-chapter3-annotationcglibaop.xml中不再需要单独定义的Bean和切面,只需要很少的配置:
![](https://epubservercos.yuewen.com/45D01C/15056703904176006/epubprivate/OEBPS/Images/Figure-P84_94669.jpg?sign=1738889165-lJ7WsrBGvU86ip5lGFkMyzVM65EM8efi-0-fc114367c9ee1466e04a3e11faf2ac50)
测试代码如下:
![](https://epubservercos.yuewen.com/45D01C/15056703904176006/epubprivate/OEBPS/Images/Figure-P84_94670.jpg?sign=1738889165-Zx07GOK1iBwkpUSk1PJmNVMamUfsGMEx-0-7a8d2950a189571b7db697e3ced23b16)
测试结果如图3-5所示。
注:以上测试代码中execution表达式各个部分含义说明:
execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
其中,除了返回类型模式、方法名模式和参数模式外,其他项都是可选的。以表达式execution(*com.test.aop.cglib.annotation.*.*(..))为例,其含义是匹配com.test.aop.cglib.annotation这个package下任意类的任意方法名、任意方法入参和任意方法返回值的这部分方法。