Spring AOP中CGLIB代理对象增强通知执行原理

前置博文:
Spring AOP中如何为Bean创建代理?
Spring AOP中是如何注册Advisor的?
Spring AOP如何为目标方法创建拦截器链?
Spring AOP拦截器调用的实现
Spring AOP中CGLIB代理对象增强通知执行原理

或者换句话说,当我们定义了切面、pointcut以及advice后,这些是如何对我们的目标对象生效的。本文这里以CglibAopProxy为例说明,至于jdk动态代理可以自行查看JdkDynamicAopProxy的invoke方法(大概流程与CGLIB代理一致)。

cglib代理为目标对象增强执行是通过CglibAopProxy的callbacks进行拦截处理的,入口在DynamicAdvisedInterceptor的intercept方法;
jdk动态代理则是通过JdkDynamicAopProxy#invoke方法进行拦截处理,流程与CGLIB类似,都是获取到拦截器链遍历调用。

继续结合前面博文分析,我们从代码角度解读其执行流程。假设我们有切面如下:

@Aspect
@Component
public class LogAspect {

//注意这里我们没有限制方法修饰符 ,以 * 表示匹配所有
    @Pointcut("execution(* com.recommend.controller.*.*(..))")
    public void logPointCut() {
    }

    @Before(value = "execution(* com.recommend.controller.*.*(..))")
    public void beforeMethod(JoinPoint point)  {
        System.out.println(" before(Joinpoint point)");
    }

    @After("logPointCut()")
    public void afterMethod(JoinPoint point)  {
        System.out.println(" afterMethod(Joinpoint point)");
    }

    @AfterReturning(pointcut = "logPointCut()",returning="result")
    public void AfterReturning(JoinPoint point,Object result)  {
        System.out.println(" AfterReturning(Joinpoint point)");
    }
    @AfterThrowing(value = "logPointCut()",throwing="exception")
    public void AfterThrowing(JoinPoint point,Exception exception)  {
        System.out.println(" AfterThrowing(Joinpoint point)");
    }

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        // 执行方法
        Object result = point.proceed();
        // 执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        //异步保存日志
        return result;
    }
}

【1】Spring AOP执行前的数据状态

如下图所示在请求执行流程时,这里就意味着反射调用目标方法。如果没有AOP增强,那么就会直接进入到我们的方法中。

在这里插入图片描述
从执行栈可以看到,这里进入了代理方法。

在这里插入图片描述


我们先看一下此时代理对象的数据状态。

当前对象是一个代理对象,有七个回调实例,参数为目标方法参数BindingAwareModelMap.

CGLIB$CALLBACK_0 = {CglibAopProxy$DynamicAdvisedInterceptor@9685} 
CGLIB$CALLBACK_1 = {CglibAopProxy$StaticUnadvisedInterceptor@9703} 
CGLIB$CALLBACK_2 = {CglibAopProxy$SerializableNoOp@9704} 
CGLIB$CALLBACK_3 = {CglibAopProxy$StaticDispatcher@9705} 
CGLIB$CALLBACK_4 = {CglibAopProxy$AdvisedDispatcher@9707} 
CGLIB$CALLBACK_5 = {CglibAopProxy$EqualsInterceptor@9708} 
CGLIB$CALLBACK_6 = {CglibAopProxy$HashCodeInterceptor@9709} 

在这里插入图片描述
其中callback 1 3实例对象维护了target,我们实际的目标实例对象。

在这里插入图片描述
而callback 0 4 5 6实例对象维护了advised也就是ProxyFactory实例。

在这里插入图片描述
我们具体看一下advised包含了哪些数据。如下图所示其包含了DefaultAopProxyFactory、targetSource、DefaultAdvisorChainFactory、methodCache以及advisors等。

其中我们需要注意methodCache与advisors。前者维护了方法与增强器/拦截器链,后者维护了目标方法所应用的顾问/增强器。

