Android aop Advice(通知、增强)

推荐阅读系列文章

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执行结果

@Before.png

@After()

在目标方法执行之后做一些操作,比如说释放资源等。

    @After("execution(* com.zx.aop1.MainActivity.testAdvice(..))")
    public void testAfter(){
        Log.e(TAG, "testAfter: after testAdvice() method " );
    }

@After执行结果

@After.png

@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();``ProceedingJoinPointJoinPoint的子类,这里先不展开讲,后面再详细讲这2个类的作用。

@Around执行结果

Around.png

@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.png

下面对@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

ProceedingJoinPointJoinPoint的子接口,该对象只用在@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 Alexandre Vasseur
 */
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;

}

写文不易,如果本文对你有所帮助,请点个关注

你可能感兴趣的:(Android aop Advice(通知、增强))