DomBro Studio

Spring核心AOP

2018/04/21

Spring的核心 —— AOP

前言

最近在撸《Spring技术内幕》 这本书(很赞的一本 Spring 源码级别的书),用作者的话来说 AOP 和 IOC 属于 Spring的核心 部分。但是要明确的是,无论是 IoC 还是 AOP 都只是 Spring 给出众多功能的一种,并不能说 Spring 是单纯的 IoC 或 AOP 框架。今天要说的 AOP 从底层介绍 Spring AOP 在底层是如何基本实现的,看过后会发现 Spring 的作者们真的是太牛逼了。我可能不会按照作者的思路来,通过这本书加上我的理解解开 Spring AOP 那 性感 神秘的面纱。然后,墙裂建议看这篇笔记的人一定不要错过最后的总结。

AOP

啥是 AOP

在进入 Spring AOP 之前,你得知道 AOP 这玩意儿 不是 Spring 的专利。人家自己是一个专有名词 —— Aspect Oriented Programming 即面向切面编程。md,不是要面向对象编程吗?咋又面向切面编程了?啥叫切面? 没错,这两个问题是我在开始学 Spring 的时候的问题。

  • 面向切面 & AOP 要做什么

先抛出一个个人结论(注意个人这两个字): 面向切面,就是面向方法。 为什么这么说?得从 AOP 要达到的目的说起。假设有一个场景,你对数据表进行 CRUD 操作,当然这四个操作在不同的方法中。你希望将记录下每次 CRUD 的执行过程及结果,并将其放入日志文件中。

1
2
3
4
5
6
7
8
/****代码清单 1 向表中插入记录并记录到日志的伪码***/
public void createStudent(Student student){
//1.在数据表中插入 student 这条记录
insert(student);
//2.将操作结果放入日志中
intoLog();
}
仅以插入为例子,RUD 操作省略...

如代码清单1中,将操作结果记录到日志并不是 createStudent 这个方法业务范围,也就是说 intoLog 与业务无关,上述代码太不规范耦合性较大。自然而然想到将 intoLog 操作抽离出来,在每次执行 createStudent() 方法之后,在调用 intoLog 方法,这样就做到了解耦又使代码整洁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/****代码清单 2 将 记录到日志操作抽离出来***/
public void createStudent(Student student){
insert(student);
}

//记录到日志的方法
public viod intoLog(){
...
}

//该方法表示上层的调用
public void doService(Student stu){
StudentService service = new StudentServiceImp();
LogService log = new LogService();
//在插入后记录到日志中去
service.createStudent(stu);
log.intoLog();

}

乍一看这好像也没啥问题,可是这只是一个添加操作,还是要手动的把操作记录到日志中去。如果有更多对标的操作,那岂不是每个操作之后都要手动调用记录日志的方法? 嫌麻烦是人类进步的阶梯, 要是有一种方式可以达到 调动插入方法 后就自动将结果记录到日志中的效果,岂不快哉? 换句话说,关注点在 方法 上,所要达到的效果也是建立在方法上。这是面向方法编程

  • 对一些名词的解释

至于为什么要叫面向切面编程,小孩没娘,说来话长。我们知道 Java 方法的调用到执行记录在 Java 运行时数据区的虚拟机栈中(具体来说是栈帧),可以抽象的把方法的调用看成是一长串的线性结构。所以当对虚拟机栈中某个方法(无论执行前还是执行后)做一些操作,就好像在这个线性结构上的某个节点(栈帧)横着切了过去!这就是面向切面编程这个名字的由来,至于这个方法我们叫他横切关注点又叫目标方法,对这个方法做的操作我们叫他对方法的切面增强。持有这个方法的对象我们叫他目标对象。

AOP 实现的核心动态代理

上面我们知道了 AOP 要做的是对某个横切关注点的增强(翻译成中文就是对目标对象的某方法执行前后做一些操作),要强调的是我们不是增强可不一定是在方法执行后,也可以是方法执行前,甚至是在方法出异常的情况下都可以对目标对象的方法增强。下面来说一下 AOP 的实现。

  • AOP 的实现

那么 AOP 要怎样实现呢?在调用目标方法前后要完成对该方法的增强,很难不让人想到代理模式的动态代理。说白了动态代理不就是在 invoke 方法中对 被代理对象的代理方法进行一个回调吗?还是以记录日志为例子,只要在代理对象的 invoke 方法回调目标方法之后记录至日志就好啦!

  • Java动态代理

复习一下 Java 动态代理,毕竟这是 AOP 的核心。Java 代理又分为静态代理和动态代理,两者的思路都是在代理对象的方法中回调被代理对象的方法,这里就不讲静态代理了。动态代理的代理类需要实现 InvocationHandler 的 invoke 方法 。而被代理类则要实现一个接口,这样代理类对象就不必知道代理对象方法的具体实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/***代码清单3 一个动态代理类***/
public class MyProxy implements InvocationHandler {

Object target;

public Object blind(Object object){
//给出被代理对象实例
target = object;
//返回一个代理类对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//每次调用被代理对象的方法,都会调用 代理对象的 invoke 方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnResult = method.invoke(target,args);
return returnResult;
}
}
  • Java动态代理 实现的 AOP

