
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接口的定义如下:

接下来,实现被代理的对象,分别用Apple类和Banana类实现这个接口,这两个类的实现如下:

以下代码是对以上两个被代理对象加的横切关注点逻辑,横切逻辑打印吃水果的时间和水果吃完的时间:

验证方式是通过从Spring IoC容器中获取Apple和Banana对象,分别调用对象的eat()方法,测试代码如下:

测试类中使用的配置文件是spring-chapter3-xmlaop.xml,具体配置如下:

执行测试代码,可以看到运行结果如图3-4所示。

图3-4 基于JDK动态代理的方式实现的Spring AOP测试结果
从图3-4执行结果可以发现,在没有修改Apple类和Banana类代码的情况下,每次执行eat()方法,都会输出“开始吃水果的时间”和“结束吃水果的时间”这样的日志记录逻辑,验证了Spring AOP是可以正常使用的。
下面将讲解基于注解方式实现Spring AOP。
还是以上例中的Fruit为例,此时横切关注点修改如下:

通过注解“@Aspect”用来声明其是切面,注解“@Before”用来表明前置通知,“@After”用来表明后置通知。

测试代码配置文件更改为spring-chapter3-annotationaop.xml。具体配置如下:

运行测试代码,其效果如图3-4所示。
3.3.2 基于CGLIB动态代理实现
3.3.1小节已经阐述了基于JDK动态代理实现的Spring AOP,可以发现,JDK动态代理的一个缺点是被代理对象必须实现一个接口。这种严苛的条件并不能满足日常开发的全部需求,毕竟Java中并不是所有的类都必须继承接口。那么有没有方法实现对没有实现接口的类进行代理呢?这就是本节将要介绍的基于CGLIB方式实现的Spring AOP编程。
下面将以XML的形式介绍基于CGLIB的方式实现的Spring AOP,这个例子中有Desk和Table两个类,分别打印各自的位置,代码如下:

下面定义一个横切关注点CglibXmlAspect,在Desk和Table两个类前后分别打印时间,以观察两个类在执行location()方法的时间,具体代码如下:

相关的Bean全部通过XML的方式进行配置,配置如下:

接下来的测试代码中,从Spring IoC容器中获取Desk和Table类的对象,分别调用对象的location()方法,测试代码如下:

运行测试代码,测试效果如图3-5所示。

图3-5 基于CGLIB方式实现的Spring AOP测试结果
下面是用注解方式演示基于CGLIB方式实现的Spring AOP。Desk和Table分别用@Component注解修饰,代码如下:

用注解配置横切关注点,“@Aspect”用来声明切面,“@Pointcut”用来定义切点,“@Before”用来设置前置通知,“@After”用来设置后置通知,具体代码如下:

配置文件修改为spring-chapter3-annotationcglibaop.xml中不再需要单独定义的Bean和切面,只需要很少的配置:

测试代码如下:

测试结果如图3-5所示。
注:以上测试代码中execution表达式各个部分含义说明:
execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
其中,除了返回类型模式、方法名模式和参数模式外,其他项都是可选的。以表达式execution(*com.test.aop.cglib.annotation.*.*(..))为例,其含义是匹配com.test.aop.cglib.annotation这个package下任意类的任意方法名、任意方法入参和任意方法返回值的这部分方法。