还是在很久以前,作过一篇 用 AOP 来记录每个方法的执行时间(Spring 或直接 AspectJ), 其中例示了三种方法来拦截方法,用以监测方法调用时间它们分别是:
1. Spring 2.0 用 AspectJ 实现 AOP
2. Spring 通用的方法拦截
3. 直接用 AspectJ 实现
在这里再次使用 <aop:aspect-autoproxy/> 再 @Aspect 注解的方式来写个新的例子。原理与前面基本一致,只是在类里用 @Aspect, @Pointcut, @Before, @After, @Around, @AfterReturning, @AfterThrowing 来写拦截类。
局限仍然是必须通过 Spring 的 BeanFactory 获得的实例才能被拦截到,除非是在 Eclipse 里安装 AJDT 或是使用 Maven-AspectJ Plugin 来编译工程。
好,我们来看完整的例子,下面列出所有的项目文件,这是一个 Maven 的项目,所以从 pom.xml 开始。
1. pom.xml
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">     <modelVersion>4.0.0</modelVersion>     <groupId>cc.unmi</groupId>     <artifactId>TestSpring</artifactId>     <version>0.0.1-SNAPSHOT</version>     <name>TestSpring</name>     <dependencies>         <dependency>             <groupId>org.springframework</groupId>             <artifactId>spring-aspects</artifactId>             <version>3.1.2.RELEASE</version>         </dependency>         <dependency>             <groupId>aspectj</groupId>             <artifactId>aspectjrt</artifactId>             <version>1.5.4</version>         </dependency>         <dependency>             <groupId>aspectj</groupId>             <artifactId>aspectjweaver</artifactId>             <version>1.5.4</version>         </dependency>         <dependency>             <groupId>cglib</groupId>             <artifactId>cglib</artifactId>             <version>2.2</version>         </dependency>     </dependencies> </project> | 
如果这是一个 Eclipse 中的 AJDT 项目,只要 spring-aspects 一个依赖即可。
2. applicatinContext.xml
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"        xmlns:aop="http://www.springframework.org/schema/aop"        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd          http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">     <bean class="cc.unmi.testspringaspectj.MethodExecutionTime"/>     <aop:aspectj-autoproxy/>     <bean id="stockService" class="cc.unmi.testspringaspectj.StockService"/>     <bean id="fundService" class="cc.unmi.testspringaspectj.FundService"/> </beans> | 
spring 从 2.0 起就可支持 aop 标签。<aop:aspectj-autoproxy/> 的作用其实是声明了一个 AnnotationAwareAspectJAutoProxyCreator 自动代理创建器实例,它会根据 MethodExecutionTime 中声明的 @Pointcut 注解中的条件去代理符合条件的实例,这些被代理的实例必须在配置到 spring 中去。
3. MethodExecutionTime.java
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | package cc.unmi.testspringaspectj; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.util.StopWatch; /**  * @author Unmi  */ @Aspect public class MethodExecutionTime {     @Pointcut("execution(* *..StockService.getBaseInfo(..))" +             " || execution(* *..FundService.getBaseInfo(..))" +             ")")     public void methodsToBeProfiled(){}     @Around("methodsToBeProfiled()")     public Object profile(ProceedingJoinPoint pjp) throws Throwable {         StopWatch sw = new StopWatch(getClass().getSimpleName());         try {             sw.start(pjp.getSignature().toShortString());             return pjp.proceed();         } finally {             sw.stop();             System.out.println(sw.prettyPrint());         }     } } | 
这个类完全是 AspectJ 的用法范畴了。这个类必须用 @Aspect 标注,@Pointcut 注明拦截条件,支持逻辑操作,例如这里用 || 操作符配置了拦截多个方法。有关 AspectJ 的 Pointcut 语法请参考:http://www.eclipse.org/aspectj/doc/released/progguide/language.html。
如果你的 Eclipse 安装了 AJDT 插件,并且是一个 AspectJ 项目,那么我们现在的这个例子可以完全不需要 spring,即可以不需要 pom.xml 文件,可以不用 applicationContext.xml,也不用在乎你的 StockService/FundService 如何得来的。
并且你在 Eclipse 中可以看到如下景象:


4. StockService.java 和 FundService
它们都有个 public String getBaseInfo(String ticker) 方法。
5. AspectJTestClient.java
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package cc.unmi.testspringaspectj; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.support.ClassPathXmlApplicationContext; /**  * @author Unmi  */ public class AspectJTestClient {     /**      * @param args      */     public static void main(String[] args) {         BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");         StockService stockService = factory.getBean(StockService.class);         FundService fundService = factory.getBean(FundService.class);         stockService.getBaseInfo("IBM");         fundService.getBaseInfo("BBBIX");         //cannot be intercepted         new StockService().getBaseInfo("MSFT");     } } | 
这是一个测试类,验证了必须通过 Spring BeanFactory 得到的实例,调用方法时才能被拦截 。执行上面代码的输出如下:
| 1 2 3 4 5 6 7 8 9 10 11 | StopWatch 'MethodExecutionTime': running time (millis) = 512 ----------------------------------------- ms     %     Task name ----------------------------------------- 00512  100%  StockService.getBaseInfo(..) StopWatch 'MethodExecutionTime': running time (millis) = 316 ----------------------------------------- ms     %     Task name ----------------------------------------- 00316  100%  FundService.getBaseInfo(..) | 
可以看出通过 Spring BeanFactory 获得的实例被拦截了,直接初始化的实例不受影响。
但如果你的 Eclipse 安装了 AJDT 插件,并且你的项目还是一个 AspectJ 项目,那么不管你是怎么获得的 StockService/FundService 实例,在调用它们的方法时都会被 MethodExecutionTime 拦截到。并且此时在 pom.xml 中只需要保留 spring-aspects 一个依赖即可。
本文链接 https://yanbin.blog/spring-aspectj-intercept-method/, 来自 隔叶黄莺 Yanbin Blog
[版权声明]  本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。