在代码清单3 的动态代理类的 invoke 方法中,我们可以在执行目标方法前后插入对目标方法的增强。还是以 记录日志为例子:

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
/***代码清单4 动态代理类实现 AOP ***/
public class CreateStudentProxy implements InvocationHandler {

Object target;

public Object blind(Object object){

target = object;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//每次调用被代理对象的方法,都会调用 代理对象的 invoke 方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnResult = method.invoke(target,args);
//在这里调用 intoLog 方法,实现后置增强
intoLog();
return returnResult;
}
}

//我们假设 createStudent 方法是 StudentServiceImp 实现的 StudentService 方法。


public static void main(String[] args) {
StudentServiceImp serviceImp = new StudentServiceImp();
CreateStudentProxy proxy= new CreateStudentProxy();
StudentService service = (StudentService)proxy.blind(serviceImp);
//这会执行 代理类 中的 invoke 方法
service.createStudent(student);
}

通过代码清单4 这是一个很简单的例子,我们大概其了解了 AOP 增强的原理,就是通过代理对象的 invoke 方法中的回调。其实这个原理还是很好懂得,现在可以揣测到 Spring 也一定是用到代理技术实现 AOP 的。 或许你会说,这也没看见代码量少了啊,反而多写了好多代码。图样图森破!使用代理实现面向切面编程充分做到了解耦,不必担心方法的增强写在方法中这种不规范的写法,也不必每次都手动调用增强方法,只要使用代理的接口调用目标方法就可以了,充分解决了提出的问题! 如果真的要挑毛病,也只能是动态代理使用反射会慢一点点。

Spring AOP

简单了解了 AOP 原理,终于来到了 Spring AOP 。看过这部分,你会发现。哇!写出 Spring AOP 的人,太牛逼了!怎么会有如此精巧的设计方式,简直到了到了代码和设计艺术水平。我一辈子都写不出来这种代码。

通知 切点 通知器

通知 Advice 、切点 PointCut 、通知器 Advisor 他们既是名词又是 Spring AOP 的三个重要对象。决定了 AOP 切入什么、怎样切入 和 在哪切入 的问题。一起来看一下。

通知 Advice

那么通知是干啥的呢?通知定义了在连接点中做些什么,为 切面增强织 入提供接口。(连接点就是我们上面说的方法也即横切关注点。Spring 中的一些名词可能会不太一致,但我们不在这里纠结) 也就是说 Advice 定义了切入内容以及怎样切入。

注:要说明一点的是 Advice 不是 Spring 中定义的接口,而是 AOP 联盟定义的。不明觉厉。当然这里也只当个了解就行了。

  • 切入什么 & 如何切入

从 Advice 的定义,可以看出我们可以在 Advice 中定义切面的增强。例如,可以把上面举的记录日志操作定义到 Advice 中。 至于如何定义到 Advice 这就涉及到了 如何切入的问题。

1
2
3
/***代码清单5 Advice 接口****/
public interface Advice {
}

我们看到 Advice 接口空空如也,啥都没有。没关系 Spring 通过扩展 Advice ,给出了更多的通知类型,供使用者选择。比如我们熟悉的前置通知,后置通知,环绕通知,异常通知… 底层原理都是实现了这些具体的通知的方法而已。

图 1 Advice 类继承关系

1
2
3
4
5
6
7
8
9
10
/***代码清单6 常见的几种具体通知***/
//前置通知
public interface MethodBeforeAdvice extends BeforeAdvice {
//我们看到 Method 参数,就是连接点
void before(Method var1, Object[] var2, Object var3) throws Throwable;
}
//后置通知
public interface AfterReturningAdvice extends AfterAdvice {
void afterReturning(Object var1, Method var2, Object[] var3, Object var4) throws Throwable;
}

可以根据通知的名称和方法名 就可以得出 如何切入 这个问题,例如 MethodBeforeAdvice 中的 before 方法,就是以前置通知的方式切入,至于切入什么据要看你在 before 中定义的操作了。

切点(关注点) Pointcut

Pointcut 决定了 Advice 通知应该作用于那个连接点,也就是作用于哪个方法。也就是 在哪切入这个问题。注意的是,我在刚开始的时候以为 Pointcut 只可能是一个方法,其实不然。Pointcut 定义的是需要增强方法的集合,这些集合的选取按照一定规则生成。例如,通过正则表达式或者编程式声明 切入点的时候我们有时候会让一个 通知 增强不止一个 方法。

1
2
3
4
5
6
7
8
/***代码清单7 Pointcut 切点****/
public interface Pointcut {
Pointcut TRUE = TruePointcut.INSTANCE;

ClassFilter getClassFilter();

MethodMatcher getMethodMatcher();
}

