之前我讲过关于JDK代理和CGLIB代理如何实现无反射调用目标方法,今天咱们聊聊动态代理两个方面:一是代理对象是怎么生成的;二是代理对象的执行过程;
1. 创建代理对象
接下来会对Spring创建代理的步骤进行介绍,如何一步一步创建代理对象的,并且会通过问题和代码的形式来阐明创建代理的过程。
首先我们得理解一下这三者的关系Advisor Advice Aspect:
-
Advisor可以理解成时一种切面,由切点和通知组成,低级切面; -
Advice是通知概念,也就是需要增强的内容; -
Aspect是我们通常所知的切面,高级切面;
高级切面和低级切面区别主要是前者一个切面可以存在多个通知,而后者一个切面只能存在一个通知。
Spring在第一个Bean加载的时候就会去根据高级切面转换低级Advisor切面, 源码见:org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisors
该方法会对Aspect切面类中的每个通知方法(加了Before After等注解)转成一个切面,也就是由高级切面Aspect转成低级切面Advisor的过程;
1.获取Spring容器中的所有Bean的name,收集每个Bean中加了Aspect注解的为切面类;
2.去除该切面类中加了Pointcut注解的方法;
3.在剩下的方法中找到上述五个注解(Around, Before, After, AfterReturning, AfterThrowing)中的任一注解就认为该方法可以转成低级切面;
4.在切面中的通知方法加了多个注解只会有一个生效,优先级根据Around > Before > After > AfterReturning > AfterThrowing.
5.根据注解转成对应的通知对象, 源码见:org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvice
:
-
Around ==> AspectJAroundAdvice -
Before ==> AspectJMethodBeforeAdvice -
After ==> AspectJAfterAdvice -
AfterReturning ==> AspectJAfterReturningAdvice -
AfterThrowing ==> AspectJAfterThrowingAdvice
6.通知对象和AspectJExpressionPointcut的切点对象形成Advisor低级切面(InstantiationModelAwarePointcutAdvisorImpl);
7.根据切面类形成了一个List
8.在每一个Bean加载的时候,会对Bean中的所有方法(包括private final方法),只要存在匹配List中低级切面的表达式,即为该Bean创建代理对象;(我理解应该排除private和final修饰的方法)
9.当存在有资格的切面时并且切面中存在AspectJ的通知时, 会在所有的切面前面加上一个切面(ExposeInvocationInterceptor),该切面优先级也是最高的,要保证最先执行。 (原因见下文) 源码见:org.springframework.aop.aspectj.AspectJProxyUtils#makeAdvisorChainAspectJCapableIfNecessary
.
10.使用ProxyFactory准备创建代理对象,读取模板ProxyFactory,该模板是根据用户的配置生成的。如果不是强制使用CGLIB代理则得检查该类是否存在接口,存在接口塞到ProxyFactory对象中用于后期生成JDK代理对象;
源码见:org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy
11.选择使用代理对象,主要由proxyTargetClass字段控制
-
true ==> 无论目标类是否实现接口,均使用CGLIB代理;
-
false ==> 目标类存在接口且接口中至少存在一个方法,则选择JDK代理。其他均为CGLIB代理;
Note:从SpringBoot 2.x开始使用CGLIB作为默认的代理方式,如果需要使用JDK代理的话,可以通过配置项 spring.aop.proxy-target-class=false实现,基于@EnableAspectJAutoProxy注解修改proxyTargetClass是不能生效的
通常情况下我们总是会看到使用Enhancer的方式创建代理对象,实际上我们也可以使用ProxyFactory来创建代理对象,两者区别是前者比后者更底层。使用ProxyFacotory可以选择创建JDK代理对象(Proxy.newProxyInstance)或者CGLIB代理对象,而使用Enhancer的话是用于创建CGLIB代理对象的,为了方便大家的理解分别写了ProxyFactory和Enhancer去创建代理对象:
1.1 CGLIB创建代理对象
1. 使用ProxyFactory创建代理对象
public class ProxyFactoryDemo {
public static void main(String[] args) {
//1.基于切面使用ProxyFactory创建代理对象
ProxyFactory proxyFactory = new ProxyFactory();
//2.创建目标对象
ProxyFactoryDemo.Target target = new ProxyFactoryDemo.Target();
//3.告诉代理对象为哪个目标对象创建代理
proxyFactory.setTarget(target);
//4.获取目标类的接口 可用于JDK代理
proxyFactory.setInterfaces(target.getClass().getInterfaces());
//5.选择使用JDK代理还是CGLIB代理
/**
* 1. false
* a).目标类存在接口,则使用JDK代理;
* b).目标类不存在接口,则使用CGLIB代理;
* 2. true
* 无论是否实现接口均使用CGLIB代理
*/
proxyFactory.setProxyTargetClass(Boolean.TRUE);
//6.构建切面 = 切点 + 通知
//6.1 构建切点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* firstTargetMethod())");
AspectJExpressionPointcut pointcut2 = new AspectJExpressionPointcut();
pointcut2.setExpression("execution(* firstTargetMethod())");
//6.2 构建通知
MethodInterceptor methodInterceptor = invocation -> {
System.out.println("Before ....");
Object obj = invocation.proceed();
System.out.println("After ....");
return obj;
};
MethodInterceptor methodInterceptor2 = invocation -> {
System.out.println("Before 2 ....");
Object obj = invocation.proceed();
System.out.println("After 2 ....");
return obj;
};
//6.3 形成切面
Advisor firstAdvisor = new DefaultPointcutAdvisor(pointcut, methodInterceptor);
Advisor secondAdvisor = new DefaultPointcutAdvisor(pointcut2, methodInterceptor2);
proxyFactory.addAdvisors(firstAdvisor, secondAdvisor);
//7. 生成代理对象
ProxyFactoryDemo.Target proxyObj = (ProxyFactoryDemo.Target)proxyFactory.getProxy();
proxyObj.firstTargetMethod();
proxyObj.finalMethod();
}
public static class Target {
public void firstTargetMethod() {
System.out.println("Target + firstTargetMethod");
}
public void secondTargetMethod() {
System.out.println("Target + secondTargetMethod");
}
public void publicMethod(){
System.out.println("publicMethod...");
}
private void privateMethod(){
System.out.println("privateMethod...");
}
public final void finalMethod(){
System.out.println("finalMethod...");
}
}
}
2.使用Enhancer创建代理对象
public class EnhancerProxyDemo {
public static void main(String[] args) {
Target target = new Target();
/**
* 1.代理对象本身
* 2.需要代理的方法
* 3.代理方法的参数
* 这里只是生成了一个代理对象,包括该代理对象在执行的情况下的一些逻辑;
* 另外如果此处的代理对象如果没有执行方法时,是不会执行生成代理对象的。
*/
Target proxyObject = (Target) Enhancer.create(Target.class, (MethodInterceptor) (proxyObj, method, methodArgs, methodProxy) -> {
System.out.println("before...");
//1.使用目标方法通过反射方式执行
//Object result = method.invoke(target, methodArgs);
//2.使用代理方法方式调用 底层无反射 spring的选择
//Object result = methodProxy.invoke(target, methodArgs);
//3.使用代理方法父类方法 底层无反射
Object result = methodProxy.invokeSuper(proxyObj, methodArgs);
System.out.println("after...");
String returnString = (String) result;
return returnString + "after Proxy";
});
proxyObject.testPrivate();
}
//该类被final修饰时时不能生成代理对象的, 会报错
static class Target {
//该方法如果被final或者是private修饰的,那么代理对象就无法对该方法实现代理
public void testPrivate(){
System.out.println("Target + testPrivate");
}
}
}
Spring中存在两个MethodInterceptor
-
org.aopalliance.intercept.MethodInterceptor#invoke implement MethodInterceptor extend Interceptor extends Advice -
org.springframework.cglib.proxy.MethodInterceptor#intercept extends Callback
对于ProxyFactory和Enhancer两者创建代理对象中有个共同的类名:MethodInterceptor, 但是两个类在中相同的类发挥的作用完全不同
-
ProxyFactory中的MethodInterceptor是通知Advice的子子接口(孙子接口),因此是用于创建通知的;
-
Enhancer中的MethodInterceptor是回调函数Callback的子接口,用于代理对象回调进而执行切面逻辑。Spring中使用org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor作为切面的回调对象
a).同一个切面类中的同一个方法只能加一个切面注解,加了多个会怎样?
如果加相同的注解会报错,加不同的注解会按照上述第四点的优先级生效,例如同时加了Before和After时,只有Before的切点会执行通知,After的切点无效。
b).如何控制不同切面的顺序?
可以使用@Order(number)注解的方式,值越小优先级越高;例如A切面的优先级高于B切面,那么A、B两个切面中注解相同的情况下,A的通知先执行。
c).如何控制相同切面内不同通知的顺序?
这种情况下@Order注解是无效的,主要是通过方法名来排序的。例如相同切点的情况下,aaa()通知会在bbb()前执行。
d).如何判断某个Bean是否需要代理?
在上述第七步中会形成一个Map,将Map中的所有value的形成一个大List(candidateAdvisors), 将List中的每一个Advisor和Bean中的每一个方法进行切点表达式匹配,只要有一个命中,就会认为这个Bean需要产生代理对象。
e).Spring为何要将高级Aspect的切面类转成低级Advisor?
高级切面转低级切面是整体碎片化的过程,这样有利于整个AOP精准控制、扩展。
f).Spring如何选择代理(JDK代理还是CGLIB代理)?
见第11步
g). 什么情况下需要创建代理?为啥BeanA不用创建代理对象而BeanB需要?
代理对象在Spring加载的过程中,主要由这个方法org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
完成代理对象的创建。在该方法中如果检测到该Bean中的某个方法存在通知即为该Bean创建代理对象。
1.2 JDK创建代理对象
JDK创建代理对象条件上述已经说明了,另外JDK创建代理对象时是必须要存在接口的,因此在注入这个Bean时,必须得使用接口名去注入,如果使用实现类的话在启动时会报错。
public interface MyInterface {
void firstTargetMethod();
}
@Service
public class BeanC implements MyInterface{
public void firstTargetMethod(){
log.info("BeaC + firstTargetMethod");
}
}
@RestController
public class BeanController {
@Autowired
private MyInterface myInterface;
}
如果上述的controller注入的是BeanC的话,在启动时会报错的,但是CGLIB代理是没问题
2.代理对象执行过程
上述主要讲述了代理对象在启动时完成加载的主要流程,这一节讲述代理对象在执行过程中如何实现增强的。 为了更好地理解代理对象执行的过程,我分别从横向和纵向来展现代理对象执行的流程。实际上整个执行过程和栈的特性一致,我们都知道栈是先进后出的方式。代理对象执行切面实际上也是这样的,高优先级的切面的before逻辑最先执行,after逻辑最后执行。
基于上述的执行方式,举出如下的例子:
MethodInterceptor methodInterceptor1 = invocation -> {
System.out.println("Before 1 ....");
Object obj = invocation.proceed();
System.out.println("After 1 ....");
return obj;
};
MethodInterceptor methodInterceptor2 = invocation -> {
System.out.println("Before 2 ....");
Object obj = invocation.proceed();
System.out.println("After 2 ....");
return obj;
};
对于这两个通知的话,如果methodInterceptor1的优先级比methodInterceptor2高的话,那整个执行的输出的结果就是:
Before 1 ....
Before 2 ...
目标输出
After 2 ....
After 1 ....
基于整个执行流程,对其整个执行链路通过图片的方式呈现出来,便于大家理解。如何去实现这样的过程,我想大家最能想到的就是递归方式,Spring也是通过递归的方式去实现的。

