在前面我们使用Pointcut和Advice描述切点和增强, 并使用Advisor整合两者描述切面[email protected]使用注解来描述切点和增强.两者使用的方式不同, 但是在本质上都是一样的.
我们还是用以前的例子来举例, 学习如何使用@AspectJ来描述切点和增强.首先看一个简单的例子.
package com.bao.bao.aspectj; /** * Created by xinfengyao on 16-10-23. */ public interface Waiter { public void greetTo(String clientName); public void serveTo(String clientName); }
package com.bao.bao.aspectj; /** * Created by xinfengyao on 16-10-23. */ public class NaiveWaiter implements Waiter { @Override public void greetTo(String clientName) { System.out.println("NaiveWaiter greetTo " + clientName + "..."); } @Override public void serveTo(String clientName) { System.out.println("NaiveWaiter serveTo " + clientName + "..."); } }
下面我们使用AspectJ定义一个切面
package com.bao.bao.aspectj; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; /** * Created by xinfengyao on 16-10-23. */ @Aspect public class PreGreetingAspect { @Before("execution(* greetTo(..))") public void beforeGreeting() { System.out.println("Here are you!"); } }
我们发现, 这个类没用实现任何特殊的接口, 只是一个普通的POJO, 特殊之处是使用了@AspectJ注解.
首先在PreGreetingAspect上面标注了@Aspect的注解, 第三方程序就可以使用这个注解来判断某个类是否是一个切面;其次, 我们在beforeGreeting()方法上面标注了注解@Before, 并提供了参数execution(*greetTo(..), 这个注解提供了两个信息, @Before注解表示该增强是一个前置增强,而成员值是一个AspectJ表达式, 含义是:在目标类的greetTo()方法上织入增强, greetTo()方法可以带任意的入参和返回任意值;最后在beforeGreeting()方法中定义的就是增强逻辑, 该横切逻辑在目标方法前调用.
PreGreetingAspect通过注解, 将切点, 增强的类型和增强的逻辑糅合在一起, 是切面的定义浑然天成.PreGreetingAspect就相当于我们之前定义的BeforeAdvice, NameMatchMethodPointcut以及DefaultPointcutAdvisor联合表达的信息.
下面我们通过org.springframework.aop.aspectj.annotation.AspectJProxyFactory为NaiveWaiter生成织入PreGreetingAspect切面的代理.
package com.bao.bao.aspectj; import org.springframework.aop.aspectj.annotation.AspectJProxyFactory; /** * Created by xinfengyao on 16-10-23. */ public class AspectJProxyTest { public static void main(String[] args) { AspectJProxyFactory proxyFactory = new AspectJProxyFactory(); Waiter target = new NaiveWaiter(); proxyFactory.setTarget(target); proxyFactory.addAspect(PreGreetingAspect.class); Waiter proxy = proxyFactory.getProxy(); proxy.greetTo("tom"); proxy.serveTo("marry"); } }
运行结果:
Here are you! NaiveWaiter greetTo tom... NaiveWaiter serveTo marry...
通过输出信息我们可以看到, 代理对象的greetTo()方法已经织入了切面类所定义的横切逻辑.
虽然可以通过编程的方式织入切面, 但一般都是通过spring配置文件的方式完成织入切面的工作.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="waiter" class="com.bao.bao.aspectj.NaiveWaiter"/> <bean class="com.bao.bao.aspectj.PreGreetingAspect"/> <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/> </beans>
在前面我们介绍过两种自动代理创建器, AnnotationAwareAspectJAutoProxyCreator能够将@AspectJ注解的切面自动织入到目标bean中.
如果使用基于schema的aop命名空间进行配置就更简单了.
<bean id="waiter" class="com.bao.bao.aspectj.NaiveWaiter"/> <bean class="com.bao.bao.aspectj.PreGreetingAspect"/> <aop:aspectj-autoproxy/>
通过aop命名空间的<aop:aspectj-autoproxy/>自动为spring容器中的那些匹配@AspectJ切面的bean自动创建代理, 完成切面织入.其实spring在内部的原理还是使用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作.