推荐阅读系列文章
Android aop切点表达式(execution)
Android aop(AspectJ)查看新的代理类
Android AOP面向切面编程详解
防止按钮连续点击
AOP AspectJ方案有三要素:
-
@Aspect
:定义切面类; -
@Pointcut
:定义切点; -
Advice(通知/增强)
:定义一个通知方法,是处理逻辑的。
Advice(通知/增强)方法的作用是:在切点方法执行前、后,甚至是抛出异常的时候去做一些其它逻辑操作。
具体来说分为5种通知:
-
@Before()
: 切点方法执行之前执行 -
@After()
: 切点方法执行之后执行 -
@Around()
: 在切点方法执行前、后都可以做一些操作 -
@AfterReturning()
:切点方法返回结果之后执行 -
@AfterThrowing()
:异常通知,切点抛出异常时执行
@Before()
在目标方法执行之前做一些操作,比如说在Activity生命周期方法打印日志。
在MainActivity
中定义过方法
/**
* 测试5种通知/增强
*/
private void testAdvice(){
Log.e(TAG, "MainActivity testAdvice(): " );
}
在切面类中定义如下方法
@Before("execution(* com.zx.aop1.MainActivity.testAdvice(..))")
public void testBefore(){
Log.e(TAG, "testBefore: before testAdvice() method " );
}
@Before
执行结果
@After()
在目标方法执行之后做一些操作,比如说释放资源等。
@After("execution(* com.zx.aop1.MainActivity.testAdvice(..))")
public void testAfter(){
Log.e(TAG, "testAfter: after testAdvice() method " );
}
@After
执行结果
@Around()
这种应用场景很多,比如说在做xx操作之前检查权限;还有登录检查;防止按钮重复点击等。
@Around("execution(* com.zx.aop1.MainActivity.testAdvice(..))")
public void testAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//在方法执行之前做一些事情
Log.e(TAG, "testAfter: before testAdvice() method do something" );
//调用原方法
proceedingJoinPoint.proceed();
//在方法执行之后做一些事情
Log.e(TAG, "testAfter: after testAdvice() method do something" );
}
这里有点区别,因为是环绕方法,需要手动调用原来的方法,具体来说是proceedingJoinPoint.proceed();``ProceedingJoinPoint
是JoinPoint
的子类,这里先不展开讲,后面再详细讲这2个类的作用。
@Around
执行结果
@AfterReturning()
这和@After
类似,都是目标方法执行之后再做些其它操作;区别是
@AfterReturning()
可以拿到原方法的返回结果。
@AfterReturning(value = "execution(* com.zx.aop1.MainActivity.testAdvice(..))",returning = "result")
public void testAfterReturning(String result){
Log.e(TAG, "testBefore: AfterReturning testAdvice() method return: "+result );
}
/**
* 测试5种通知/增强
*/
private String testAdvice(){
Log.e(TAG, "MainActivity testAdvice() perform: " );
return "this is testAdvice() result";
}
这里多了一个returning
参数,注意,它的值必须和方法参数的名称一致,@AfterReturning
标注的方法参数的类型必须和原方法返回类型一致。
@AfterReturning
执行结果
下面对@AfterReturning()
参数做一个详细解释:
源码定义
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterReturning {
/**
* The pointcut expression where to bind the advice
*/
String value() default "";
/**
* The pointcut expression where to bind the advice, overrides "value" when specified
*/
String pointcut() default "";
/**
* The name of the argument in the advice signature to bind the returned value to
*/
String returning() default "";
/**
* When compiling without debug info, or when interpreting pointcuts at runtime,
* the names of any arguments used in the advice declaration are not available.
* Under these circumstances only, it is necessary to provide the arg names in
* the annotation - these MUST duplicate the names used in the annotated method.
* Format is a simple comma-separated list.
*/
String argNames() default "";
}
源码中定义了4个方法:
其中value()
和pointcut()
功能基本一样:都是定义advice(通知/增强)方法
中用到的切点表达式的。一样既可以是已有的切入点,也可直接定义切入点表达式。当指定了pointcut
属性值后,value属性值将会被覆盖。通常用其中一个就行了。
returning()
可以拿到目标方法的返回值。注意:它的值必须与Advice方法中定义的参数一致。returning()
的值是String类型,别误以为返回参数只能是String,返回参数可以是任意类型,这里的值只是参数的名称而已。
@AfterThrowing()
切点抛出异常时执行这个通知方法
@AfterThrowing(value = "execution(* com.zx.aop1.MainActivity.testException(..))", throwing = "exception")
public void testAfterThrowing(Exception exception) {
Log.e(TAG, "testBefore: AfterThrowing testException() method return: " + exception.toString());
// throw exception;
}
/**
* 测试异常
*/
private void testException() {
TextView textView = null;
textView.setText("hello");
Log.e(TAG, "MainActivity testException() perform: ");
}
在testException ()
方法里面会抛出一个空指针异常,然后在testAfterThrowing()
会执行。它与try catch
的方式是不太一样的,try catch
可以完全处理异常,但是@AfterThrowing
却做不到,它只能在异常抛出之后做一些其它的逻辑处理,但是处理后异常仍然会往上一级调用者传播。
另外需要注意:如果目标方法中出现异常,并由catch捕捉处理且catch又没有抛出新的异常,那么针对该目标方法的AfterThrowing增强处理将不会被执行。
结论:我目前的感觉是这个通知注解没什么用。先做个了解,后面再看看。
JoinPoint和ProceedingJoinPoint
JoinPoint
JoinPoint
其实就是封装了切点方法相关的信息,
@Before("execution1()")
public void testExecution(JoinPoint joinPoint){
Log.e(TAG, "testExecution ----: "+joinPoint.toString() );
}
JoinPoint
方法详解
public interface JoinPoint {
String toString(); //连接点相关信息
Object getThis(); //返回AOP代理对象
Object getTarget(); //返回目标对象
Object[] getArgs(); //返回切点方法参数列表
Signature getSignature(); //返回当前连接点签名
SourceLocation getSourceLocation();//返回连接点方法所在类文件中的位置
String getKind(); //连接点类型
StaticPart getStaticPart(); //返回连接点静态部分
}
ProceedingJoinPoint
ProceedingJoinPoint
是JoinPoint
的子接口,该对象只用在@Around
的切面方法中
@Around("execution(* *..MainActivity.testAOP())")
public void onActivityMethodAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
String key = proceedingJoinPoint.getSignature().toString();
Log.e(TAG, "onActivityMethodAroundFirst: before " + key + "do something");
//执行原方法
proceedingJoinPoint.proceed();
Log.e(TAG, "onActivityMethodAroundSecond: after " + key + "do something");
}
关键点在于proceed()
方法,有没有发现,这个流程不就是代理模式吗?
方法详解
/**
* ProceedingJoinPoint exposes the proceed(..) method in order to support around advice in @AJ aspects
*
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
*/
public interface ProceedingJoinPoint extends JoinPoint {
/**
* 执行目标方法
*
* @return
* @throws Throwable
*/
public Object proceed() throws Throwable;
/**
* 传入的新的参数去执行目标方法
*
* @param args
* @return
* @throws Throwable
*/
public Object proceed(Object[] args) throws Throwable;
}
写文不易,如果本文对你有所帮助,请点个关注 或 赞!