所谓的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", "原有代码");
}
}