一.AspectJ概念。
AspectJ
实际上是对AOP编程思想的一个实践,AOP虽然是一种思想,但就好像OOP中的Java一样,一些先行者也开发了一套语言来支持AOP。目前用得比较火的就是AspectJ了,它是一种几乎和Java完全一样的语言,而且完全兼容Java。当然,除了使用AspectJ特殊的语言外,AspectJ还支持原生的Java,只要加上对应的AspectJ注解就好。所以,使用AspectJ有两种方法:
- 完全使用AspectJ的语言。这语言一点也不难,和Java几乎一样,也能在AspectJ中调用Java的任何类库。AspectJ只是多了一些关键词罢了。
- 或者使用纯Java语言开发,然后使用AspectJ注解,简称@AspectJ。
二.配置Android studio支持AspectJ。
如果要在Android Studio中使用AspectJ,需要在项目的build里面进行一大堆配置,为了方便快捷,推荐使用沪江的gradle_plugin_android_aspectjx。
步骤一:
在project级别的build.gradle中添加:
buildscript {
...
dependencies {
...
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'
}
}
步骤二:
在module级别的build.gradle中添加:
apply plugin: 'android-aspectjx'
//或者这样也可以
apply plugin: 'com.hujiang.android-aspectjx'
三.AspectJ 简单使用:
步骤一:
创建一个FirstActivity ,只有基本的生命周期方法。
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onResume() {
super.onResume();
}
}
步骤二:
编写一个AspectJ类。这个类要做的事情是告诉ACJ编译器,要在MainActivity中的每个方法前面打印一行log,输出当前执行的是哪个方法,
@Aspect// 告诉ACJ编译器这是个AspectJ类
public class TraceAspect {
private static final String TAG = "TraceAspect";
@Pointcut("execution(* com.yousheng.aspectjdemo.FirstActivity.**(..))")// 注意*号后面必须有空格
public void executeAspectJ() {
}
@Before("executeAspectJ()")
public void beforeAspectJ(JoinPoint joinPoint) throws Throwable {
Log.d(TAG, "injected -> " + joinPoint.toShortString());
}
}
步骤三:
查看这段代码执行的结果
D/TraceAspect: injected -> execution(FirstActivity.onCreate(..))
D/TraceAspect: injected -> execution(FirstActivity.onStart())
D/TraceAspect: injected -> execution(FirstActivity.onResume())
分析: 通过上面例子我们可以看到,我们在FirstActivity 的onCreate(),onStart(),onResume()中并未写任何代码,但是在执行这些方法的时候却输出了Log信息。这说明我们的切面成功的进行了切入。
而且在上面引出了@Aspect
,Join Points
,@Pointcut
, @Around
这样的概念, 下面我们分别对这些进行解释。
-
@Aspect 修饰一个类,作用是把当前类标识为一个切面供容器读取,切面是切入点和通知的集合。
-
Join Points 程序运行执行点,比如上面的execution就是其中的一种类型,还有Call类型等 ,Join Points可以看做是程序运行时的一个
执行点
,比如:一个函数的调用可以看做是个Join Points,相当于代码切入点。但在AspectJ中,只有下面几种执行点是认为是Join Points
Join Points | 说明 | 实例 |
---|---|---|
method call | 函数调用 | 比如调用Log.e(),这是一个个Join Point |
method execution | 函数执行 | 比如Log.e()的执行内部,是一处Join Points。注意这里是函数内部 |
constructor call | 构造函数调用 | 和method call 类似 |
constructor execution | 构造函数执行 | 和method execution 类似 |
field get | 获取某个变量 | 比如读取DemoActivity.debug成员 |
field set | 设置某个变量 | 比如设置DemoActivity.debug成员 |
pre-initialization | Object在构造函数中做的一些工作。 | - |
initialization | Object在构造函数中做的工作。 | - |
static initialization | 类初始化 | 比如类的static{} |
handler | 异常处理 | 比如try catch 中,对应catch内的执行 |
advice execution | 这个是AspectJ 的内容 | - |
- Pointcut 选出我们需要的Join Points,是指那些通过使用一些特定的表达式过滤出来的想要切入Advice的连接点。如
@Pointcut("execution(@com.yousheng.aspectjdemo.SingleClick * *(..))")// 切点表达式
private void dataAccessOperation() {} // 切点前面的这个方法必须无返回值.
execution
指的是类型,也就是以方法执行时为切点,触发Aspect类。而execution里面的字符串是触发条件,也是具体的切点。com.yousheng.aspectjdemo.annotation.SingleClick
指的是包名+对应的类名,第一个* 指的是返回值为任意类型,第二个*指的是方法名为任意类型或者是构造器的话使用new代替,(..)指的是任意类型的参数,参数用的是正则匹配语法。
Join Points和Pointcut的区别
Join point 就是菜单上的选项,Pointcut就是你选的菜。Join point 你只是你切面中可以切的那些方法,一旦你选择了要切哪些方法,那就是Pointcut。
也就是说,所有在你程序中可以被调用的方法都是Join point. 使用Pointcut 表达式,那些匹配的方法,才叫Pointcut。所以你根本不用关心Join point。比如你有10个方法,你只想切2个方法,那么那10个方法就是Join point, 2个方法就是Pointcut。
所有的方法都可以认为是 joinpoint, 但是我们并不希望在所有的方法上都添加 Advice, 而 pointcut 的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配joinpoint, 给满足规则的 joinpoint 添加 Advice.
- Advice 就是我们插入的代码可以以何种方式插入,常见的有Before 还有 After、Around。
名称 | 描述 |
---|---|
Before | 在方法执行之前执行要插入的代码 |
After | 在方法执行之后执行要插入的代码 |
Around | 在方法前后各插入代码,他包含了 Before和 After 的全部功能 |
- Weaving 编织:主要是在编译期使用AJC将切面的代码注入到目标中, 并生成出代码混合过的.class的过程.
execution()是最常用的切点函数,其语法如下所示:
例如下面这段语法:
@Around(“execution(* ..MainActivity+.on(..))")
整个表达式可以分为五个部分:
execution()是表达式主体
第一个号代表返回类型,号代表所有的类型。
包名 表示需要拦截的包名,这里使用.代表匹配所有的包名。
第二个号表示类名,后面跟.MainActivity是指具体的类名叫MainActivity。
(..) 最后这个星号表示方法名,+.代表具体的函数名,号通配符,包括括弧号里面表示方法的参数,两个dot代表任意参数。
结合注解的例子:
第一步:
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface Transaction {
}
第二步:
@Aspect
public class TransactionAspect {
private static final String TAG = "TransactionAspect";
private static final String TRANSACTION_METHOD =
"execution(@com.goach.myaspectj.annotation.Transaction * *(..))";
@Pointcut(TRANSACTION_METHOD)
public void transactionMethod(){}
@Around("transactionMethod()")
public void addTransaction(ProceedingJoinPoint joinPoint){
Log.d(TAG,"开始事务....");
try {
joinPoint.proceed();//执行原方法
} catch (Throwable throwable) {
throwable.printStackTrace();
}
Log.d(TAG,"结束事务....");
}
}
第三步:
@Transaction//加上注解
public void hello(View view){
Log.d(TAG,"hello aspectJ");
}
执行结果 :

@Around("transactionMethod()")
public void addTransaction(ProceedingJoinPoint joinPoint){
MethodSignature methodSignature = (MethodSignature) point.getSignature();
// 拿到注解
BehaviorTrace behaviorTrace = methodSignature.getMethod().getAnnotation(BehaviorTrace.class);
// 类名
String className = methodSignature.getDeclaringType().getSimpleName();
// 方法名
String methodName = methodSignature.getName();
}
四.AspectJ主要应用场景
- 数据统计
- 日志记录
- 用户行为统计
- 应用性能统计
- 数据校验
- 行为拦截
- 无侵入的在宿主中插入一些代码,
- 做日志埋点
- 性能监控
- 动态权限控制
- 代码调试
AOP之AspectJ在Android中的应用
AndroidStudio 配置 AspectJ 环境实现AOP
Spring 之AOP AspectJ切入点语法详解(最全面、最详细。)
AOP AspectJ 字节码 示例 Hugo MD
AOP之@AspectJ技术原理详解
关于AspectJ,你需要知道的一切
网友评论