我们看到 pointcut 需要返回一个 MethodMather 对象。 Pointcut 就是通过这个 MethodMather 来判断当前连接点是否匹配的。也就是是说有这个 MethodMather 来判断是否要对当前调用的方法应用配置好的 Advice 通知。
Pointcut 也有很多子类伙子接口,比较常见的是使用 正则表达式 切点 JdkRegexpMethodPointcut,该切点通过匹配正则表达式来匹配连接点; 以及 NameMatchMethodPointcut ,他通过 切点的方法

通知器 Advisor

事实上,我们通过 通知 和 切点 这这俩 在表面上看起来已经 AOP 的必要条件就已经满足了。那么就需要另一个对象 通知器 Advisor 将这二者结合起来。 通过这个通知器,可以定义应该使用那个通知并在那个关注点使用它。体现了 Spring 良好的封装性。Advisor 为 使用 IoC 容器配置 AOP 应用,提供了便利( 设置 bean property 就可以了)。
Advisor 是一个接口,默认的 Advisor 是 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
/***代码清单8 默认的 Advisor ***/
public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
private Pointcut pointcut;
//注意,这里的关注点用单例模式获取
public DefaultPointcutAdvisor() {
this.pointcut = Pointcut.TRUE;
}

public DefaultPointcutAdvisor(Advice advice) {
this(Pointcut.TRUE, advice);
}
//初始化 Pointcut 和 Advice
public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
this.pointcut = Pointcut.TRUE;
this.pointcut = pointcut;
this.setAdvice(advice);
}

public void setPointcut(Pointcut pointcut) {
this.pointcut = pointcut != null ? pointcut : Pointcut.TRUE;
}

public Pointcut getPointcut() {
return this.pointcut;
}

public String toString() {
return this.getClass().getName() + ": pointcut [" + this.getPointcut() + "]; advice [" + this.getAdvice() + "]";
}
}

Spring AOP 设计与实现

现在我们知道了 Spring 是通过 Advisor 通知器 对关注点进行切面增强的。然而知道这个还远远不够,Advisor 只是给了一个大概的轮廓。Spring AOP 要做到的效果是调用目标对象的关注点方法,就会将对应的增强效果显示出来 ,显然,为了解决这个问题 Spring AOP 同样是靠动态代理实现的。这里可以猜测一下下:

猜测 1 : 由于 Advisor 可以知道关注点和通知,那么 Spring AOP 的 代理类的 代理方法一定会使用到 Advisor 。

Spring AOP 的设计分析

在介绍 AOP 的时候介绍到实现 AOP 的核心是动态代理,即通过 代理类 实现切面的织入。同样 Spring 也是如此,Spring 的核心技术就是 JDK动态代理。

以动态代理技术为基础,设计出一系列 AOP 的横切实现,比如 前置通知、返回通知,异常通知等。同时 Spring 还提供一些列的 Pointcut 来匹配切入点。 ——《Spring技术内幕》

使用 Spring AOP 是一件简单的事,只需要配置相关的 Bean 定义即可。然而为了让 Spring AOP 起作用,需要完成一系列的步骤。比如 为目标对象建立 代理,启动代理对象的拦截器完成横切面(增强切入点) 的织入。

Spring AOP 的 重点研究对象 ProxyFactoryBean
  • Spring AOP 的应用类

Spring AOP 为我们准备了三个 AOP 的应用类,分别是 AspectJProxyFactory、ProxyFactoryBean、ProxyFactory 。对于使用 Spring AOP 的应用,以上三个类都提供了对 AOP 功能的封装。区别在于 AspectJProxyFactory 集成了 Spring 和 AspectJ 的作用;ProxyFactoryBean 可以在 IoC 容器中完成声明式配置 AOP 功能,而使用 ProxyFactory 则需要编程式的使用 AOP 功能。 明眼人一看就知道,那肯定是结合了 IoC 和 AOP 的 ProxyFactoryBean 更有研究的价值

图2 ProxyFactoryBean 的类继承关系

在 图2 中,有几个要注意的类

1.ProxyConfig 是一个数据基类,这个基类为 ProxyFactoryBean 提供了配置属性。
2.AdvisedSupport 的实现中,封装了 AOP 对通知的和通知器的相关操作。

  • 配置 ProxyFactoryBean

前面提到了,ProxyFactoryBean 需要在 IOC 容器中进行配置,ProxyFactoryBean 是最灵活的一种 Spring AOP 应用。我们要从这里为入口了解一下。首先了解,在 XML 中配置 ProxyFactoryBean,配置 ProxyFactoryBean 要经过一系列步骤:

