一、基本知识
AOP基本概念
Aspect-Oriented Programming 面向切面编程的简称,Aspect是一种新的模块化机制,用来描述分散在对象、类或方法中的横切关注点(crosscutting concern),从关注点中分离出横切关注点是面向方面程序设计的核心所在。分离关注点使得解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑的代码中不再含有针对特定领域问题代码的调用,业务逻辑同特定领域问题的关系通过方面来封装、维护,这样原本分散在整个应用程序中的变动就可以很好地管理起来。
AOP和OOP的区别
面向方面编程AOP和面向对象编程OOP在字面上虽然非常类似,但却是面向不同领域的两种设计思想。
OOP针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。
而AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
AOP常用的技术以及实现
常用的AOP技术有:
- AspectJ:源代码和字节码级别的方面编织器,用户需要使用不同于Java的新语言。
- AspectWerkz:AOP框架,使用字节码动态编织器和XML配置。
- JBoss-AOP:基于拦截器和元数据的AOP框架,运行在JBoss应用服务器上。
AOP中使用的一些实现技术有:
- BCEL:Byte-Code Engineering Library,Java字节码操作类库。
- CGLIB:CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。
- Javassist:Java字节码操作类库,JBoss的一个子项目。
面向方面编程(AOP)的常用术语
- 切面Aspect: Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些切入点Pointcut 以及对切入点进行相应的操作的通知Advice。
- 连接点Joint point:表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它连接点jointpoint。
- 切入点Pointcut:表示一组连接点jointpoint,这些连接点或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的操作处理通知Advice将要发生的地方。
- 通知Advice:Advice 定义了在切入点pointcut 里面定义的程序点具体要做的操作和处理,它通过 before、after 和 around 来区别是在每个切入点之前、之后还是代替执行的代码。
- 目标对象Target:代理的目标对象,即切面要被应用到的目标对象。
- 织入Weave:指将切面应用到目标对象,并导致代理对象创建的过程。
二、Advice通知
Advice通知是AOP联盟定义的一个接口,定义当拦截到连接点做相应的处理操作,为切面增强提供织入接口。在spring AOP中,通知主要描述Spring AOP围绕方法调用而注入切面的行为,Spring AOP的通知扩展了AOP联盟的通知接口,提供了前置通知 BeforeAdvice
、后置通知 AfterReturningAdvice
、最终通知 AfterAdvice
和例外通知 ThrowsAdvice
等。
AOP联盟定义的接口都只是类似一个标记,的接口方法还是要视具体的连接点的情况而定,源码如下:
1 | public interface Advice { |
可以看到,都只有接口,而没有具体方法的定义,而真正要实现的方法都在具体的连接器指定后才有,如下所示:
1 | // 前置增强接口,使用这个前置接口需要实现一个回调函数 |
三、Pointcut切点
Pointcut切入点决定通知Advice应该作用于哪个连接点,即通过Pointcut切入点来定义目标对象中需要使用AOP增强的方法集合,这些集合的选取可以按照一定的规则来完成。
Pointcut通常意味着标识方法,这些需要增强的方法可以被某个正则表达式进行标识,或者根据指定方法名进行匹配等。下面是Pointcut设计:
Pointcut切入点源码
1 | public interface Pointcut { |
在Pointcut的基本接口定义中可以看到,需要返回一个MethodMatcher。对于Point的匹配判断功能,具体是由这个返回的MethodMatcher来完成的,也就是说,由这个MethodMatcher来判断是否需要对当前方法调用进行增强,或者是否需要对当前调用方法应用配置好Advice通知。
实现原理
在Pointcut的类继承关系中,以正则表达式切点JdkRegexpMethodPointcut的实现原理为例,来具体了解切点Pointcut的工作原理。该类完成通过正则表达式对方法名进行匹配的功能。
1 | public class JdkRegexpMethodPointcut extends AbstractRegexpMethodPointcut { |
从上面的源码分析中,我们可以看到,最简单的使用正则表达式匹配的Pointcut切入点基本功能就是根据正则表达式判断方法名等是否匹配。
四、Advisor通知器:
当完成对目标对象方法的增强行为操作 Advice 和切入点 Point 的设计开发之后,需要一个对象将目标对象、增强行为和切入点三者结合起来,通知器Advisor就是一个实现这个功能的对象,即通过Advisor通知器,可以定义那些目标对象的那些方法在什么地方使用这些增加的行为。
Advisor通知器定义
Advisor通知器的源码如下:
1 | public interface Advisor { |
实现原理
查看Advisor通知器的继承体系,发现Advisor的实现类很多,我们以最常用的DefaultPointcutAdvisor为例,分析通知器的工作原理。
DefaultPointcutAdvisor源码如下
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
30
31
32
33public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
//默认切入点 — - - ——>
//Pointcut.TRUE在切入点中的定义为:Pointcut TRUE = TruePointcut.INSTANCE;
private Pointcut pointcut = Pointcut.TRUE;
//无参构造方法,创建一个空的通知器
public DefaultPointcutAdvisor() {
}
//创建一个匹配所有方法的通知器
public DefaultPointcutAdvisor(Advice advice) {
this(Pointcut.TRUE, advice);
}
//创建一个指定切入点和通知的通知器
public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
this.pointcut = pointcut;
setAdvice(advice);
}
//为通知设置切入点
public void setPointcut(Pointcut pointcut) {
this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);
}
//获取切入点
public Pointcut getPointcut() {
return this.pointcut;
}
public String toString() {
return getClass().getName() + ": pointcut [" + getPointcut() + "]; advice [" + getAdvice() + "]";
}
}
其中默认切入点为TruePointcut,主要功能是配置默认的类过滤器和方法匹配器,即定义Spring AOP对于哪些类的哪些方法其作用,源码如下:
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
30
31
32
33
34
35
36
37
38
39class TruePointcut implements Pointcut, Serializable {
//INSTANCE是TruePointcut类的一个常量单件,即整个应用中只有这个一个,
//不会创建第二个实例对象,确保该实例对象的唯一性,单例模型
public static final TruePointcut INSTANCE = new TruePointcut();
//单态模式构造方法
private TruePointcut() {
}
//获取切入点的类过滤器
public ClassFilter getClassFilter() {
return ClassFilter.TRUE;
}
//获取切入点的方法匹配器
public MethodMatcher getMethodMatcher() {
return MethodMatcher.TRUE; // ————>
}
//获取单态模式对象的方法
private Object readResolve() {
return INSTANCE;
}
public String toString() {
return "Pointcut.TRUE";
}
}
//———————————————
public interface MethodMatcher {
boolean matches(Method method, Class<?> targetClass);
boolean isRuntime();
boolean matches(Method method, Class<?> targetClass, Object... args);
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}从TruePointcut的源码我们看到,切入点使用TrueClassFilter作为类过滤器,匹配任意的类; 使用TrueMethodMatcher作为方法匹配器,匹配任意的方法。下面我们继续分析TrueClassFilter类过滤器和TrueMethodMatcher方法匹配器。
TrueClassFilter作为默认切入点的默认类过滤器,主要告诉切入点对哪些类进行增强,源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class TrueClassFilter implements ClassFilter, Serializable {
//单态模式
public static final TrueClassFilter INSTANCE = new TrueClassFilter();
//单态模式的构造方法
private TrueClassFilter() {
}
//切入点过滤匹配类的方法,默认对所有的类都增强
public boolean matches(Class clazz) {
return true;
}
//获取单态模式对象的方法
private Object readResolve() {
return INSTANCE;
}
public String toString() {
return "ClassFilter.TRUE";
}
}
TrueMethodMatcher作为默认切入点的默认方法匹配器,主要告诉切入点对哪些方法进行增强,源码如下:
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
30class TrueMethodMatcher implements MethodMatcher, Serializable {
//单态模式
public static final TrueMethodMatcher INSTANCE = new TrueMethodMatcher();
//单态模式构造方法
private TrueMethodMatcher() {
}
//不支持运行时调用
public boolean isRuntime() {
return false;
}
//切入点匹配方法时调用,默认匹配所有的方法
public boolean matches(Method method, Class targetClass) {
return true;
}
//运行时调用将抛出异常
public boolean matches(Method method, Class targetClass, Object[] args) {
throw new UnsupportedOperationException();
}
//获取单态模式对象的方法
private Object readResolve() {
return INSTANCE;
}
public String toString() {
return "MethodMatcher.TRUE";
}
}从上面方法匹配器的源码,我们可以看出,切入点对方法进行匹配时不支持运行时的匹配,如果在运行时进行匹配将抛出异常。