在《Spring AOP实现浅析 一》《Spring AOP实现浅析 二》中,我们了解了Java中动态代理的两种实现、Spring AOP的基础概念、基础类。本文带你了解代理对象的创建和代理逻辑执行。
本文中源码来自Spring 5.3.x分支,github源码地址:https://github.com/spring-projects/spring-framework
一 创建代理对象
1.1 选择代理方式
ProxyFactory.getProxy()方法如下:
public Object getProxy() {
return createAopProxy().getProxy();
}
AopProxy接口,是对JDK动态代理和CGLIB代理开箱即用的抽象,实现类如下,其中ObjenesisCglibAopProxy是CglibAopProxy的一个子类。

AopProxyFactory接口,用于创建AopProxy对象。源码中的默认实现类DefaultAopProxyFactory,其中有对使用哪种代理实现的判断逻辑。

ProxyFactory是ProxyConfig的子类,optimize和proxyTargetClass属性默认值都是false,即默认使用JDK代理。当我们设置任意一个为true时,就有可能使用cglib代理。

proxyFactory.setOptimize(true);
proxyFactory.setProxyTargetClass(true);
当给proxyFactory添加Interface时,必然会使用JDK代理。(Spring AOP自动创建代理对象时,会判断类是否实现了接口,并设置ProxyFactory.interfaces属性)
proxyFactory.addInterface(OrderInterface.class);
注意这几点:
- 在Spring 3.2.0版本前,代理实现只有JDK动态代理一种方案。
- Spring 3.2.0版本引入了Cglib,因为Cglib性能更好,且创建的代理对象即可以赋值给父类变量,也可以赋值给接口变量。Cglib代理对类型转换支持更好。
- optimize即优化的意思,设置为true时,即从默认的JDK代理,改用更好的Cglib代理。
1.2 创建代理对象
JdkDynamicAopProxy中实现很简单,通过Proxy.newProxyInstance创建代理对象。

JdkDynamicAopProxy类也是InvocationHandler接口实现,当代理对象执行某个方法时,会进入invoke()方法。
在CglibAopProxy实现中,创建Enhancer对象并设置superClass、advised、callbackFilter、callbacks等属性,调用Enhancer.create()来创建代理对象。

DynamicAdvisedInterceptor是一个关键callback,当代理对象执行某个方法时,会进入intercept()方法。
二 执行代理逻辑
以我们熟悉的JDK代理实现为例。
2.1 整体流程
以JdkDynamicAopProxy为例,当代理对象执行任何方法时,都会进入JdkDynamicAopProxy#invoke中,步骤如下:
- 对equals、hashCode方法,不进行增强,直接使用被代理对象执行方法;

- 如果exposeProxy=true,设置代理对象到AopContext.currentProxy。
- 根据被代理对象、当前执行的方法,与ProxyFactory中所有Advisor进行匹配,并将Advice适配成MethodInterceptor对象;

- 得到List<MethodInterceptor> chain,并放入methodCache缓存中(一个Map)


- 如果当前方法未命中任何Advisor,则使用被代理对象直接反射执行;

- 如果有命中的Advisor,则使用ReflectiveMethodInvocation执行Interceptor chain;
- 执行完最后一个MethodInterceptor了,就会执行被代理对象的当前方法;

- 代理结束,还原AopContext.currentProxy为旧值。
2.2 为方法匹配Advisor
当前方法是否匹配Advisor,是根据Advisor的pointcut属性来判断。

一个Pointcut接口实现,持有ClassFilter接口、MethodMatcher接口实现类对象;

ClassFilter用于检查当前类是否匹配;
boolean matches(Class<?> clazz);
MethodMatcher用于检查当前方法是否匹配;
boolean matches(Method method, Class<?> targetClass);
可见,Pointcut就在类和方法层面,规定了一个Advisor的适用范围。
2.3 适配Advice为MethodInterceptor
如下,当我们定义Advice实现如MethodBeforeAdvice时,只提供了增强逻辑,那被代理对象的原方法是如何被调用的?
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class SimpleBeforeAdvice implements MethodBeforeAdvice {
// 只定义代理逻辑,不用手动回调
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("MethodBeforeAdvice...");
}
}
当前方法匹配到一个Advisor时,会将Advisor的advice属性,适配成MethodInterceptor对象。
MethodInterceptor接口就是Advice的子接口,仅有一个方法声明。

