还是在很久以前,作过一篇 用 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) 进行许可。