AOP简介

面向切面编程(AOP,Aspect-oriented programming) 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP应用场景

假设有这样一个需求,需要统计所有的点击事件并进行上报。一般情况下会在所有的点击事件中调用事件上报的方法,这使得代码之间的耦合度非常之高,将来修改事件上报的方法,所有调用该方法的地方都得修改。
AOP的目标就是解决这些问题,把这些功能集中起来进行管理。比如上面所说的点击事件上报将使用AOP统一管理。

普通例子(非AOP)

每个页面点击事件中都需要调用事件上报的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class AOPActivity extends AppCompatActivity implements View.OnClickListener {
private Button firstBtn;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aop);

firstBtn = (Button) findViewById(R.id.btn_first);
firstBtn.setOnClickListener(this);
}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_first:
//上报点击事件
EventManage.sendClickEvent();
break;
}
}

}

AspectJ

AspectJ是一套支持AOP的语言,完全兼容Java。
使用AspectJ的两种方法:

  • 完全使用AspectJ的语言
  • 使用AspectJ注解

AspectJ语法

Join Points 程序运行时的一些执行点。比如函数调用、函数执行等等。
Pointcuts 一个程序会有很多个JPoints,不是每一个JPonints都是我们所需要的,而选择感兴趣的JPonints就是Pointcuts提供的功能。

Join Points类型与之对应的Pointcuts:

Join point category Pointcut syntax
Method execution(函数执行) execution(MethodSignature)
Method call(函数调用) call(MethodSignature)
Constructor execution(构造函数执行) execution(ConstructorSignature)
Constructor call(构造函数调用) call(ConstructorSignature)
Class initialization(类初始化) staticinitialization(TypeSignature)
Filed read access(获取变量) get(FiledSignature)
Filed write access(设置变量) set(FiledSignature)
Exception handler execution(异常处理) handler(TypeSignature)
Object initialization(Object 在构造函数中做得工作) initialization(ConstructorSignature)
Object pre-initialization preinitialization(ConstructorSignature)
Advice execution adviceexecution()

Advice 选择Join Points后要做的事情:

关键词 说明
before() Join Points执行之前需要做的事情
after() Join Points执行之后需要做的事情
after():returning(返回值类型) after():throwing(异常类型) 假设JPoint是一个函数调用的话,那么函数调用执行完有两种方式退出,一个是正常的return,另外一个是抛异常。注意,after()默认包括returning和throwing两种情况
返回值类型 around() around替代原Join Points,如果要执行原JPoint的话,需要调用proceed

Android AspectJ Usage

使用AspectJ Gradle插件gradle-android-plugin-aspectjx

AOP实践(使用AspectJ注解)

选择切入的目标函数:
访问权限(public/private/protect,以及static/final)属于可选项。如果不设置它们,则默认都会选择。
返回值类型就是普通的函数的返回值类型。如果不限定类型的话,就用*通配符表示。
包名.函数名用于查找匹配的函数。可以使用通配符,包括*..以及+号。其中*号用于匹配除.号之外的任意字符,而..则表示任意子package,+号表示子类。

@Pointcut("execution(* *.onClick(android.view.View)) && args(view)")
表示Join Points类型为execution,目标函数为任意包下返回值为任意类型的名为onClick的函数,其中参数类型为View

@Around("onClickPointcut(view)")
替换原Join Points

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
26
27
28
29
@Aspect
public class UBCAspectJ {
@Pointcut("execution(* *.onClick(android.view.View)) && args(view)")
public void onClickPointcut(View view) {
}

@Pointcut("execution(* *.onItemClick(android.widget.AdapterView<?>,android.view.View,int,long)) && args(adapterView,view,position,id)")
public void onItemClickPointcut(AdapterView<?> adapterView, View view, int position, long id) {
}

@Around("onClickPointcut(view)")
public void onClick(ProceedingJoinPoint joinPoint, View view) throws Throwable {
//统一处理onClick事件
joinPoint.proceed(joinPoint.getArgs());
handlerClickEvent(joinPoint, null, view, 0);

}

@Around("onItemClickPointcut(adapterView,view,position,id)")
public void onItemClick(ProceedingJoinPoint joinPoint, AdapterView<?> adapterView, View view, int position, long id) throws Throwable {
//统一处理onItemClick事件
handlerClickEvent(joinPoint, adapterView, view, position);
joinPoint.proceed(joinPoint.getArgs());
}

public void handlerClickEvent(ProceedingJoinPoint joinPoint, AdapterView<?> adapterView, View view, int position) {
//上报点击事件
EventManage.sendClickEvent();
}

总结

使用AOP我们就不用给所有点击事件添加上报点击事件的方法,而是统一使用AOP管理。