1.定义使用的通知器 Advisor,这个通知器作为一个 Bean 来定义。很重要的一点是,这个通知器的实现定义了需要对目标对象进行增强的切面行为,也就是 Advice
2.定义一个 ProxyFactoryBean ,同样把他作为 Bean 来定义,是封装 AOP 主要功能的类。 在配置 ProxyFactoryBean 时,需要设定与 AOP 实现相关的重要属性,比如 proxyInterface 、interceptorName 和 target 等。interceptorName 往往设置为 Advisor 通知器,你可能会纠结这不是拦截器的意思吗?实际上这些通知器在 ProxyFactoryBean 的 AOP 配置下,是通过使用代理对象的拦截器机制起作用的。 请记住这句话。
3.定义 target 属性,作为 target 属性注入的 Bean。很明显,表示目标对象。

1
2
3
4
5
6
7
8
9
10
11
/***代码清单 9 在 XML 中配置 ProxyFactoryBean ***/
<bean id="advisor" class="cn.dombro.spring.aop.MyAdvisor"/>
<bean id="aop_bean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="cn.dombro.spring.aop.xml.Counter"/>
<property name="target">
<bean class="cn.dombro.spring.aop.xml.CounterImple"/>
</property>
<property name="interceptorNames">
<list><value>advisor</value></list>
</property>
</bean>

ProxyFactoryBean 生成 AopProxy

前面说到 Spring AOP 其作用的第一件事就是 为目标对象生成代理。那么 ProxyFactoryBean 是如何生成代理的呢?ProxyFactoryBean 中,需要为 target 目标对象生成 Proxy 代理对象,从而为 AOP 横切面的编织做好准备。

  • 两种生成代理对象的方式

ProxyFactoryBean 有两种生成代理对象 AopProxy 的方式一种是使用JDK动态代理,另一种是使用 CGLIB。你可以把这句话当成结论记住,可以想一下为什么要用两种方式?答案很简单,JDK 的动态代理 只能代理那些 实现某个接口的目标对象,而 CGLIB 可以为没有实现接口的目标对象生成代理。

图3 两种代理都实现了 AopProxy接口

  • AopProxy 的生成过程

由于 ProxyFactoryBean 实现了 FactoryBean 接口,而从 FactoryBean 中获取对象是以 getObject() 方法,作为入口完成的。所以理所当然,如果想通过 ProxyFactoryBean 获得 AopProxy 对象就一定会从 ProxyFactoryBean 实现的 getObject() 方法中寻找。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/***代码清单 10 ProxyFactoryBean 的 getObject ****/
public Object getObject() throws BeansException {
//这个方法对 ProxyFactoryBean 的 Advisor 链进行初始化
this.initializeAdvisorChain();
//这里针对单例也就是默认的目标对象生成代理对象
if (this.isSingleton()) {
return this.getSingletonInstance();
} else {
if (this.targetName == null) {
this.logger.warn("Using non-singleton proxies with singleton targets is often undesirable. Enable prototype proxies by setting the 'targetName' property.");
}
//这里针对非单例的目标对象生成代理对象
return this.newPrototypeInstance();
}
}

从代码清单10 中可以看到,getObject() 方法做了两个非常重要的事情。

  1. 通过 initializeAdvisorChain()方法,将配置在 ProxyFactoryBean 的 Advisor 链进行初始化。这个初始化是把从 IoC 容器中获取到的 通知器 添加到 拦截器链中。这里简单记忆有个印象即可。

  2. 对目标对象生成代理对象 AopProxy ,针对目标对象的单例或和单例对应生成单件代理和非单件代理的方法。我们主要研究单件代理生成方式 getSingletonInstance();

  • getSingletonInstance() 生成单件代理对象

如上,ProxyFactoryBean 通过 在 getObject() 方法中调用 getSingletonInstance() 方法,为目标对象生成一个单例的代理。我们现在走进 getSingletonInstance 看一下这个代理是如何生成的

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
/***代码清单11 getSingletonInstance 生成单例代理对象 ***/
private synchronized Object getSingletonInstance() {
if (this.singletonInstance == null) {
this.targetSource = this.freshTargetSource();
if (this.autodetectInterfaces && this.getProxiedInterfaces().length == 0 && !this.isProxyTargetClass()) {
//根据 AOP 框架判断需要代理的接口
Class<?> targetClass = this.getTargetClass();
if (targetClass == null) {
throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
}

this.setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
}

super.setFrozen(this.freezeProxy);
//2.这里使用 ProxyFactory 来生成 代理对象
this.singletonInstance = this.getProxy(this.createAopProxy());
}

return this.singletonInstance;
}

//真正生成代理对象是使用 AopProxy.getProxy实现的 <- 这句话很重要哦
protected Object getProxy(AopProxy aopProxy) {
return aopProxy.getProxy(this.proxyClassLoader);
}

看上去貌似也没那么难,注意代码清单11 中的 17 行 通过 getProxy() 方法来生成代理对象,而其参数 是由 createAopProxy 返回的 AopProxy 对象。这里面又有什么玄机呢?createAopProxy 是在 ProxyCreatorSupport 定义,返回一个 AopProxy。只好点开 createAopProxy

1
2
3
4
5
6
7
8
/***代码清单12 ProxyCreatorSupport 中的  createAopProxy ***/
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
this.activate();
}
//通过调用 AopProxyFactory 的 createAopProxy 方法得到 AopProxy 对象
return this.getAopProxyFactory().createAopProxy(this);
}

