文章目录
前言
在第五章 SpringBoot源码解读与原理分析(十六)SpringBoot的AOP支持 中,我们通过一个例子学习了SpringBoot整合AOP的用法,代码如下:
代码清单1
// 组件类:DemoService.java
@Service
public class DemoService {
public void test() {
System.out.println("DemoService.test run ......");
}
}
// 切面类:DemoServiceAspect.java
@Aspect
@Component
public class DemoServiceAspect {
@Before("execution(public * com.xiaowd.springboot.aop.test01.*.*(..))")
public void beforeTest() {
System.out.println("DemoServiceAspect.beforeTest run ......");
}
}
// 主启动类:Test01App.java
@SpringBootApplication
@EnableAspectJAutoProxy
public class Test01App {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Test01App.class, args);
context.getBean(DemoService.class).test();
}
}
执行main方法,控制台打印出以下内容:
DemoServiceAspect.beforeTest run ......
DemoService.test run ......
可见,AOP代理已经生效。本节来研究下当DemoService的test
方法执行时,内部执行了哪些重要环节的操作。
9.6 代理对象的底层执行逻辑
9.6.1 DemoService#test
将断点打在DemoService的test
方法上,Debug启动后发现程序会运行到CglibAopProxy的内部类DynamicAdvisedInterceptor的intercept
方法上。
代码清单2:CglibAopProxy.java
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
private final AdvisedSupport advised;
public DynamicAdvisedInterceptor(AdvisedSupport advised) {
this.advised = advised;
}
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
// 如果在@EnableAspectJAutoProxy注解上配置了exposeProxy属性为true
// 则会把当前代理对象放入AOP上下文中
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// 从TargetSource中提取目标对象
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 根据当前执行的方法,获取要执行的增强器,并以列表返回(链的思想)
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// 如果没有要执行的增强器,则直接执行目标方法
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
} else {
// 否则,构造增强器链,执行增强器的逻辑
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
} // finally ......
}
}
由 代码清单2 可知,intercept
有两个核心步骤:获取增强器链、执行增强器链。
9.6.2 获取增强器链
代码清单3:AdvisedSupport.java
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
MethodCacheKey cacheKey = new MethodCacheKey(method);
List<Object> cached = this.methodCache.get(cacheKey);
if (cached == null) {
// 缓存增强器链
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached);
}
return cached;
}
由 代码清单4 可知,获取增强器链的核心逻辑是advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice
。同时,获取的增强器链最终会被缓存起来,后续再执行被增强的方法时会直接拿到该增强器链,而无需再次重新解析。
9.6.2.1 前置准备
代码清单5:DefaultAdvisorChainFactory.java
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
// 初始化一个AdvisorAdapterRegistry
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
Advisor[] advisors = config.getAdvisors();
List<Object> interceptorList = new ArrayList<>(advisors.length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
Boolean hasIntroductions = null;
// ......
}
由 代码清单5 可知,getInterceptorsAndDynamicInterceptionAdvice
首先会初始化一个AdvisorAdapterRegistry对象,意为增强器适配器的注册器*。它的主要作用是将AspectJ类型的增强器转换为方法拦截器MethodInterceptor并返回。
9.6.2.2 匹配增强器
代码清单6:DefaultAdvisorChainFactory.java
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
// 前置准备 ......
for (Advisor advisor : advisors) {
if (advisor instanceof PointcutAdvisor) {
// 此处获取的是AspectJ形式的通知方法封装
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
// 根据通知方法上的切入点表达式,判断是否可以匹配
// 当前要执行的目标对象所属类
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
boolean match;
// 引介匹配
if (mm instanceof IntroductionAwareMethodMatcher) {
if (hasIntroductions == null) {
hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
}
match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
} else {
// 方法匹配
match = mm.matches(method, actualClass);
}
// ......
}
} // else ......
}
return interceptorList;
}
由 代码清单6 可知,循环匹配增强器会把取出的增强器集合依次与当前正在调用的目标对象进行匹配,匹配方法借助MethodMatcher进行。
9.6.2.3 匹配后的处理
代码清单7:DefaultAdvisorChainFactory.java
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
// 前置处理 ......
// 匹配增强器 ......
// 匹配后的处理
if (match) {
// 封装MethodInterceptor
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
if (mm.isRuntime()) {
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
} else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
// ......
}
由 代码清单7 可知,匹配到增强器后,开始封装方法拦截器MethodInterceptor。
注意这里有一个MethodMatcher的isRuntime
判断,这是判断MethodMatcher是否是动态的。通常情况下,MethodMatcher都是静态匹配器,但如果MethodMatcher被设置为动态匹配器,则每次调用匹配方法时,可以提前获取方法调用的参数值列表。
代码清单8:MethodMatcher.java
boolean matches(Method method, Class<?> targetClass);
boolean matches(Method method, Class<?> targetClass, Object... args);
由 代码清单8 可知,静态匹配器只会做基本的方法匹配,而动态匹配器可以提前获取方法调用的参数值列表并进行深度匹配。
匹配完成后,获得一个方法拦截器的List集合。
9.6.2.4 其他增强器的处理
代码清单9:DefaultAdvisorChainFactory.java
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
// 前置处理 ......
for (Advisor advisor : advisors) {
if (advisor instanceof PointcutAdvisor) {
// 处理AspectJ类型的增强器 ......
} else if (advisor instanceof IntroductionAdvisor) {
// 处理引介增强器
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
} else {
// 处理其他类型的增强器
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
由 代码清单9 可知,除了处理AspectJ类型的增强器,还要处理引介增强器以及其他类型的增强器。
经过getInterceptorsAndDynamicInterceptionAdvice
方法的处理后,当前目标对象要执行的方法被全部筛选出来,接下来的环节就是构建方法执行器。
9.6.3 执行增强器
回到DynamicAdvisedInterceptor的intercept
方法(代码清单2),如果获取到了增强器链,则执行CglibMethodInvocation的proceed
方法。
CglibMethodInvocation的proceed
方法只是单纯地调用父类ReflectiveMethodInvocation的proceed
方法。
代码清单10:ReflectiveMethodInvocation.java
protected final List<?> interceptorsAndDynamicMethodMatchers;
private int currentInterceptorIndex = -1;
public Object proceed() throws Throwable {
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
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 {
return proceed();
}
} else {
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
下面借助Debug来拆解该方法。
9.6.3.1 执行proceed方法
第一次进入proceed
方法,首先执行第一个if判断,比对当前已经执行过的增强器在增强器链的下标位置。
此时Debug可以发现currentInterceptorIndex=-1,判断-1≠2-1,因此不进入invokeJoinpoint
方法,继续往下执行。
9.6.3.2 下标值++
接下来执行以下代码:
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
注意this.currentInterceptorIndex变量执行了一次自增操作,自增后currentInterceptorIndex值为0。
9.6.3.3 执行第一个增强器
由上图可知,interceptorsAndDynamicMethodMatchers.get(0)
得到的增强器的类型是ExposeInvocationInterceptor.ADVISOR,经过if判断会进入else结构中,执行其invoke
方法。
代码清单11:ExposeInvocationInterceptor.java
public Object invoke(MethodInvocation mi) throws Throwable {
MethodInvocation oldInvocation = invocation.get();
invocation.set(mi);
try {
return mi.proceed();
} finally {
invocation.set(oldInvocation);
}
}
由 代码清单11 可知,invoke
方法保存好MethodInvocation之后,继续向下执行proceed
方法。
9.6.3.4 再次执行proceed
方法
通过Debug发现,程序回到了 代码清单10 的第一步,不同的是此时currentInterceptorIndex的值不再是-1,而是0。判断0≠2-1,因此也不进入invokeJoinpoint
方法,继续向下执行增强器的逻辑。
9.6.3.5 执行第二个增强器
下一个要执行的增强器是MethodBeforeAdviceInterceptor。
代码清单12:MethodBeforeAdviceInterceptor.java
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}
代码清单13:AspectJMethodBeforeAdvice.java
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
代码清单14:AbstractAspectJAdvice.java
protected Object invokeAdviceMethod(
@Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex)
throws Throwable {
return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
}
protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
Object[] actualArgs = args;
if (this.aspectJAdviceMethod.getParameterCount() == 0) {
actualArgs = null;
}
try {
// 反射执行通知方法
ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
} // catch ......
}
由 代码清单12-14 可知,MethodBeforeAdviceInterceptor会调用advice的before
方法,一直调用到invokeAdviceMethodWithGivenArgs
方法,最终借助反射调用AspectJ切面类的通知方法。
通知方法执行完毕后,继续执行MethodInvocation的proceed
方法,又回到了 代码清单10 的第一步。
9.6.3.6 执行目标对象方法
再次回到proceed
方法后,此时currentInterceptorIndex的值为1,判断1=2-1,进入invokeJoinpoint
方法,该方法会借助反射执行目标对象的目标方法。
9.6.3.7 流程小结
代理对象的底层执行逻辑:利用一个全局索引值决定每次执行的拦截器,当所有拦截器都执行完时,索引值刚好等于size()-1,此时就可以执行真正的目标方法。
9.6.4 JDK动态代理的执行底层
默认情况下,SpringBoot使用Cglib动态代理。而要想在SpringBoot 2.x中激活JDK动态代理,必须在application.properties中显式配置spring.aop.proxy-target-class=false,才会默认禁用Cglib的配置,激活JDK动态代理。
JDK动态代理的核心类是JdkDynamicAopProxy,它的invoke
如下:
代码清单15:JdkDynamicAopProxy.java
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
// 处理一些不代理的情况
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// equals方法不代理
return equals(args[0]);
} else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// hashCode方法不代理
return hashCode();
} else if (method.getDeclaringClass() == DecoratingProxy.class) {
// 方法来自DecoratingProxy接口的不代理
return AopProxyUtils.ultimateTargetClass(this.advised);
} else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// 目标对象本身实现了Advised接口的不代理
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy)
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 根据当前执行的方法获取要执行的增强器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
// 增强器链为空,直接执行目标方法
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
} else {
// 增强器链不为空,执行增强器链
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}
// ......
return retVal;
} // finally ......
}
由 代码清单15 可知,JDK动态代理首先会处理一些不代理的情况(如equals
方法、hashCode
方法等),接着根据当前执行的方法获取要执行的增强器链,如果增强器链为空,直接执行目标方法;如果增强器链不为空,执行增强器链。
可以发现,JDK动态代理的逻辑和CglibAopProxy的实现逻辑是基本一致的,获取增强器链的getInterceptorsAndDynamicInterceptionAdvice
方法是同一个,方法执行器也都是ReflectiveMethodInvocation,其proceed
也是同一个。
······
本节完,更多内容请查阅分类专栏:SpringBoot源码解读与原理分析