此时(还没有进入DynamicAdvisedInterceptor#intercept执行我们目标方法的增强),methodCache只有一个toString。这个我们无需理会,只需要关注接下来的变动。

在这里插入图片描述
如下所示,这些advisors除了第一个,其他5个其实就对应了我们切面定义的五个增强通知方法。

0 = {ExposeInvocationInterceptor$1@9902} "org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR"
1 = {InstantiationModelAwarePointcutAdvisorImpl@9903} "InstantiationModelAwarePointcutAdvisor: expression [logPointCut()]; advice method [public void com.recommend.config.LogAspect.AfterThrowing(org.aspectj.lang.JoinPoint,java.lang.Exception)]; perClauseKind=SINGLETON"
2 = {InstantiationModelAwarePointcutAdvisorImpl@9904} "InstantiationModelAwarePointcutAdvisor: expression [logPointCut()]; advice method [public void com.recommend.config.LogAspect.AfterReturning(org.aspectj.lang.JoinPoint,java.lang.Object)]; perClauseKind=SINGLETON"
3 = {InstantiationModelAwarePointcutAdvisorImpl@9905} "InstantiationModelAwarePointcutAdvisor: expression [logPointCut()]; advice method [public void com.recommend.config.LogAspect.afterMethod(org.aspectj.lang.JoinPoint)]; perClauseKind=SINGLETON"
4 = {InstantiationModelAwarePointcutAdvisorImpl@9906} "InstantiationModelAwarePointcutAdvisor: expression [logPointCut()]; advice method [public java.lang.Object com.recommend.config.LogAspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; perClauseKind=SINGLETON"
5 = {InstantiationModelAwarePointcutAdvisorImpl@9907} "InstantiationModelAwarePointcutAdvisor: expression [execution(* com.recommend.controller.*.*(..))]; advice method [public void com.recommend.config.LogAspect.beforeMethod(org.aspectj.lang.JoinPoint)]; perClauseKind=SINGLETON"

【2】DynamicAdvisedInterceptor

AOP的执行流程首先从DynamicAdvisedInterceptor的intercept方法开始。该类是CglibAopProxy的内部类,实现了MethodInterceptor接口,维护了一个AdvisedSupport advised常量。

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 {
	
	// 如果exposeProxy设置为true,则将proxy暴露给AopContext
		if (this.advised.exposeProxy) {
			// Make invocation available if necessary.
			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...
		// 获取目标对象,如我们的homeController
		target = targetSource.getTarget();
		Class<?> targetClass = (target != null ? target.getClass() : null);

		// 获取目标方法的拦截器链 很重要
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
		Object retVal;
		
		// 检查是否可以直接反射调用目标方法,注意哦,这里判断了public 修饰符
		if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
			// We can skip creating a MethodInvocation: just invoke the target directly.
			// Note that the final invoker must be an InvokerInterceptor, so we know
			// it does nothing but a reflective operation on the target, and no hot
			// swapping or fancy proxying.
			Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
			// 直接调用目标方法
			retVal = methodProxy.invoke(target, argsToUse);
		}
		else {
			// We need to create a method invocation...
			// 如果我们有增强Advice,那么就会从这里开始执行
			retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
		}
		retVal = processReturnType(proxy, target, method, retVal);
		return retVal;
	}
	finally {
		if (target != null && !targetSource.isStatic()) {
			targetSource.releaseTarget(target);
		}
		if (setProxyContext) {
			// Restore old proxy.
			AopContext.setCurrentProxy(oldProxy);
		}
	}
}

这里我们需要关注两点:拦截器的获取与执行。即如下两个入口。

  • List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
  • retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

① 获取拦截器链

这里来到了AdvisedSupport的getInterceptorsAndDynamicInterceptionAdvice方法。方法很简洁,首先从缓存里面获取,如果有直接返回,否则调用advisorChainFactory获取然后放到methodCache缓存里面。

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
	// 包装得到方法的缓存key
	MethodCacheKey cacheKey = new MethodCacheKey(method);
	
	// 从本地缓存Map<MethodCacheKey, List<Object>> methodCache里面获取
	List<Object> cached = this.methodCache.get(cacheKey);
	if (cached == null) {
	// 进一步获取拦截器链
		cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
				this, method, targetClass);
				// 放到缓存里面
		this.methodCache.put(cacheKey, cached);
	}
	return cached;
}

这里的advisorChainFactoryDefaultAdvisorChainFactory。该类是真正为目标方法创建拦截器链的方法。这里不做介绍更多详情参考博文Spring AOP如何为目标方法创建拦截器链?

获取到拦截器执行链后,我们advised的methodCache就发生了变化如下所示:

在这里插入图片描述

② 拦截器链的执行

其实看到上图,我们心里已经有了推测。其会逐个调用链中的每个拦截器实现目标方法的增强。我们接下来从源码角度追踪流程的实现。

retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, 
chain, methodProxy).proceed();

在这里插入图片描述

这里创建了CglibMethodInvocation实例对象然后调用了proceed方法。CglibMethodInvocation是CglibAopProxy的静态内部类继承自ReflectiveMethodInvocation

如下代码所示其有参构造方法调用了父类ReflectiveMethodInvocation 的有参构造方法。其proceed方法直接调用了父类的proceed方法。