由于代理对象的具体生成是 AopProxy 而生成,但 AopProxy 又有两种实现,JdkDynamicAopProxy 和 Cglib2AopProxy 两种方式。所以 ProxyCreatorSupport 的 createAopProxy 方法,会根据传入的 AdvisedSupport(这里传入this 是因为 ProxyCreatorSupport 实现了 AdvisedSupport) 来决定使用 JDK动态代理 还是 CGLIB 的方式生成代理。

  • 小结

我觉的看到这你一定是蒙圈了,我用一句话总结,ProxyFactoryBean 生成代理对象由 getObject 方法中调用的 getSingletonInstance 方法为入口。而真正生成代理对象的是 AopProxy 的 getProxy 方法 ,具体使用哪种 AopProxy 生成 代理对象由 ProxyFactoryBean 的父类 AdvisedSupport 决定。

JdkDynamicAopProxy 和 Cglib2AopProxy

上面说到 ProxyFactoryBean 会从 getObject 方法中的 getSingletonInstance 方法 成两种 AopProxy ,即 图3 中的 JdkDynamicAopProxy 和 Cglib2AopProxy 。其中 JdkDynamicAopProxy 使用动态代理为接口类型(实现接口类型)目标对象生成代理对象,而 Cglib2AopProxy 则通过第三方库可以为非接口类型目标对象生成代理对象。不过通过上一节我们知道,AopProxy 生成代理对象是通过 getProxy() 方法,所以无论 JdkDynamicAopProxy 还是 Cglib2AopProxy 我们只需要查看 getProxy() 中的代码就可以知道代理对象的生成过程了

代理对象的生成
  • JdkDynamicAopProxy 生成 代理对象

首先来看一下 基于 动态代理的 JdkDynamicAopProxy 是如何生成代理对象的

1
2
3
4
5
6
7
8
9
10
11
/***代码清单13 JdkDynamicAopProxy 生成代理对象***/
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}

Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
this.findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
//生成代理对象
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

我们可以看到 JdkDynamicAopProxy 和 代码清单3 中的自定义动态代理类中的 blind 几乎是一样的。还可以得出一个结论 JdkDynamicAopProxy 一定实现了 InvocationHandler 接口,对,就是通过那个 this 。

  • Cglib2AopProxy 生成代理对象

具体参见 Cglib2AopProxy.getProxy() 方法,CGLIB 原理我也不是很懂,不敢瞎比比..(T_ T)。

Spring AOP 拦截器的调用

首先,一句结论 : 在 Spring AOP 通过 JdkDynamicAopProxy 或 Cglib2AopProxy 生成 代理对象的时候,相关的拦截器已经配置到代理对象中去了,拦截器在代理对象中起的作用是通过对这些方法的回调完成的。上述两种 AopProxy 拦截的方式是不一样的。

  • 拦截器、拦截器链

关于什么是拦截器,实际上拦截器 就是 Spring AOP 将 Advisor 配置给代理对象起到拦截目标方法方法的对象。也就是说代理对象通过拦截器知道要对目标对象的哪些目标方法进行哪种通知。对目标方法的增强就是通过拦截器完成的。这句话可以当做结论记下来。拦截器链,就是配置的所有拦截器的集合

AopProxy 的拦截

拦截,拦截什么呢?当然是拦截目标方法,实际上这个拦截的过程就是面向切面的过程。还记的原生代理类中我们在哪里对目标方法进行拦截吗?invoke 方法 。Spring 的 JdkDynamicAopProxy 也是在 invoke 方法中实现的拦截,但复杂得多。

  • JdkDynamicAopProxy 的 invoke 拦截

如果使用 JdkDynamicAopProxy 生成代理对象,则需要实现 InvocationHandler 的 invoke 方法设置拦截器的回调。这和代码清单3 中的原生 代理类是一样的。

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/***代码清单14 JdkDynamicAopProxy 的 invoke 回调***/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Class<?> targetClass = null;
Object target = null;

Boolean var10;
try {
if (this.equalsDefined || !AopUtils.isEqualsMethod(method)) {
if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
Integer var20 = this.hashCode();
return var20;
}

if (method.getDeclaringClass() == DecoratingProxy.class) {
Class var18 = AopProxyUtils.ultimateTargetClass(this.advised);
return var18;
}

Object retVal;
if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) {
retVal = AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
return retVal;
}

if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
//1.得到目标对象
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
//1.通过 AdvisedSupport 获取目标方法的拦截器链。
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
//2.如果拦截器链是空的,则直接调用目标方法
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
//这里是对目标方法的调用
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
} else {
//3.如果有拦截器链,则将代理对象,目标对象,目标方法,方法参数,
//以及拦截器链封装为一个 MethodInvocation 对象
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
//4.invocation.proceed 方法会沿着拦截器链向前执行
retVal = invocation.proceed();
}

Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
retVal = proxy;
} else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);
}