// invocation就是MethodInterceptor链
Object invoke(@Nonnull MethodInvocation invocation) throws Throwable;
MethodBeforeAdviceAdapter类,会将MethodBeforeAdvice适配成MethodBeforeAdviceInterceptor对象,最终执行的是Interceptor的invoke方法。


相似的还有AfterReturningAdviceInterceptor、AfterReturningAdviceAdapter、ThrowsAdviceInterceptor。
之所以如此:
- 一是因为Advice实现中只需提供增强逻辑,没有回调被代理对象原方法;
- 二是因为Advice只是一个个独立的通知,当一个方法匹配到多个Advice时,需要将它们按顺序依次执行完,最后才执行一次被代理对象原方法。
3.4 执行MethodInterceptor链
MethodInvocation接口就是链的定义,有JDK代理的实现ReflectiveMethodInvocation,它有这些属性:
// 代理对象
protected final Object proxy;
// 被代理对象
@Nullable
protected final Object target;
// 被代理方法
protected final Method method;
// 当前方法匹配的所有MethodInterceptor
protected final List<?> interceptorsAndDynamicMethodMatchers;
// 标记执行到哪个Interceptor了
private int currentInterceptorIndex = -1;
ReflectiveMethodInvocation.process方法中,会依次执行interceptorsAndDynamicMethodMatchers中每个MethodInterceptor;而MethodInterceptor.invoke方法中,又会回调ReflectiveMethodInvocation.process。
这样一来,链条便逐个推进了。
2.5 流程图

三 exposeProxy原理
3.1 AopContext原理
ProxyConfig的exposeProxy属性,表示对当前线程是否暴露代理对象,默认为false;当设置为true时,我们可在方法中获取代理对象。比如:
package com.xiakexing.service;
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Component;
@Component
public class UserService {
public void test() {
UserService self = (UserService) AopContext.currentProxy();
System.out.println(self);
}
}
底层是如何实现的呢?来看源码。
AopContext类持有静态属性ThreadLocal,用于在线程中保存当前代理对象。

将在执行通知的第一步,把代理对象设置到currentProxy。
JdkDynamicAopProxy#invoke中,在执行通知前,暴露代理对象;被代理类的原方法执行结束后,在finally中还原ThreadLocal。


在CglibAopProxy.DynamicAdvisedInterceptor#intercept中,也有相似的代码。
线程中,方法是基于方法栈的后进先出顺序被执行的。
当A类的a方法,调用B类的b方法时,currentProxy从proxyA,切换为proxyB;
当b方法执行完毕时,需要将currentProxy又切换回proxyA,以便a方法继续执行。
当AopContext.setCurrentProxy()入参为null时,会释放ThreadLocal(避免内存泄露)。

3.2 替代方案
AopContext类有如下说明
- Spring的AOP框架默认情况下不公开代理,因为这样做会带来性能成本。
- 当有合理的替代方法时,不应该使用AopContext,因为它使应用程序代码依赖于Spring AOP框架才能使用。
在使用@EnableAspectJAutoProxy时,exposeProxy也默认false。

可见,Spring官方并不推荐使用AopContext。为了在业务方法中获取代理对象,又有哪些替代方案呢?
- 在当前类中注入自己
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class UserService {
@Autowired
private UserService self;
public void test() {
System.out.println(self);
}
}
- 从ApplicationContext中获取代理对象
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class UserService implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void test() {
UserService self = applicationContext.getBean(UserService.class);
System.out.println(self);
}
}
在下一篇文章中,我们将分析@EnableAspectJAutoProxy的底层实现:
- 如何将@Aspect、 @Before处理为Advisor;
- 在bean生命周期的哪个阶段创建了代理对象。
1万+

被折叠的 条评论
为什么被折叠?