public class MyInvocation implements MethodInvocation {
private final Object targetObject;
private final Method method;
private final List<MethodInterceptor> methodInterceptorList;
private final Object[] arguments;
public MyInvocation(Object targetObject, Method method, List<MethodInterceptor> methodInterceptorList, Object[] arguments) {
this.targetObject = targetObject;
this.method = method;
this.methodInterceptorList = methodInterceptorList;
this.arguments = arguments;
}
public static void main(String[] args) throws Throwable {
//构建目标类的对象
TargetClass targetClass = new TargetClass();
//构建目标方法
Method targetMethod = targetClass.getClass().getMethod("targetMethod", null);
//构建通知List
MethodInterceptor methodInterceptor1 = invocation -> {
System.out.println("before 1 ...");
Object obj = invocation.proceed();
System.out.println("after 1 ...");
return obj;
};
MethodInterceptor methodInterceptor2 = invocation -> {
System.out.println("before 2 ...");
Object obj = invocation.proceed();
System.out.println("after 2 ...");
return obj;
};
List<MethodInterceptor> methodInterceptors = List.of(methodInterceptor1, methodInterceptor2);
MyInvocation myInvocation = new MyInvocation(targetClass, targetMethod, methodInterceptors, null);
myInvocation.proceed();
}
@Nonnull
@Override
public Method getMethod() {
return null;
}
@Nonnull
@Override
public Object[] getArguments() {
return new Object[0];
}
private int index;
@Nullable
@Override
public Object proceed() throws Throwable {
//如果没有切面(size=0)或者前面执行完了 就得执行连接点方法(目标类方法)
int size = this.methodInterceptorList.size();
if(index == size){
return this.method.invoke(this.targetObject, null);
}
MethodInterceptor methodInterceptor = this.methodInterceptorList.get(index ++);
return methodInterceptor.invoke(this);
}
@Nullable
@Override
public Object getThis() {
return null;
}
@Nonnull
@Override
public AccessibleObject getStaticPart() {
return null;
}
public static class TargetClass {
public void targetMethod() {
System.out.println("TargetClass + targetMethod");
}
}
}
2.1CGLIB代理执行
首先看看代理类结构,我只把和目标相关的提取出来的,整个代理类很复杂:
public class BeanC$$EnhancerBySpringCGLIB$$40185bf0 extends BeanC implements SpringProxy, Advised, Factory {
private static final Method CGLIB$firstTargetMethod$0$Method;
private static final MethodProxy CGLIB$firstTargetMethod$0$Proxy;
static void CGLIB$STATICHOOK5() {
CGLIB$firstTargetMethod$0$Method = ReflectUtils.findMethods(new String[]{"firstTargetMethod", "()V"}, (var1 = Class.forName("com.dev.wizard.bean.BeanC")).getDeclaredMethods())[0];
CGLIB$firstTargetMethod$0$Proxy = MethodProxy.create(var1, var0, "()V", "firstTargetMethod", "CGLIB$firstTargetMethod$0");
}
final void CGLIB$firstTargetMethod$0() {
super.firstTargetMethod();
}
//代理中增强后的目标方法
public final void firstTargetMethod() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$firstTargetMethod$0$Method, CGLIB$emptyArgs, CGLIB$firstTargetMethod$0$Proxy);
} else {
super.firstTargetMethod();
}
}
}
-
调用增强方法:代理对象执行增强方法时,首先去获取CGLIB$CALLBACK_0; CGLIB$CALLBACK_0是代理对象中的回调属性,在生成代理对象前塞值。代理对象中有很多CallBack对象,我们重点关注的是CGLIB$CALLBACK_0,Spring中的具体实现是DynamicAdvisedInterceptor,其作用是为了构建切面的调用链对象;

