这几天在研究aspectj的使用方法,所以,顺便写个博客总结下。
一、不带参数的常规写法:
1.具体的逻辑处理类:
@Aspect //必须使用@AspectJ标注,这样class DemoAspect就等同于 aspect DemoAspect了 public class DemoAspect { static final String TAG = "DemoAspect"; /* @Pointcut:pointcut是一个注解,这个注解是针对一个函数的,getTimeout() 其实它代表了这个pointcut的名字。Pointcut后面跟的是该注解被调用的限制条件,比如此处限制为是该MainActivity下以get开头的方法。 */ @Pointcut("execution(* com.victor.aspectj.activity.MainActivity.get*(..))") public void getTimeout(){}; //注意,这个函数必须要有实现,否则Java编译器会报错 /* @Before:这就是Before的advice,对于after,after -returning,和after-throwing。对于的注解格式为 @After,@AfterReturning,@AfterThrowing。Before后面跟的是pointcut名字,然后其代码块由一个函数来实现。 */ @Before("getTimeout()") public void test(JoinPoint joinPoint){ Log.e(TAG, joinPoint.toShortString()); } }
2,Annotation类:
@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) public @interface DemoTrace {}
3.使用方式
@DemoTrace @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); }
二、需要带参数的写法
1.首先,申明annotation时就必须加入方法:
//第一个@Target表示这个注解只能给函数使用 //第二个@Retention表示注解内容需要包含的Class字节码里,属于运行时需要的。 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface CheckLoginAnnotation {//@interface用于定义一个注解。 public boolean isLogin(); // isLogin是一个函数,其实代表了注解里的参数,在需要的地方需要调用该方法获取参数 }
2.在对应的AspectJ中的potincut中:
@Pointcut("execution(@com.victor.aspectj.CheckLoginAnnotation * *(..)) && @annotation(ann)") public void checkPermssion(CheckLoginAnnotation ann){};@annotation(ann)其实就是限制该注解需要传入一个参数
3.具体参数的使用
/* 接下来是advice,advice的真正功能由check函数来实现,这个check函数第二个参数就是我们想要 的注解。在实际运行过程中,AspectJ会把这个信息从JPoint中提出出来并传递给check函数。 */ @After("checkPermssion(checkLoginAnnotation)") public void check(JoinPoint joinPoint, CheckLoginAnnotation checkLoginAnnotation){ MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); String className = methodSignature.getDeclaringType().getSimpleName(); String methodName = methodSignature.getName(); Log.e(TAG, "className = " + className + " methodName = " + methodName); boolean isLogin = checkLoginAnnotation.isLogin(); Log.e(TAG, "Is logined " + isLogin); return; }
最后是通过调用checkLoginAnnotation.isLogin()方法来获取参数的。
另外,通过joinPoint.getArgs();方法可以获取到被注解方法的参数;
4.调用方式
@CheckLoginAnnotation(isLogin = true) public boolean setContent() { return true; }
三、advice的类型
关键词 | 说明
| 示例 |
before() | before advice | 表示在JPoint执行之前,需要干的事情 |
after() | after advice | 表示JPoint自己执行完了后,需要干的事情。 |
after():returning(返回值类型) after():throwing(异常类型) | returning和throwing后面都可以指定具体的类型,如果不指定的话则匹配的时候不限定类型 | 假设JPoint是一个函数调用的话,那么函数调用执行完有两种方式退出,一个是正常的return,另外一个是抛异常。 注意,after()默认包括returning和throwing两种情况 |
返回值类型 around() | before和around是指JPoint执行前或执行后备触发,而around就替代了原JPoint | around是替代了原JPoint,如果要执行原JPoint的话,需要调用proceed |
before和after都很好理解,传参的话按照上面的步骤来就可以了,但是我好像发现在传参的时候,只能传入常量,这个就有点蛋疼了,可能还需要继续挖掘才能发现怎么传变量吧,如果你知道了可以告诉我哦。
主要想说的是关于around()的使用,这个获取会用的相对多些。
四、around() advice类型注解的使用
1.获取方法中返回的参数
@Around("execution(@org.android10.gintonic.annotation.DebugTrace * *(..))") public Object getMethodParams(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); String className = methodSignature.getDeclaringType().getSimpleName(); String methodName = methodSignature.getName(); DebugLog.log("className", "className = " + className + " methodName = " + methodName); Object result = joinPoint.proceed(); Log.e("result", "" + result); return result; }
调用方法:
@DebugTrace public int getTestParams() { return 123; }
最后打印出来的结果是:123。
2.覆盖原方法
有时候我们我们不需要调用原来的方法时,只要注释掉joinPoint.proceed();方法即可。
// Object result = joinPoint.proceed();