Object var13 = retVal;
return var13;
}

var10 = this.equals(args[0]);
} finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}

if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}

}

return var10;
}

代码清14 的重点有三处:

  1. 当目标方法不存在拦截器链时,将直接调用目标方法。这个目标方法的调用是 AopUtils.invokeJoinpointUsingReflection() 方法。
  2. 拦截器链是通过 AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice()方法得到的,传入的参数是 目标方法 和 一个类加载器。
  3. 将拦截器链、目标方法、目标对象、目标方法参数作为参数,构造出一个 ReflectiveMethodInvocation 对象,在执行 MethodInvocation.proceed 方法,这个方法会按照拦截器链的方向向前执行。

以上三个发现的结论是很重要的。

  • Cglib2AopProxy 的 intercept 拦截

Cglib2AopProxy 对 目标方法的拦截和 JdkDynamicAopProxy 的方式十分类似。这里就只说下区别:

  1. Cglib2AopProxy 的拦截器回调发生在 intercept 方法中。
  2. Cglib2AopProxy 对拦截器链的调用通过构造 CglibMethodInvocation 对象完成的。
拦截器链的调用

Spring AOP 对目标对象的增强的实现封装在拦截器链中,有一个个具体的拦截器来完成。那么究竟是如何实现的呢?通过上一节我们知道,无论是 JdkDynamicAopProxy 还是 Cglib2AopProxy 对拦截器链的调用都是殊途同归,通过 ReflectiveMethodInvocation 对象的 proceed 方法。一起来看一下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/***代码清单 15 ReflectiveMethodInvocation 的 proceed 方法***/
public Object proceed() throws Throwable {
//0.如果拦截器链中的迭代器调用完毕,这里开始调用目标方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return this.invokeJoinpoint();
} else {
//1.从拦截器链中第一个拦截器开始
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
//2. 对拦截器进行动态判断,如果和定义的 Pointcut 一致,那么 Advice 将会执行。
//如果不匹配,那么递归调用 proceed, 直到所有拦截器都被运行过为止。
InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
return dm.methodMatcher.matches(this.method, this.targetClass, this.arguments) ? dm.interceptor.invoke(this) : this.proceed();
} else {
return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
}
}
}

以上就是整个拦截器目标方法被调用的过程。有点神奇耶,我们看到ReflectiveMethodInvocation.proceed 方法会遍历拦截器链里面的所有拦截器,如果拦截器中切入点和当前要调用的匹配就执行拦截器的 invoke 方法。如果拦截器中的切入点和当前方法不匹配,则按照 ReflectiveMethodInvocation 的 proceed 方法,继续下一个拦截器。在下面会介绍拦截器的 inbvoke 方法。当所有拦截器都遍历完了,就执行目标方法!多么精巧的设计。

拦截器链的生成

上面直接讲了 拦截器链的调用 而跳过了 拦截器的生成这部分。这里来简单补充一下。

  1. 首先拦截器链是由 AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice() 方法得到的(上面提到了)。
  2. 而在上述方法中生成 拦截器链 则是由配置好的生成拦截器链的工厂 DefaultAdvisorChainFactory. getInterceptorsAndDynamicInterceptionAdvice() 生成。拦截器工厂生成拦截器则需要 通知器链。一起来看一下
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
39
40
41
42
43
44
45
46
47
48
/***代码清单16 DefaultAdvisorChainFactory 生成拦截器链***/
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class<?> targetClass) {
//1.首先通过 config 得到通知器链
List<Object> interceptorList = new ArrayList(config.getAdvisors().length);
Class<?> actualClass = targetClass != null ? targetClass : method.getDeclaringClass();
boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
Advisor[] var8 = config.getAdvisors();
int var9 = var8.length;

for(int var10 = 0; var10 < var9; ++var10) {
Advisor advisor = var8[var10];
MethodInterceptor[] interceptors;
if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor)advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
//2.看到拦截器是通过 AdvisorAdapterRegistry 对象的 getInterceptors 方法让针对通知器
//生成拦截器的。<- 这个方法很重要!
interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
if (mm.isRuntime()) {
MethodInterceptor[] var15 = interceptors;
int var16 = interceptors.length;

for(int var17 = 0; var17 < var16; ++var17) {
MethodInterceptor interceptor = var15[var17];
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
} else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
} else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor)advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
} else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}

return interceptorList;
}