-
AspectJAroundAdvice -
AspectJAfterThrowingAdvice -
AspectJAfterAdvice
下述两种通知没有实现该接口:
-
AspectJAfterReturningAdvice -
AspectJMethodBeforeAdvice
实现了该接口即为环绕通知,为了让该两种通知实现了MethodInterceptor接口使用了适配器模式。
-
AspectJMethodBeforeAdvice ==> MethodBeforeAdviceAdapter(适配器) ==> MethodBeforeAdviceInterceptor -
AspectJAfterReturningAdvice ==> AfterReturningAdviceAdapter (适配器) ==> AfterReturningAdviceInterceptor
这种转换是在调用的时候产生。并非在生成代理对象时产生。当然Spring只会在第一次调用的时候会进行通知转换,然后将转换后的通知List存在ConcurrentHashMap中已达到缓存的目的,其中key为method对象。
通过适配器模式进行通知转换
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);
}
//如果是上述三种的通知进行统一转换成MethodInterceptor环绕通知
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[0]);
}
我理解这个为了解决历史的兼容性的问题 但是兼容写法值得学习
另外AdvisorAdapter扩展还可以帮助工程师实现自己的切面方式
-
构建调用链对象:根据callback对象去构建切面调用链对象,在构建该链对象之前需要获取到通知链,也就是上述所说的MethedInterceptorList,构建完成后调用processed方法实现链式递归调用,源码见 org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept
-
实现调用:根据调用链对象(CglibMethodInvocation)实现调用; 源码如下:
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
//与上述我自己的实现方法相比,小编是从0开始的,spring是从-1开始的
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
//调用目标类的切点方法,也就是待增强的方法
return invokeJoinpoint();
}
//此处spring采用++this.currentInterceptorIndex,小编使用index++,那是因为index是从0开始的,currentInterceptorIndex是从-1开始的
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
//执行下一个通知时不忘记把调用链对象(this)传递
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
//执行目标方法
//当methodProxy不为空时 结合目标对象实现无反射调用
//当methodProxy为空时 使用反射调用
protected Object invokeJoinpoint() throws Throwable {
if (this.methodProxy != null) {
try {
return this.methodProxy.invoke(this.target, this.arguments);
}
catch (CodeGenerationException ex) {
logFastClassGenerationFailure(this.method);
}
}
return super.invokeJoinpoint();
}
在代理对象执行的过程中,你会发现在任何一个代理对象中,第一个切面始终是ExposeInvocationInterceptor,它也是一个环绕通知,如下图所示:

