疯狂的小鸡

AOP的实现

字数统计: 2.7k阅读时长: 11 min
2018/10/01 Share

本文章着重分析AOP实现中的方法链调用。阅读本文章时,强烈建议打开IDE自己跟着方法调用一层层走。建议从ProxyFactoryBean类中的getObject()方法入手。

spring与AOP有关的模块有2个,一个是spring-aop, 这是spring自己的aop实现,另一个是spring-aspect,这是利用 aspectJ的aop实现。这两者最大的区别在于Spring中AOP是基于JDK动态代理,而AspectJ是基于字节码操作。这篇文章主要介绍基于JDK动态代理的实现。

先回顾一下代理模式。代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。代理对象可以在目标对象实现的基础上,增强额外的功能操作,扩展目标对象的功能。对比spring-aop, 这里要提到两个概念。第一个是Pointcut, Pointcut就是核心关注点,也就是代理模式中被代理的对象,第二个是advice, advice就是横切关注点,也就是切面,对应代理模式中对代理对象的增强功能。在spring中advice切面功能通过拦截器来实现。

AOP的实现主要分代理对象的生成与拦截器的调用两部分。

代理对象的生成

Spring有三个类负责代理对象的生成,ProxyFactoryBean、ProxyFactory和AspectJProxyFactory。三者都在同一集成体系下。其中ProxyFactoryBean和ProxyFactory使用的spring自己的实现,两者的区别在于ProxyFactoryBean可以直接通过声明式配置而ProxyFactory只能通过编程式配置。
AspectJProxyFactory利用的是AspectJ的实现。下面以ProxyFactoryBean为例详细讲讲Proxy对象的生成过程。

getObject()

首先ProxyFactoryBean它是一个FactoryBean。根据FactoryBean的特性可以知道Proxy对象生成的入口位于getObject方法。

1
2
3
4
5
6
7
8
9
10
11
12
public Object getObject() throws BeansException {
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();
}
}

可以看到getObject方法中首先通过initializeAdvisorChain对通知链进行了初始化。为什么是AdvisorChain而不是AdviceChain呢。这里就要提到第三个概念advisor。如果我们去看Advisor的默认实现,可以发现advisor持有一个Pointcut和一个advice对象。Advisor的作用就在于把这两个对象结合起来,告诉spring该使用哪个advice并在哪个Pointcut使用它。关于对通知链初始化的具体过程我们留在后面再说。

getSingletonInstance()

继续看getObject方法可以看到,spring在生成代理对象时作了对象类型的区分,两者差别不大。我们以getSingletonInstance方法为例,它干了两件事。第一是通过setInterfaces获取代理接口,第二是通过getProxy方法获取代理对象。setInterfaces不用说,就是代理模式代理对象需要实现被代理对象的接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private synchronized Object getSingletonInstance() {
if (this.singletonInstance == null) {
this.targetSource = this.freshTargetSource();
if (this.autodetectInterfaces && this.getProxiedInterfaces().length == 0 && !this.isProxyTargetClass()) {
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);
this.singletonInstance = this.getProxy(this.createAopProxy());
}

return this.singletonInstance;
}

createAopProxy()

看getProxy方法。可以看到getProxy方法接受一个AopProxy参数,而这个参数是由createAopProxy方法获取的。再往下走可以发现spring使用AopProxyFactory来创建AopProxy,具体使用的对象是DefaultAopProxyFactory。所以我们去DefaultAopProxyFactory看一下AopProxy是如何被创建的。在createAopProxy方法中可以看到,默认生成JdkDynamicAopProxy,如果目标对象设置了targetClass并且targetClass不是一个接口类且不是代理类,则会生成 ObjenesisCglibAopProxy。无论生成哪一种AopProxy,都需要一个AdvisedSupport config作为参数,这个AdvisedSupport实际上就是对AopProxy所需参数的封装。

1
2
3
4
5
6
7
8
9
10
11
12
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
return new JdkDynamicAopProxy(config);
} else {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
} else {
return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
}
}
}

getProxy()

从JdkDynamicAopProxy的getProxy方法中可以看到,spring使用Proxy对象通过反射获取Proxy。Proxy的是JDK自带的类,在此就不多赘述了。有兴趣的同学还可以去CglibAopProxy中的getProxy方法中查看使用cglib生成AopProxy的过程。可以看到第一行声明的enhancer已经是cglib中的类了。

1
2
3
4
5
6
7
8
9
10
//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);
}

拦截器的调用

现在我们已经拥有了AopProxy对象了,在ProxyFactoryBean中配置的target已经不会被应用直接调用其方法实现了,而是会被AopProxy代理对象拦截。另外,相关的拦截器也通过被配置到代理对象中了。那么,这些拦截器是如何被调用的呢?

proceed()

对于JdkDynamicAopProxy,是通过实现InvocationHandler中的invoke方法来实现的。可以看到在invoke方法中,主要通过生成的ReflectiveMethodInvocation对象的proceed方法来遍历拦截器链。在CglibAopProxy中的实现类似,可以看到在intercept方法中同样生成了ReflectiveMethodInvocation的子对象,并调用其proceed方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
public Object proceed() throws Throwable {
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return this.invokeJoinpoint();
} else {
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
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);
}
}
}

