7、spring核心源码解析之aop概况流程2

1. 源码分析aop的切面@Aspect解析

​ 前文分析spring ioc源码时,了解到,spring 解析内部注解,通常通过后置处理器进行解析,上文提到 aop注入的后置处理器AnnotationAwareAspectJAutoProxyCreator。在进行切面解析时,其实存在一些父子后置处理器之间的调用关系。

通过源码了解到,@Aspect解析的起点在spring bean的实例化之前AbstractAutoProxyCreator#postProcessBeforeInstantiation方法中。

该方法主要作用是获取spring容器中的切面类,同时将切面和通知存在缓存中,调用链本人通过debug形式打印出来了。

1590298291362

核心代码AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors,在该方法中找到所有的通知并返回。

	protected List<Advisor> findCandidateAdvisors() {
		// Add all the Spring advisors found according to superclass rules.
		//根据父类的规则,找到所有的spring 通知
		List<Advisor> advisors = super.findCandidateAdvisors();
		// Build Advisors for all AspectJ aspects in the bean factory.
		//获取Bean工厂中所有AspectJ切面的通知
		if (this.aspectJAdvisorsBuilder != null) {
			//找到所有的通知,并放入到aspectJAdvisorsBuilder中缓存起来,并返回
			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		}
		return advisors;
	}

aspectJAdvisorsBuilder这个spring内部使用的建造者模式中,会缓存所用容器中的切面,切点,通知等信息。以便后期需要代理类动态增强时进行织入。

在这里插入图片描述

2. 源码分析aop使用jdk代理还是cglib动态代理?

通过源码了解到,spring在实例化bean之后的后置处理器中AbstractAutoProxyCreator#postProcessAfterInitialization方法中,判断是否需要去创建代理对象,找到动态代理创建时需要的通知,并进行代理的创建。

我将本文的重点放在了动态代理的创建,至于如何根据切点找到代理类的通知在此不进行详细描述,这里是创建代理类的调用链。
在这里插入图片描述

	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			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.");
			}
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
            //cgilb动态代理
			return new ObjenesisCglibAopProxy(config);
		}
		else {
            //jdk动态代理
			return new JdkDynamicAopProxy(config);
		}
	}

在本步骤中spring aop创建了动态代理的实例,并且作为bean的实例化对象。那么spring aop具体是如何创建的呢?

3. spring中jdk动态代理和aop动态代理

jdk动态代理:

原理: 利用反射机制生成一个实现代理接口的实现类,在调用具体方法前调用InvokeHandler来处理。

jdk动态代理关键点

1.目标类(被代理类)必须实现接口。

2.自定义InvocationHandler 类实现 InvocationHandler 接口的invoke(),在invoke方法中通过反射完成代理对象的方法调用。在该过程中可以增强我们的目标对象方法调用,即织入我们自定义的逻辑。

关键增强的逻辑一般都在 实现该接口的invoke()方法中。

来看看spring的jdk动态代理源码,分析粒度不大,先简单介绍

熟悉jdk动态代理的同学就知道,jdk动态代理核心就在handler的invoke方法中,即JdkDynamicAopProxy#invoke,该类实现了InvocationHandler 接口的invoke()方法

通过代码可以看到动态织入通知的起点执行逻辑,