@Override
@Nullable
public Object invoke(MethodInvocation mi) throws Throwable {
//这个第一次get的时候通常为null
MethodInvocation oldInvocation = invocation.get();
//将当前调用链塞到ThreadLocal中方便后续方法获取
invocation.set(mi);
try {
return mi.proceed();
}
finally {
invocation.set(oldInvocation);
}
}
在生成动态代理对象字节文件时,private,final修饰的方式是不会生成对象的代理方法的,但是对于该类中的其他方法均会生成代理对象,即使不满足切点要求均会生成对应的代理方法和methodProxy对象。
2.2 JDK代理执行过程
public final class $Proxy61 extends Proxy implements MyInterface, SpringProxy, Advised, DecoratingProxy {
public final void firstTargetMethod() {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
上述h是父类Proxy中的变量 protected InvocationHandler h;
具体实现类是来自org.springframework.aop.framework.JdkDynamicAopProxy
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
}

JDK中使用ReflectiveMethodInvocation通过反射的方式调用目标方法,源码见:org.springframework.aop.framework.ReflectiveMethodInvocation#proceed
3. 总结
本文主要分两个方面讲述动态代理,一是创建代理对象,二是代理对象的执行。创建代理对象主要是判断是否有切面匹配Bean中的方法,如果有即为该Bean创建代理对象。代理对象的执行主要通过DynamicAdvisedInterceptor(CGLIB)和JdkDynamicAopProxy(JDK)回调函数,该函数创建了调用链对象,调用链对象中包含该方法的所有通知,执行调用链对象的proceed方法实现递归调用完成代理对象的执行过程。
原文始发于微信公众号(处女座码农):Spring动态代理的背后原理
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/251631.html