通常在使用 AspectJ 时都是基于识别方法的规则来进行方法拦截,例如切片里这样写
@Pointcut
(
"execution(* *..StockService.getBaseInfo(..))")
它拦截到的是以 StockService 结尾的,方法名为 getBaseInfo,参数任意,返回值任意的方法。而我这里要说的一种方式是基于自定义注解来拦截方法的,此处的注解不是指 @Aspect, @Pointcut, 或 @Before 那一堆东西,而是指你可以自定义一个注解,如 @cc.unmi.testaspectj.MonitorMethod,被它所注解的方法即被拦截,像:
@cc.unmi.testaspectj.MonitorMethod
public void foo();
这可以给我们很大的自由度来快捷控制哪些方法需要被拦截,加个上面的注解 @MonitorMethod 即可,而不像从前那般要想像用什么规则去匹配某个方法,用 || 连接起来,同时还要防止影响到别的不期望被拦截的方法。
需要的代码并不多,四步,创建自定义注解类 MonitorMethod, 需被拦截的方法加上 @MonitorMethod,方面类,测试类。
1. MonitorMethod.java
1 2 3 4 5 6 7 8 9 10 11 12 |
package cc.unmi.testaspectj; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MonitorMethod { String value() default ""; } |
2. 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 |
package cc.unmi.testaspectj; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.util.StopWatch; /** * @author Unmi */ @Aspect public class MethodExecutionTime { @Around("execution(* *.*(..)) && @annotation(cc.unmi.testaspectj.MonitorMethod)") 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()); } } } |
上面是简单的写法,也可以把 @Pointcut 和 @Around 分开来,如先声明
@Pointcut("execution(* *.*(..)) && @annotation(cc.unmi.testaspectj.MonitorMethod)")
public void methodsToBeProfiled(){}
把用
@Around("methodsToBeProfiled()")
注解到 profile() 方法
3. StockService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package cc.unmi.testaspectj; /** * @author Unmi */ public class StockService { @MonitorMethod public String getBaseInfo(String ticker){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } return ""; } } |
4. AspectJTestClient.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package cc.unmi.testaspectj; /** * @author Unmi */ public class AspectJTestClient { public static void main(String[] args) { new StockService().getBaseInfo("MSFT"); //new FundService().getBaseInfo("BBBIX"); } } |
这样,因为 StockService 的 getBaseInfo 加上了 @MonitorMethod 注解,所以可被拦截到,假如有个类 FundService 的 getBaseInfo 方法未加上 @MonitorMethod 注解,将不被拦截到。
执行结果如下:
1 2 3 4 5 |
StopWatch 'MethodExecutionTime': running time (millis) = 500 ----------------------------------------- ms % Task name ----------------------------------------- 00500 100% StockService.getBaseInfo(..) |
如果你用 AJDT 的话,可以在 MethodExecutionTime.java 和 StockService.java 编辑器里看到箭头指示拦截到了什么方法,以及 Cross References View 中看到关联关系。
补充(2015-01-21)
因为写此文时是用的 AJDT 插件做的,所以是自动织入的,如果是命令行的话就要用到 ajc 或 aspectjtools.jar 来编译,可以直接起动 aspecttools.jar AspectJ Browser 来处理。这里以 Mac 平台命令行为例,假如项目目录结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
├── lib │ ├── aspectjrt.jar │ ├── aspectjtools.jar │ ├── aspectjweaver.jar │ └── spring-core-2.5.6.jar └── src └── cc └── unmi └── testaspectj ├── AspectJTestClient.java ├── MethodExecutionTime.java ├── MonitorMethod.java └── StockService.java |
编译:
$ javac -classpath lib/aspectjtools.jar:lib/aspectjrt.jar:lib/spring-core-2.5.6.jar org.aspectj.tools.ajc.Main -d bin -source 1.7 src/cc/unmi/testaspectj/*
执行:
$java -classpath bin:lib/aspectjrt:lib/spring-core-2.5.6.jar:lib/aspectjweaver.jar cc.unmi.testaspectj.AspectJTestClient
这样就输出上面的结果:
StopWatch 'MethodExecutionTime': running time (millis) = 505
-----------------------------------------
ms % Task name
-----------------------------------------
00505 100% StockService.getBaseInfo(..)
由于 Spring 2.0 开始支持 AspectJ,因此你可以把上面的方法应用到 Spring 中去。可参考:
1. Spring+AspectJ+ 简单方式来拦截方法,监测性能
2.用 AOP 来记录每个方法的执行时间(Spring 或直接 AspectJ),
参考:http://www.captaindebug.com/2011/09/using-springs-aspectj-support-and.html
好像在通过 Spring 使用 AspectJ 时私有方法不被拦截到,而单独用 AspectJ 不会有这样的局限。
本文链接 https://yanbin.blog/aspectj-baseon-annotation-method/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
仍然还是依赖spring,差评
不需要依赖于 Spring, 这里只是用了 Spring 的 “org.springframework.util.StopWatch”
为什么 这么main方法运行不输出 东西也不报错 呢?而且也不进去那个切面?你这个是不是要用到spring?
我真的要回顾一下了
补充了命令行下编译和执行的步骤:$ java -classpath lib/aspectjtools.jar:lib/aspectjrt.jar:lib/spring-core-2.5.6.jar org.aspectj.tools.ajc.Main -d bin -source 1.7 src/cc/unmi/testaspectj/*$java -classpath bin:lib/aspectjrt:lib/spring-core-2.5.6.jar:lib/aspectjweaver.jar cc.unmi.testaspectj.AspectJTestClient
您好 您的文章非常有帮助,我这里还有个问题,使用AJDT插件 把 aspectj项目打成jar包,想把写好的aop作为一个公共方法给别的项目使用 ,然而切面中使用了 @Around("execution(public * a.b.c..*.*(..) && @annotation(cc.unmi.testaspectj.MonitorMethod)") 也就是a.b.c这个路径是jar报所在的路径,所以给别的项目添加jar包后根本不起作用,不知道您怎么处理这个问题。
记得 AspectJ 可以把方面织入一个 jar 文件,也就是要对 jar 进行转换下。一般项目中可控部分都是自己所写的代码。
我要对之前一个web项目进行日志记录工作,按照您的方法使用AJDT写了一个aop,现在想在web中使用这个aop的jar,但是 aop所扫描的只是aop所在jar项目的路径 扫描不了web项目的路径
没过有这种需求,你是想用某个 jar 中的 aop 去对 web 项目中的 WEB-INF/classes/** 和 WEB-INF/lib/** 中的类和 jar 施加影响吗?
是的 , 用jar中的aop 对web 项目中的 WEB-INF/classes/** 和 WEB-INF/lib/** 中的类 施加影响
如果可以的话 ,这种方法可以给老的没有使用spring的web项目,仅仅使用aspectj 很方便的进行切面操作。不然老的web中没办法 直接写aspectj
单独使用 AspectJ 就是能收到这种灵活性,不要求对象实例出息于 Spring 的 BeanFactory.
哦 ,那么在web中使用aspectj写一个切面 是用 .aj 还是 .java 去做呢?
个人觉得 .aj 是个很不错的选择,更原生态,也可以用 AspectJ 的 XML 配置。
可是在java project 中运行代码是不会进 @Aspect 定义的切面的 ,只有安装了AJDT插件代码才会进去@Aspect 定义的切面中,我的问题是 怎样在java project 或者web项目中 不借助 spring使用 Aspectj 。thanks
`
那就要在打包的时候用 ajc 或 aspectjrt.jar 来编译
thank you help me
请教下通过 Spring 使用 AspectJ 时私有方法怎么样才能被拦截到?谢谢
基础的使用时,看这个 http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-introduction-spring-defn, given pointcut will be matched against public methods only!,只能拦截公有方法。
但是如果采用 http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-aj-ltw,Load-time weaving (LTW)这种方式,便何应用上直接使用 AspectJ 的功能,私有方法也能拦截到。这需要有
META-INF/aop.xml` 这个 AspectJ 配置文件,并在 Spring 配置文件中加上
这一 LTW 功能还依赖于 JDK 1.5 的 agent 特性,所以在启动 JVM 时需加上
-javaagent:$PATH/spring-instrument.jar
这样一个虚拟机参数,应用服务器也可能有相应的支持,例如对于 Tomcat 可在 context.xml 中配上: