Android-AOP之AspectJ:1基础

564 阅读4分钟

所谓的AOP就是在不修改原有逻辑代码的前提下,在原宿主代码中插入一些代码来实现特定的功能,比如日志埋点,性能监控,代码调试等。它的实现过程大致如下:我们通过pointCut(切点,相当于查询条件)来找到需要操作的所有方法pointJoint(连接点,程序执行的某个特定位置,比如类的初始化之前、之后,类中某个方法调用前,调用后,方法抛出异常后等),最后通过advise(增强,织入到目标类连接点上的一段代码)来告诉程序要进行什么样的操作和怎么操作(这个步骤是放在Aspect中的)。上面所涉及的被修改的对象都叫target,整个过程叫weaving(织入,将增强添加到目标类具体连接点上的过程)。但AOP只是一个概念,是一个规范,并没有具体的实现语言。AspectJ是对AOP编程思想的实现,能够和Java配合起来使用。 AspectJ最核心的模块是他提供的ajc编译器。ajc会构建目标程序与AspectJ代码的联系,在编译期将AspectJ代码插入到被切出的pointCut中,从而达到AOP的目的。

AspectJ的注解:

  • @Aspect:定义切面类,以便能让ajc编译器识别
  • @PointCut(切点表达式):用来定义切点,标记方法
  • @Before(切点表达式):前置增强,在某连接点之前执行的增强
  • @After(切点表达式):后置增强,在某连接点之后执行的增强
  • @Around(切点表达式):环绕增强,在切点前后执行,该注解主要用于something after需要根据返回值result进行不同处理的场景,或者同时需要在方法执行前后都需要处理的场景,代码示例如下:
public Object fragmentOnCreateViewMethod(ProceedingJoinPoint joinPoint) throws Throwable {
    //before:do something
    Log.i("tag", "something before");
    //执行原有方法
    Object obj = joinPoint.proceed();
    //after:do something
    Log.i("tag", "something after");
    return obj;
}

  • AfterReturning(切点表达式):返回增强,切入点方法返回结果之后执行。
  • AfterThrowing(切点表达式):异常增强,切入点抛出异常时执行。

切点表达式:execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?) 这里的修饰符模式(public、private等)可省略,异常模式(ClassNotFoundException异常等)可省略。 具体解释如下:

@After("execution(public * *(..))")
public void testAfter(JoinPoint joinPoint)throws Throwable {
    Log.i("MainAcitity", "@After");
}

该切入点会将所有修饰符模式为public的方法匹配出来,并且在方法执行之后打印日志“@After”

@Around("execution(* on*(..))")
public void testAround(ProceedingJoinPoint joinPoint) throws Throwable {
    Log.i("MainAcitity", "something before");
    joinPoint.proceed();
    Log.i("MainActivity", "something after");
}

该切入点会匹配所有方法名以“on”开头的方法,并且会在方法执行前打印“something before”,方法执行后打印“something after”

@Before("execution(* com.mwy.aspectj.demo..*View(..))")
public void testBefore(JoinPoint joinPoint) throws Throwable {
    Log.i("MainAcitity", "@before");
}

该切入点会匹配com.mwy.aspectj.demo这个包及子包下的所有方法名以VIew结尾的方法,并在方法执行之前打印“@before”。注意这里的包名后面是"..",表示当前包及其子包

@AfterReturning("execution(String com.mwy.aspectj.demo.*(..))")
public void testAfterReturning(JoinPoint joinPoint) throws Throwable {
    Log.i("MainAcitity", "@AfterReturning");
}

该切入点会匹配com.mwy.aspectj.demo这个包下的所有返回值为String的方法,并在方法返回结果之后打印”@AfterReturning“

上面的joinpoint包含了切点方法的所有信息,具体如下:

   @Around("execution(public void *(..))")
    public void testAround(ProceedingJoinPoint joinPoint) throws Throwable {
        //方法参数值,不仅仅是ProceedingJoinPoint,是所有的JoinPoint都可以获取该参数
        Object[] args = joinPoint.getArgs();
        Log.i("tag", "args = " + Arrays.toString(args));

        long startNanoTime = System.nanoTime();
        joinPoint.proceed();
        long endNanoTime = System.nanoTime();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //方法名
        String name = signature.getName();
        Log.i("tag", "name = " + name);
        Method method = signature.getMethod();
        //这里拿到方法之后还可以进行其他操作,比如拿到注解
        //method.getAnnotation(TestAnnotation.class);

        //返回值类型
        Class returnType = signature.getReturnType();
        Log.i("tag", "returnType = " + returnType.getName());

        //方法所在类名
        Class declaringType = signature.getDeclaringType();
        Log.i("tag", "declaringType = " + declaringType.getCanonicalName());

        //参数类型
        Class[] parameterTypes = signature.getParameterTypes();
        for (Class cls : parameterTypes) {
            Log.i("tag", " cls = " + cls.getSimpleName());
        }

        //参数名
        String[] parameterNames = signature.getParameterNames();
        for (String param : parameterNames) {
            Log.i("tag", " param = " + param);
        }

        Log.i("tag", String.valueOf(endNanoTime - startNanoTime));
    }
}

这里虽然使用的是ProceedingJoinPoint,它是JoinPoint的子类,他比父类多了一个proceed方法,用于执行原来的方法。在切入点表达式中还能用到call方法,比如call("call(* on*(..))"),它与execution的区别在于call捕获joinPoint时,捕获的事签名方法的调用点,而execution捕获的是方法的执行点。再通俗点说就是call插入的代码在方法体外,execution插入的代码在方法体内。比如在onCreate的方法中调用testAOP(),如果用call方法

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //@Before(call(....))这里插入切点代码
        testAOP();
    }

    public void testAOP() {
        Log.i("MainActivity", "原有代码");
    }
}

如果用execution方法的话,插入后的代码如下

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        testAOP();
    }

    public void testAOP() {
        //@Before(execution(....))这里插入切点代码
        Log.i("MainActivity", "原有代码");
    }
}