通常在使用 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
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 "";
}
1
2
3
4
5
6
7
8
9
10
11
12
packagecc.unmi.testaspectj;
importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public@interfaceMonitorMethod{
Stringvalue()default"";
}
2. MethodExecutionTime.java
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());
}
}
}
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
packagecc.unmi.testaspectj;
importorg.aspectj.lang.ProceedingJoinPoint;
importorg.aspectj.lang.annotation.Around;
importorg.aspectj.lang.annotation.Aspect;
importorg.springframework.util.StopWatch;
/**
* @author Unmi
*/
@Aspect
publicclassMethodExecutionTime{
@Around("execution(* *.*(..)) && @annotation(cc.unmi.testaspectj.MonitorMethod)")
publicObjectprofile(ProceedingJoinPoint pjp)throwsThrowable{
StopWatchsw=newStopWatch(getClass().getSimpleName());
try{
sw.start(pjp.getSignature().toShortString());
returnpjp.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
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 "";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
packagecc.unmi.testaspectj;
/**
* @author Unmi
*/
publicclassStockService{
@MonitorMethod
publicStringgetBaseInfo(Stringticker){
try{
Thread.sleep(500);
}catch(InterruptedExceptione){
e.printStackTrace();
}
return"";
}
}
4. AspectJTestClient.java
package cc.unmi.testaspectj;
/**
* @author Unmi
*/
public class AspectJTestClient {
public static void main(String[] args) {
new StockService().getBaseInfo("MSFT");
//new FundService().getBaseInfo("BBBIX");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
packagecc.unmi.testaspectj;
/**
* @author Unmi
*/
publicclassAspectJTestClient{
publicstaticvoidmain(String[]args){
newStockService().getBaseInfo("MSFT");
//new FundService().getBaseInfo("BBBIX");
}
}
这样,因为 StockService 的 getBaseInfo 加上了 @MonitorMethod 注解,所以可被拦截到,假如有个类 FundService 的 getBaseInfo 方法未加上 @MonitorMethod 注解,将不被拦截到。
执行结果如下:
StopWatch 'MethodExecutionTime': running time (millis) = 500
-----------------------------------------
ms % Task name
-----------------------------------------
00500 100% StockService.getBaseInfo(..)
1
2
3
4
5
StopWatch'MethodExecutionTime':runningtime(millis)=500
-----------------------------------------
ms % Taskname
-----------------------------------------
00500 100% StockService.getBaseInfo(..)
如果你用 AJDT 的话,可以在 MethodExecutionTime.java 和 StockService.java 编辑器里看到箭头指示拦截到了什么方法,以及 Cross References View 中看到关联关系。
补充(2015-01-21)
因为写此文时是用的 AJDT 插件做的,所以是自动织入的,如果是命令行的话就要用到 ajc 或 aspectjtools.jar 来编译,可以直接起动 aspecttools.jar AspectJ Browser 来处理。这里以 Mac 平台命令行为例,假如项目目录结构如下:
├── 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
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 中去。可参考:
参考:http://www.captaindebug.com/2011/09/using-springs-aspectj-support-and.html
好像在通过 Spring 使用 AspectJ 时私有方法不被拦截到,而单独用 AspectJ 不会有这样的局限。