实际上,在代码清单16 中,最值得研究的就是利用 AdvisorAdapterRegistry 对象的 getInterceptors ,即 registry.getInterceptors(advisor) 方法 ,从通知器得到拦截器。这里用到了适配器模式。下面会详细讲解。值得注意的是,我们看到拦截器链的生成

  1. 通知器链从哪里生成的?还记得 ProxyFactoryBean 的 getObject 中的 initializeAdvisorChain() 方法吗? 一起来看一下:
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
39
40
41
42
43
44
45
46
/***代码清单 17 ProxyFactoryBean 的初始化 通知器链***/
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
if (!this.advisorChainInitialized) {
if (!ObjectUtils.isEmpty(this.interceptorNames)) {
if (this.beanFactory == null) {
throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) - cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
}

if (this.interceptorNames[this.interceptorNames.length - 1].endsWith("*") && this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
throw new AopConfigException("Target required after globals");
}

String[] var1 = this.interceptorNames;
int var2 = var1.length;

for(int var3 = 0; var3 < var2; ++var3) {
String name = var1[var3];
if (this.logger.isTraceEnabled()) {
this.logger.trace("Configuring advisor or advice '" + name + "'");
}

if (name.endsWith("*")) {
if (!(this.beanFactory instanceof ListableBeanFactory)) {
throw new AopConfigException("Can only use global advisors or interceptors with a ListableBeanFactory");
}

this.addGlobalAdvisor((ListableBeanFactory)this.beanFactory, name.substring(0, name.length() - "*".length()));
} else {
//这里才是重点
Object advice;
if (!this.singleton && !this.beanFactory.isSingleton(name)) {
//1.看,通过 BeanFactory 获得 interceptorNames 这个 List 中的每个 Advisor
//的名字,交给BeanFactory,再通过 getBean 去获取
advice = new ProxyFactoryBean.PrototypePlaceholderAdvisor(name);
} else {
advice = this.beanFactory.getBean(name);
}
//2.将得到的所有 Advisor 加入到通知链中
this.addAdvisorOnChainCreation(advice, name);
}
}
}

this.advisorChainInitialized = true;
}
}

通过代码清单17 我们知道了 Advisor 的获取是通过 IoC 容器完成的,多么精巧的设定。

从 Advice 通知得到 Interceptor 拦截器

前面通过 代码清单16 ,已经介绍了这个很重要的方法 AdvisorAdapterRegistry.getInterceptors 方法,会根据 Advisor 得到 拦截器 Interceptor,但具体的实现是在 DefaultAdvisorAdapterRegistry 对象中,


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
/***代码清单 18 DefaultAdvisorAdapterRegistry 中的适配注册 拦截器方法***/
//这里实在 DefaultAdvisorChainFactory 中启动的 getInterceptors 的地方
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList(3);
//1.从 通知器中得到 通知
Advice advice = advisor.getAdvice();
//2.如果是 MethodInterceptor 类型通知,直接加入 interceptors 的 List 中不适配
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor)advice);
}
//3.得到是配置的 Adapter 迭代器,此处配置好的 Adapter 有 :
// MethodBeforeAdviceAdapter,AfterReturningAdviceInterceptor,ThrowsAdviceInterceptor
Iterator var4 = this.adapters.iterator();

while(var4.hasNext()) {
//3.使用 AdvisorAdapter 进行适配
AdvisorAdapter adapter = (AdvisorAdapter)var4.next();
//4.从 Advice 中得到的其对应的 Adapter 适配器,再从该适配器中取出已经封装好了的 拦截器。
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}

if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
} else {
return (MethodInterceptor[])interceptors.toArray(new MethodInterceptor[interceptors.size()]);
}
}

看这部分的时候。一定要看 代码清单18 中的注释,DefaultAdvisorAdapterRegistry 的 getInterceptors 方法说白了通过 通知 Advisor 中的 不同通知类型,利用适配器模式生成不同类型的拦截器。不同类型的拦截器的 invoke 方法实现各有不同,用来达到以哪种形式进行增强的目的。

举个例子:Advisor 中的通知 Advice 如果是 前置通知 就生成 MethodBeforeAdviceInterceptor 拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/***代码清单 19  MethodBeforeAdviceInterceptor 拦截器***/
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
private MethodBeforeAdvice advice;

public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
//注意这个方法!是真正实现对目标方法增强的部分
public Object invoke(MethodInvocation mi) throws Throwable {
//首先调用了advice 的前置增强
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
//执行过后会让整个拦截器链向前执行
return mi.proceed();
}
}

Advisor 中的通知 Advice 如果是 返回通知 就生成 AfterReturningAdviceInterceptor 拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/***代码清单 20 AfterReturningAdviceInterceptor 拦截器***/
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
private final AfterReturningAdvice advice;

public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}

public Object invoke(MethodInvocation mi) throws Throwable {
//先让拦截器链向前执行
Object retVal = mi.proceed();
//之后调用后置增强
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
}

我们看到不同的拦截器,首先一定要注意到 invoke() 方法,结合 代码清单15 看到的 Interceptor.invoke() 的调用,再以方法前置拦截器 MethodBeforeAdviceInterceptor 中的 invoke()为例,我们看到 方法前置拦截器会首先出发 Advice.before 的回调,然后在执行 MethodInvocation.proceed ,整个拦截器链向前运行,当某个拦截器对目标方法匹配,同样会执行该拦截器的 invoke 方法,所以拦截器的 invoke 方法是真正实现切面增强的地方。啊!多么精巧的设计!!!

