0%

AspectJ基本用法

概述

AspectJ是一种编译器,它在Java编译器的基础上增加了关键字识别和编译方法。因此,AspectJ可以编译Java代码,它还提供了Aspect程序,在编译期间,将开发者编写的Aspect程序加入到目标程序中,扩展目标程序的功能。

使用AspectJ有两种方法:

  • 使用AspectJ语言,这种语言和Java几乎一样,也能在AspectJ中调用Java的任何类库,AspectJ只是多了一些关键词
  • 使用Java语言开发,然后使用@AspectJ注解

在Android平台,常用的是沪江的aspectjx,该框架基于Android Gradle Transform。

Join Points

Join Points可以看作是程序运行时的一个执行点,比如函数的调用,执行等。

Join Points 说明 示例
method call 函数调用 比如调用Log.e()
method execution 函数执行 比如Log.e()的执行内部
constructor call 构造函数调用 和method call类似
constructor execution 构造函数执行 和method execution类似
field get 获取某个变量
field set 设置某个变量
pre-initialization Object在构造函数前做的一些工作
initialization Object在构造函数中做的工作
static initialization 类初始化 比如类的static{}
handler 异常处理 比如try catch()中,对应catch内的执行

某些AspectJ框架中不是所有AspectJ支持的JPoint都有。

Pointcuts

切入点可以通过在一个方法上使用@Pointcut来指定,这个方法必须返回void,方法的参数与切入点的参数一致,方法的修饰符与切入点的修饰符一致。

一般情况下,使用@Pointcut注解的方法的方法体必须是空的,并且没有任何throws语句。如果切入点绑定了形式参数(使用args()、target()、this()、@args()、@target()、@this()、@annotation()),那么它们必须也是方法的形式参数。

1
2
3
4
5
6
@Pointcut("call(* *.*(..))")
void anyCall(){}

// 当要绑定参数的时候,只需要将参数作为被注解的方法的参数即可
@Pointcut("call(* *.*(int)) && args(i) && target(callee)")
void anyCall(int i, Fool callee){}
  • call:处理Join Point的类型,例如call、execution
  • 第一个*表示返回值,*表示返回值为任意类型
  • 第二个*.*是匹配方法,*是进行通配,几个*没区别
  • android.app.Activity.on**表示on开头的方法
  • 可以通过&&||!来进行条件组合
  • ()代表方法参数,可以指定类型,例如android.os.Bundle,或者(..)这样来代表任意类型、任意个数的参数

Advice

Before、After、AfterReturning、AfterThrowing或者Around(等效于Before和After)等。

Java原生AspectJ

安装:

  • 下载AspectJ:AspectJ
  • 运行:java -jar /Users/guangjie.peng/development/aspectj-xxx.jar
  • 将bin目录添加到环境变量,将lib目录下的jar添加到CLASSPATH

Main.java:

1
2
3
4
5
public class Main {
public static void main(String[] args) {
System.out.println("-----main-----");
}
}

MyAspect.java:

1
2
3
4
5
public aspect MyAspect {
before():execution(* *.*(..)) {
System.out.println("-------------------");
}
}

运行:

1
2
$ ajc -d *.java
$ java Main

Android平台AspectJ

根gradle脚本:

1
2
classpath 'com.android.tools.build:gradle:3.6.3'
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10'

子module:

1
2
apply plugin: 'android-aspectjx'
implementation 'org.aspectj:aspectjrt:1.9.5'

Service:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Service {
public int add(int a, int b) {
return a + b;
}

public int min(int a, int b) {
return Math.min(a, b);
}

public String hello(String s) {
return "s: " + s;
}
}

Aspect:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Aspect
public class MyAspect {

@Pointcut("execution(public * *.add(int, int)")
public void addPointcut() {
}

@Before("addPointcut()")
public void addAdvice() {
System.out.println("addPointcut");
}

@Around("execution(public * *.min(int, int)")
public Object deletePointcut(ProceedingJoinPoint joinPoint) {
System.out.println("before deletePointcut");
Object o = null;
try {
o = joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("after deletePointcut");
return o;
}
}

自定义Pointcuts

注解类:

1
2
3
4
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface DebugTrace {
}

Aspect:

1
2
3
4
5
6
7
8
9
10
11
@Aspect
public class MyAspect {

@Pointcut("execution(@*.DebugTrace * *..*.*(..))")
public void DebugTraceMethod() {}

@Before("DebugTraceMethod()")
public void beforeDebugTraceMethod(JoinPoint joinPoint) throws Throwable {
System.out.println(joinPoint.getSignature().toString());
}
}

添加注解:

1
2
3
4
5
6
public class Service {
@DebugTrace
public int add(int a, int b) {
return a + b;
}
}