private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

	@Nullable
	private final MethodProxy methodProxy;

	public CglibMethodInvocation(Object proxy, @Nullable Object target, Method method,
			Object[] arguments, @Nullable Class<?> targetClass,
			List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {

		super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);

		// Only use method proxy for public methods not derived from java.lang.Object
		this.methodProxy = (Modifier.isPublic(method.getModifiers()) &&
				method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) &&
				!AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method) ?
				methodProxy : null);
	}

	@Override
	@Nullable
	public Object proceed() throws Throwable {
		try {
			return super.proceed();
		}
		//...
	}

	//...
}

我们看下其父类ReflectiveMethodInvocation的proceed方法。

public Object proceed() throws Throwable {
	// We start with an index of -1 and increment early.
	// 判断是否需要反射调用目标方法,即拦截器链执行完
	if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
		return invokeJoinpoint();
	}

// 获取下一个拦截器
	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.
		return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
	}
}

这里首先判断拦截器是否执行完,如果执行完则反射调用目标方法。否则获取下一个拦截器进行判断、调用。

这里第一个拦截器是ExposeInvocationInterceptor。

我们看下其invoke方法。该方法首先将当前的MethodInvocation 对象(也就是CglibMethodInvocation实例)放到ThreadLocal<MethodInvocation> invocation中,然后调用MethodInvocation 对象的proceed方法,最后又恢复了invocation。

public Object invoke(MethodInvocation mi) throws Throwable {
	MethodInvocation oldInvocation = invocation.get();
	invocation.set(mi);
	try {
		return mi.proceed();
	}
	finally {
		invocation.set(oldInvocation);
	}
}

那么CglibMethodInvocation实例的proceed方法只是直接调用了父类ReflectiveMethodInvocation的proceed方法,将会获取到下一个拦截器。


第二个拦截器是AspectJAfterThrowingAdvice

如下所示其调用mi.proceed(),那么又会来到ReflectiveMethodInvocation的proceed方法,将会获取到下一个拦截器。

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
	try {
		return mi.proceed();
	}
	// 假设抛出了异常,那么这里会尝试反射调用我们切面的增强方法
	catch (Throwable ex) {
		if (shouldInvokeOnThrowing(ex)) {
			invokeAdviceMethod(getJoinPointMatch(), null, ex);
		}
		throw ex;
	}
}

第三个拦截器是AfterReturningAdviceInterceptor。

其方法如下所示,首先调用mi.proceed(),然后调用advice的afterReturning方法。

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
// CglibMethodInvocation.proceed--ReflectiveMethodInvocation#proceed
	Object retVal = mi.proceed();
	this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
	return retVal;
}

其中mi.proceed方法又会触发CglibMethodInvocation.proceed--ReflectiveMethodInvocation#proceed流程得到下一个拦截器。

而AspectJAfterReturningAdvice的afterReturning如下所示。

@Override
public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
	if (shouldInvokeOnReturnValueOf(method, returnValue)) {
		// 这里会触发我们切面的增强方法
		invokeAdviceMethod(getJoinPointMatch(), returnValue, null);
	}
}

第四个拦截器是AspectJAfterAdvice。

首先是mi.proceed();流程,然后触发了invokeAdviceMethod方法。

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
	try {
		return mi.proceed();
	}
	finally {
	// 尝试反射调用我们切面的增强方法
		invokeAdviceMethod(getJoinPointMatch(), null, null);
	}
}

第五个拦截器是AspectJAroundAdvice方法。

其invoke方法如下所示尝试拿到ProceedingJoinPointJoinPointMatch 后直接反射调用我们切面的增强方法。

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
	if (!(mi instanceof ProxyMethodInvocation)) {
		throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
	}
	ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
	ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
	JoinPointMatch jpm = getJoinPointMatch(pmi);

	// 这个与上面的invokeAdviceMethod(getJoinPointMatch(), null, null);不一样了哦
	return invokeAdviceMethod(pjp, jpm, null, null);
}

从这里也可以看出around方法中,我们写得前置逻辑是要优先于before的增强的。而invokeAdviceMethod(pjp, jpm, null, null)方法首先会进行参数的绑定然后走到了invokeAdviceMethodWithGivenArgs(Object[] args)方法中进行增强方法的反射调用。

在这里插入图片描述
在我们增强around方法中会触发point.proceed();的调用。其将会触发methodInvocation到ReflectiveMethodInvocation的proceed流程。然后获取到下一个拦截器。

// MethodInvocationProceedingJoinPoint#proceed()
@Override
public Object proceed() throws Throwable {
	return this.methodInvocation.invocableClone().proceed();
}