总结

  • 一些总结(想起什么说什么)

0.一些专业术语就不总结了。打字累。
1.Spring AOP 中一个重要对象 Advisor 通知器。它是由 Advice 通知 和 Pointcut 切点构成。Advisor 决定了要在哪个 目标方法 进行那种类型的切面增强。Advisor 就像是一个作战计划,有了 Advisor 还不能实现真正意义上的切面增强。
2.AOP 实现的核心是靠动态代理生成代理对象(很多书都不把这句话说完整,歧义很大的),Spring AOP 有两种生成代理对象的方法,使用 JDK 动态代理 的 JdkDynamicAopProxy 和 使用 第三方库的 cglib 的 Cglib2AopProxy。之所以会有两种生成代理对象的策略,是因为 JDk 动态代理只能为实现某接口的类生成代理对象,而 cglib 则可以为任何类生成代理对象。
3.生成代理对象的入口是 ProxyFactoryBean 的 getObject() 方法,该方法也是使用首先调用 initializeAdvisorChain() 方法,完成 通知器链的初始化,然后 调用 getSingletonInstance () 方法,获取代理对象。
4.getSingletonInstance()的原理是调用 AopProxy.getProxy() 生成代理对象。调用 ProxyFactoryBean.createAopProxy 返回的两种不同的 生成代理类 JdkDynamicAopProxy 和 Cglib2AopProxy。在调用这两个类的 getProxy 方法。具体操作可以。戳这里
5.Spring AOP 完成对目标方法的增强是靠拦截器,JdkDynamicAopProxy 中实现对目标方法的拦截是在 invoke 方法中。在 invoke 方法中 通过 AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice() 方法获取拦截器链。并将拦截器链配置成 MethodInvocation 对象,然后调用 MethodInvocation.proceed() 方法,实现对拦截器的向前调用。戳这里 真正生成 拦截器链的 地方是 DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice() 方法。戳这里
6.MethodInvocation.proceed()方法首先会检查当前拦截器是否和调用的方法匹配,如果匹配,则执行该拦截器的invoke方法。拦截器的 invoke 方法是真正实现切面增强的地方。戳这里
7.至于拦截器链如何生成,拦截器是在 GlobalAdvisorAdapterRegistry 的 getInterceptors 方法生产给你目标方法的所有拦截器,这个方法需要传入一个通知器 Advisor 作为参数,即通过 Advisor 生成 目标方的所有拦截器,再将这些拦截器添加到一个 List 中,形成了拦截器链。戳这里
8.在 GlobalAdvisorAdapterRegistry.getInterceptors(advisor)方法中,使用适配器模式 为不同的通知器生成不同拦截器,不同的拦截器的 invoke 方法是不一样的,戳这里
9.ProxyFactoryBean.initializeAdvisorChain() 方法,通过 BeanFactory 从 XML 配置中获取 所有 Advisor 便将其组成 通知器链,并将其交给 AdvisedSupport 持有。通知器链为后面拦截器链的生成提供了原材料,所以 Advisor 是很重要的对象。

  • 一点感受

咳咳!一个不小心写了这么长的笔记。昨晚还刷了一个夜。起初看 Spring AOP 这里我很崩溃!满脑子都再说:啥!啥!啥!这都是个啥!究其原因,是因为当时对 AOP 和动态代理 这的概念有些模糊。弄清楚 AOP 后,我已经完全被好奇心驱使,开始去看源码。没有看得那么细,但是 Spring 的命名规范,不需要一个方法一个方法的去看。了解大概意思和思想那就好了。看过这篇笔记的同学一定会发现,笔记说的最多的一句话就是 :啊!多么精巧的设计!是的,Spring 得开发人员实在是太牛了。

CATALOG
  1. 1. Spring的核心 —— AOP
    1. 1.1. 前言
    2. 1.2. AOP
      1. 1.2.1. 啥是 AOP
      2. 1.2.2. AOP 实现的核心动态代理
    3. 1.3. Spring AOP
      1. 1.3.1. 通知 切点 通知器
        1. 1.3.1.1. 通知 Advice
      2. 1.3.2. 切点(关注点) Pointcut
      3. 1.3.3. 通知器 Advisor
      4. 1.3.4. Spring AOP 设计与实现
        1. 1.3.4.1. Spring AOP 的设计分析
        2. 1.3.4.2. Spring AOP 的 重点研究对象 ProxyFactoryBean
      5. 1.3.5. ProxyFactoryBean 生成 AopProxy
      6. 1.3.6. JdkDynamicAopProxy 和 Cglib2AopProxy
        1. 1.3.6.1. 代理对象的生成
        2. 1.3.6.2. Spring AOP 拦截器的调用
          1. 1.3.6.2.1. AopProxy 的拦截
          2. 1.3.6.2.2. 拦截器链的调用
        3. 1.3.6.3. 拦截器链的生成
      7. 1.3.7. 从 Advice 通知得到 Interceptor 拦截器
    4. 1.4. 总结