注意: 本文最后会有一张时许图详细描述ReflectiveMethodInvocation().proceed()方法中,通知拦截器链的织入过程

	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)) {
				// The target does not implement the equals(Object) method itself.
				return equals(args[0]);
			}
			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				// The target does not implement the hashCode() method itself.
				return hashCode();
			}
			else if (method.getDeclaringClass() == DecoratingProxy.class) {
				// There is only getDecoratedClass() declared -> dispatch to proxy config.
				return AopProxyUtils.ultimateTargetClass(this.advised);
			}
			else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
				// Service invocations on ProxyConfig with the proxy config...
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}

			Object retVal;

			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.
			target = targetSource.getTarget();
			Class<?> targetClass = (target != null ? target.getClass() : null);

			// Get the interception chain for this method.
			//获取此方法的拦截链
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			// Check whether we have any advice. If we don't, we can fallback on direct
			// reflective invocation of the target, and avoid creating a MethodInvocation.
			if (chain.isEmpty()) {
				// 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 = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				// We need to create a method invocation...
				//我们需要创建一个方法调用...
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// Proceed to the joinpoint through the interceptor chain.
				//通过拦截器链进入连接点,该处逻辑与cglib相似,会执行方法拦截intercep。
				retVal = invocation.proceed();
			}

			// Massage return value if necessary.
			Class<?> returnType = method.getReturnType();
			if (retVal != null && retVal == target &&
					returnType != Object.class && returnType.isInstance(proxy) &&
					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
				// Special case: it returned "this" and the return type of the method
				// is type-compatible. Note that we can't help if the target sets
				// a reference to itself in another returned object.
				retVal = proxy;
			}
			else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
				throw new AopInvocationException(
						"Null return value from advice does not match primitive return type for: " + method);
			}
			return retVal;
		}
		finally {
			if (target != null && !targetSource.isStatic()) {
				// Must have come from TargetSource.
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				// Restore old proxy.
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}

cglib动态代理:

原理: 是利用asm,加载代理类的class文件,通过修改其字节码生成子类,覆盖父类的方法,来进行处理。(注意 :目标类方法最好不要声明成final )。

cglib动态代理关键点

1.目标类(被代理类)不需要实现接口。

2.通过增强器类Enhancer创建代理对象,设置织入回调的拦截器链setCallbackFilter,(前置通知等等都是通过此功能实现)。

3.spring aop中有一个关键的内部类,定义了动态通知的拦截CglibAopProxy.DynamicAdvisedInterceptor。

4.spring 的方法拦截链中的每个拦截器都实现了MethodInterceptor接口。

Cglib中的拦截器有点多,可以看一下CglibAopProxy的内部类带有Interceptor的很多。

在这里插入图片描述

拦截器Interceptor很多,但是我们的关注点应该放在动态织入通知的拦截器中DynamicAdvisedInterceptor的invoke方法,看该方法中的方法拦截,其中和jdk动态代理织入的逻辑相似。都调用了new CglibMethodInvocation().proceed()方法。那么通知织入的核心其实在该该类的proceed方法中。

@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 {
				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...
				target = targetSource.getTarget();
				Class<?> targetClass = (target != null ? target.getClass() : null);
				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
				Object retVal;
				// Check whether we only have one InvokerInterceptor: that is,
				// no real advice, but just reflective invocation of the target.
				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...
					//我们需要创建一个方法调用。该方法调用的proceed,就是一个通知动态织入到代理对象的过程
					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);
				}
			}
		}

那就看一下ReflectiveMethodInvocation#proceed是如何执行的

	public Object proceed() throws Throwable {
		// We start with an index of -1 and increment early.
		//我们从索引-1开始并提前增加,直至将所有同通知拦截执行完毕。
		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);
		}
	}

将断点调制该处,我们可以看到通知拦截器链的执行顺序:

afteThrowingr -> afterReturning -> after ->afterAround -> MethodBefore.

至于为什么是这个执行顺序,肯定是有讲究的,但是本人并没有继续深入,有兴趣可以继续追踪一下源码。

在这里插入图片描述

ok,那么它是如何执行通知的链式调用的呢,代码看了,看一张时序图,思路会更加清晰。

4. Advice通知MethodIntercept方法拦截织入代理时序图

在这里插入图片描述

个人觉得,spring的aop实现整体流程涉及到的点很多,但是大体思路并不算很复杂。真正的基础和复杂点是jdk和cgilib如何在jvm运行时动态生成代理类。

上一篇:6、spring核心源码解析之aop概况流程1
[下一篇:8.未完待续]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章