下面来看看proceed方法。这个方法比较短,我们可以仔细研究一下。第一个if代码块中有一个currentInterceptorIndex,指的是当前的拦截器在interceptorList里的下标。把这句话翻译一下就是说如果当前拦截器是interceptorList里最后一个拦截器,则直接执行被代理对象的方法。那么是不是最后一个拦截器就不执行呢?不是。这里有个坑,可以看到在调用拦截器的时候会先执行递增++this.currentInterceptorIndex,并且currentInterceptorIndex的初始值是-1。所以在if中判断的当前拦截器,实际执行的是下一个拦截器。所以把这句话重新翻译一下就是如果当前拦截器已迭代完毕,则直接执行被代理对象的方法。

再来看else。第一句话通过一个interceptorsAndDynamicMethodMatchers获取到一个Object。看到这里我一下懵了。这个interceptorsAndDynamicMethodMatchers是个啥,获取到的Object又是啥。这里不卖关子,interceptorsAndDynamicMethodMatchers实际就是一个包含所有拦截器的List, 这个Object自然就是获取到的当前拦截器了。

getInterceptorsAndDynamicInterceptionAdvice()

关于这个List的获取,我们可以往回看到JdkDynamicAopProxy中的invoke方法。该方法中通过advised.getInterceptorsAndDynamicInterceptionAdvice 获取了这个List。进入getInterceptorsAndDynamicInterceptionAdvice方法可以看到该方法实际上只是为这个List设置了缓存,实际List获取是通过advisorChainFactory完成的。在advisorChainFactory的默认实现类DefaultAdvisorChainFactory里我们可以看到,getInterceptorsAndDynamicInterceptionAdvice方法完成了interceptorList的构建工作。

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
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class<?> targetClass) {
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)) {
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;
}

getIntercepters()

需要注意的是,interceptorList是通过遍历AdvisorChain完成的,可以看到interceptors = registry.getInterceptors(advisor)方法接收一个advisor参数,返回一个interceptor数组,这个过程称为拦截器的注册。可以看到这个注册过程是通过AdvisorAdapterRegistry对象完成的。继续往下找可以看到这个registry对象实际上是一个DefaultAdvisorAdapterRegistry的单例。可以看到,这个类持有一个adapters的List, 包括了全部3种通知类型。在getIntercepters方法中,可以看到对于每个advice, spring都回遍历这个List用以判断这个advice属于什么类型的advice, 从而注册不同的intercepter。

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
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
private final List<AdvisorAdapter> adapters = new ArrayList(3);

public DefaultAdvisorAdapterRegistry() {
this.registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
this.registerAdvisorAdapter(new AfterReturningAdviceAdapter());
this.registerAdvisorAdapter(new ThrowsAdviceAdapter());
}

public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList(3);
Advice advice = advisor.getAdvice();
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor)advice);
}

Iterator var4 = this.adapters.iterator();

while(var4.hasNext()) {
AdvisorAdapter adapter = (AdvisorAdapter)var4.next();
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()]);
}
}

invoke()

以MethodBeforeAdviceAdapter为例,它将advice封装成MethodBeforeAdviceInterceptor,在它的invoke回调方法中可以看到,先执行advice的before回调,然后出发proceed方法去执行下一个拦截器的迭代。根据这个逻辑,不用看源码也能知道,在AfterReturningAdviceAdapter的invoke方法中,首先执行proceed,然后执行advice的afterReturning回调,从而实现后置通知。ThrowsAdviceInterceptor则持有一个exceptionHandlerMap来应对不同的错误。

1
2
3
4
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}

TargetSource

spring-aop对目标的增强是通过拦截器来完成的,但有些时候需要对目标本身进行处理,这就要用到TargetSource接口了。

比较常用的TargetSource实现有HotSwappableTargetSource,它可以使用户以线程安全的形式切换目标对象,提供所谓的热交换功能。在需要切换目标对象时,调用其swap方法即可。常见的应用场景如切换数据库源,使用mysql做测试,上线时切换oracle。

AspectJ的AOP实现

以上就是spring-aop的实现。这里稍微提一下AspectJ,它的AOP实现是通过编织阶段(Weaving Phase),对目标Java类型的字节码进行操作,将需要的Advice逻辑给编织进去,形成新的字节码。因为JVM执行的都是Java源代码编译后得到的字节码,所以AspectJ相当于在这个过程中做了一点手脚,让Advice能够参与进来。

编织阶段可以有两个选择,分别是加载时编织(也可以成为运行时编织)和编译时编织:加载时编织(Load-Time Weaving)是在JVM加载类的时候完成的。而编译时编织(Compile-Time Weaving)则需要使用AspectJ的编译器来替换JDK的编译器。

CATALOG
  1. 1. 代理对象的生成
    1. 1.1. getObject()
    2. 1.2. getSingletonInstance()
    3. 1.3. createAopProxy()
    4. 1.4. getProxy()
  2. 2. 拦截器的调用
    1. 2.1. proceed()
    2. 2.2. getInterceptorsAndDynamicInterceptionAdvice()
    3. 2.3. getIntercepters()
    4. 2.4. invoke()
    5. 2.5. TargetSource
  3. 3. AspectJ的AOP实现