第六个拦截器是MethodBeforeAdviceInterceptor。

其invoke方法如下所示其首先反射调用我们的增强方法,然后触发mi.proceed()尝试获取下一个拦截器,但是目前已经是最后一个拦截器将会触发invokeJoinpoint方法的调用。

@Override
public Object invoke(MethodInvocation mi) throws Throwable {

	// 这里会触发我们的before增强方法
	this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
	
	//这里会触发ReflectiveMethodInvocation的proceed方法
	return mi.proceed();
}

CglibAopProxy的invokeJoinpoint

如果执行完拦截器,那么ReflectiveMethodInvocation的proceed方法中将会触发CglibAopProxy的invokeJoinpoint方法。

protected Object invokeJoinpoint() throws Throwable {
	if (this.methodProxy != null) {
		return this.methodProxy.invoke(this.target, this.arguments);
	}
	else {
		return super.invokeJoinpoint();
	}
}

这里的methodProxy如下所示,其将会触发我们真正目标方法(本文这里是HomeController的index方法)的执行。
在这里插入图片描述
方法执行栈如下所示,从invoke到了index。

在这里插入图片描述


around增强方法的后半部分

即我们around增强方法中 Object result = point.proceed();之后代码的执行。之后就结束around增强。

在这里插入图片描述


AspectJAfterAdvice的finally

这里会触发我们after增强代码。

AspectJAfterAdvice


AfterReturningAdviceInterceptor后半部分

如下图所示,这里将会触发AspectJAfterReturningAdviceafterReturning方法,即,将会触发我们自己的@AfterReturning增强。
在这里插入图片描述


AspectJAfterThrowingAdvice的catch

如果上面过程中抛出了异常,那么接下里会触发AspectJAfterThrowingAdvice的catch逻辑。即,将会触发我们 @AfterThrowing增强。

如果没有抛出异常,继续返回。

public Object invoke(MethodInvocation mi) throws Throwable {
	try {
		return mi.proceed();
	}
	catch (Throwable ex) {
		if (shouldInvokeOnThrowing(ex)) {
			invokeAdviceMethod(getJoinPointMatch(), null, ex);
		}
		throw ex;
	}
}

ExposeInvocationInterceptor的finally

最终返回到了ExposeInvocationInterceptor的finally方法块。其恢复invocation状态,然后return。

在这里插入图片描述

接下来就是CglibAopProxy.DynamicAdvisedInterceptor的intercept方法后面部分了,其将会尝试处理返回结果类型,然后return retVal。

在这里插入图片描述

再之后,就交给了springMVC的流程。

在这里插入图片描述

【3】增加了引入通知

如下所示,我们增加了引入通知。

@Aspect
@Component
public class LogAspect {

    @DeclareParents(value = "com.recommend.controller.HomeController",defaultImpl=MyDeclareImpl.class)
    private MyDeclareService myDeclareService;
    //...
}    

此时我们的执行器链如下所示:

在这里插入图片描述

DelegatePerTargetObjectIntroductionInterceptor如下所示:

在这里插入图片描述

这里我们看一下DelegatePerTargetObjectIntroductionInterceptor的invoke方法。

public Object invoke(MethodInvocation mi) throws Throwable {
// 校验目标方法是否为引入通知感兴趣的接口类型的方法
	if (isMethodOnIntroducedInterface(mi)) {
	// 获取增强服务类
		Object delegate = getIntroductionDelegateFor(mi.getThis());

		// Using the following method rather than direct reflection,
		// we get correct handling of InvocationTargetException
		// if the introduced method throws an exception.
		// 调用增强服务类的增强方法
		Object retVal = AopUtils.invokeJoinpointUsingReflection(delegate, mi.getMethod(), mi.getArguments());

		// Massage return value if possible: if the delegate returned itself,
		// we really want to return the proxy.
		if (retVal == delegate && mi instanceof ProxyMethodInvocation) {
			retVal = ((ProxyMethodInvocation) mi).getProxy();
		}
		// 返回结果
		return retVal;
	}

// 只是 mi.proceed();
	return doProceed(mi);
}

也就是说首先判断一下目标方法所在类是否为引入通知感兴趣的服务类,如果是则反射调用增强服务类的增强方法,如果不是直接交给mi.proceed();

从这里也可以看出引入通知是在不改变目标类的基础上为其增加方法业务逻辑,这种手段可以实现多继承的思想。

这种手段可以应用在动态为某批服务增加方法的场景,区别于某批服务实现了同一接口,这种更为灵活。

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值