Spring AOP以及事务详解(二)

1 阅读10分钟

书接上回

在上一篇中,详细介绍了Spring是如何对自定义切面通知以及spring内置的事务切面的识别,加载,解析的过程。那本篇主要介绍bean在创建过程中,如果匹配到了符合的通知器时,创建代理,以及在实际执行时,切面通知是如何发挥作用的,以及事物切面是如何发挥作用的。

代理类创建

如上一篇断点如下:自定义的service中方法上添加有 @Transactional 注解(本文重点) 以及被自定义的两个前置通知的方法命中(这个不是本文的重点) ,所以可以发现这里spring创建出了4个interceptor。接着会进入org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy 方法进行创建代理

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {

		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}

		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

		if (proxyFactory.isProxyTargetClass()) {
			// Explicit handling of JDK proxy targets and lambdas (for introduction advice scenarios)
			if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {
				// Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
				for (Class<?> ifc : beanClass.getInterfaces()) {
					proxyFactory.addInterface(ifc);
				}
			}
		}
		else {
			// No proxyTargetClass flag enforced, let's apply our default checks...
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}
        // 获取 Advisor
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		// Use original ClassLoader if bean class not locally loaded in overriding class loader
		ClassLoader classLoader = getProxyClassLoader();
		if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
			classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
		}
        // 根据 ProxyFactory 创建代理类
		return proxyFactory.getProxy(classLoader);
	}

可以看到这里做的事情也很清晰,定义了一个清晰的执行步骤,创建一个代理生成工厂 ProxyFactory , 对ProxyFactory 设置一些配置,之后将 specificInterceptors

适配成Advisor(实际上这一步什么都没做,本身之前解析完之后,已经都是Advisor了),

接着就是通过 proxyFactory.getProxy(classLoader) 开始获取代理了。

proxyFactory.getProxy(classLoader)

public Object getProxy(@Nullable ClassLoader classLoader) {
		return createAopProxy().getProxy(classLoader);
}

可以看到内部又是中转了一道,使用的是 “抽象工厂” 模式,先创建具体的代理工厂,之后根据创建出的代理工厂再创建具体的代理,详细看下吧

protected final synchronized AopProxy createAopProxy() {
		if (!this.active) {
			activate();
		}
		return getAopProxyFactory().createAopProxy(this);
	}

其中 getAopProxyFactory() 其实只有一个实现就是 DefaultAopProxyFactory ,看下createAopProxy(this) ,可以看到这里返回了真正具有创建代理能力的类了

@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (!NativeDetector.inNativeImage() &&
				(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) || ClassUtils.isLambdaClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

到这里算是执行完了 return createAopProxy().getProxy(classLoader); 中的createAopProxy()的部分 之后, 然后通过JdkDynamicAopProxy 或者 ObjenesisCglibAopProxy 的getProxy 开始创建具体的bean的代理,下面详细看下

先看JdkDynamicAopProxy#getProxy(classLoader)

@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		return Proxy.newProxyInstance(determineClassLoader(classLoader), this.proxiedInterfaces, this);
	}

可以看到 JdkDynamicAopProxy 本身就是一个 InvocationHandler ,所以看下

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 {
			// ....... 省略若干行
			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 fall back 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.
				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);
			}
		}
	}

所以逻辑也是非常清晰的

1、获取拦截器chain-其实就是一个list

2、构造一个 MethodInvocation ,然后执行其proceed() ,触发逻辑的执行

下面一步步看,先看下获取拦截器chain,这里解释下,为什么要做这个操作,原因是spring中底层抽象拦截器和MethodInvocation的抽象如下

spring会把方法的执行体抽象成 MethodInvocation ,方法的执行是通过 MethodInvocation.proceed()的方式触发的。所以如果要想使一个无参的方法执行来达到正常反射api的效果比如 method.invoke(instance,args) 的话,则MethodInvocation的内部必要要包含各种元信息(包括instance, method, args 等) 。

而spring底层把拦截器抽象成 MethodInterceptor ,看下其定义,可以看到拦截器只有一个invoke方法,而入参正是上面的MethodInvocation,所以这个套路就很清晰了,拦截器中可以在MethodInvocation入参调用proceed执行前,后,返回之前,或者出现异常,亦或者finally中插入各种想做的逻辑了,甚至都可以忽略MethodInvocation的真正的执行。所以想下环绕通知,前置通知,返回通知,等等都是通过这种方式实现的。而且这种套路在Mybatis的Interceptor以及dubbo的invoker 的设计中都有体现。算是一个中间件中比较常用的套路。好了,相信通过对设计思想的解析之后,带着方向出发,再看下面的源码就不会迷路了

@FunctionalInterface
public interface MethodInterceptor extends Interceptor {

	@Nullable
	Object invoke(@Nonnull MethodInvocation invocation) throws Throwable;

}

上面解释了spring 方法执行体 和 拦截器 的抽象模型和调用关系之后,就明白了为什么还要把Advisor的列表进行转换了吧,就是要把Advisor转成Spring内部最底层真正能运行的组件MethodInterceptor

List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

方法经过层层调用会来到org.springframework.aop.framework.DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice

其实这个方法做的事情也很简单,为了凸显本质,直接删除不相干的代码,只保留最重要的几行代码 , 可以看到就是从配置中取出之前传入的Advisor, 之后对Advisor的PointCut对当前bean再次做校验(之前其实已经做过了)看是否匹配规则,之后就是把Advisor转成MethodInterceptor

@Override
	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, @Nullable Class<?> targetClass) {

		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
		//这里获取配置中所有的Advisor
        Advisor[] advisors = config.getAdvisors();
		List<Object> interceptorList = new ArrayList<>(advisors.length);
	
		for (Advisor advisor : advisors) {
			if (advisor instanceof PointcutAdvisor) {
				// Add it conditionally.
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					//...... 省略 .......
                    // 把advisor适配成MethodInterceptor
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    interceptorList.addAll(Arrays.asList(interceptors));
                    //...... 省略 .......
            }      
               //...... 省略 .......     
		}
		return interceptorList;
	}

下面详细看下转换过程吧

如下所示,就是先获取Advisor的Advice(也能理解,到这一步了,Pointcut已经校验通过了,没什么用了,只需要真正的切面的执行逻辑了,也就是Advice), 然后如果本身就是MethodInteceptor的实现的话,就不用处理,如果不是的话,就尝试通过Adapter(一个转换工具类或者说转换器)把 Advice 转换成一个MethodInterceptor,所以可以看到这里其实是使用了适配器模式,直接在MethodInterceptor内部集成了Advice,所以这样一来,对于一个MethodInterceptor来说就什么都有了,要执行的拦截的逻辑在Advice中,要执行的真正的方法体通过方法入参MethodInvocation传入了,那Spring还剩什么工作呢?编排, 是的,原料都有了,现在spring只需要通过一个编排规则,发动整个流水加工线就行了

@Override
	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);
		}
		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]);
	}

在看编排之前,先简单看下adapter.getInterceptor(advisor) 是怎么玩的,会发现这里只有三个实现类,原因是因为其他类型的Advice已经实现了MethodInterceptor,不需要再做转换了,下面就以前置通知来看下

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

	@Override
	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof MethodBeforeAdvice);
	}

	@Override
	public MethodInterceptor getInterceptor(Advisor advisor) {
		MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
		return new MethodBeforeAdviceInterceptor(advice);
	}

}

很简单,就是把Advice传入MethodBeforeAdviceInterceptor中,构造一个MethodBeforeAdviceInterceptor类型的拦截器,其他两个同理。

当把MethodInteceptor都组装好了之后,再组装一个MethodInvocation,之后Spring就可以通过编排触发真正的执行了

MethodInvocation 的组装

MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);

这里可以看到是创建了一个MethodInvocation的实例ReflectiveMethodInvocation,把需要的原料都传进去了

之后通过invocation.proceed()方法触发了整个逻辑的执行,通过上面的讲解,既然MethodInvocation能通过procceed()无参方法触发原生方法反射的执行,则必然,在proceed的内部执行了方法的反射执行,而在target method执行之前,则必然要执行下其内部持有的拦截器的逻辑,所以MethodInvocation的实现,在其内部肯定已经提前编排好了执行顺存或者说调用规则,能让拦截器的逻辑和target method的逻辑按照定义的顺序正常执行下去。ok,下面就详细分析下,spring是怎么操作的。

在上面构造完成MethodInvocation 之后,接着就是调用其 proceed() 方法了,所有的编排的逻辑应该都在这个方法里有所体现了。注:下面的场景会以一个before前置通知为具体场景分析这个调用链路

@Override
@Nullable
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) {
        // ....... 省略 .......
    }
    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);
    }
}

看下上面这里的逻辑,会发现MethodInvocation的内部有一个Index用于记录当前执行到的Interceptor,如果发现已经执行完了所有的拦截器,那么就会去真正执行Target Method方法。否则就会去执行当前获取到的拦截器,并把当前MethodInvocation作为参数传入拦截器,所以可想而知,对与before前置通知拦截器而言,当他接受到一个MethodInvacation之后,肯定是先执行Advice的逻辑,执行完成之后,无脑通过MethodInvocation.proceed() 把执行权再返回回去,是不是,可看前置通知拦截器的实现

@Override
@Nullable
public Object invoke(MethodInvocation mi) throws Throwable {
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    return mi.proceed();
}

可以看到确实如此。

仔细回想下,这种操作模式,是不是跟tomcat的FilterChain的执行方式很像,在http请求处理真正调用到Servlet的service方法之前,需要先走一遍所有的Filter,tomcat也是通过在内部集成一个Filter列表,以及一个index, 当所有的Filter都执行过之后,会真正走service调用。

而且最重要的这种设计模式还解决了一个问题,因为这种设计模式其实有点像栈,先执行的Interceptor反而最后才会被返回,什么意思呢,就是调用栈是类似如下,和Mybatis的Interceptor的调用栈也是一样的,这是他们的共同的特点。

Interceptor1#before
Interceptor2#before
.....
methodInvocation#invoke
.....
Interceptor2# after
Interceptor2# after

那其实这种模式除了使用这种spring以及tomcat的实现方式以为,还可以怎么实现呢?

那就是mybatis的所使用的实现方式:装饰器模式

通过上面的调用栈也可以看到,最终的调用栈其实就是外层装饰逻辑不断先执行,知道内部持有的是真正的执行体的时候,开始执行真正的业务逻辑,这不就是装饰器模式吗

但同时如果使用装饰器模式的话,调用链路就没这么清晰了,spring这种实现显然更清晰些

总结

这一篇还是在打扫AOP以及事务执行的外围,还没开始真正进行事务的分析,不过基本所有的外围都打扫干净了,目前只剩下直接分析拦截器的逻辑了

本篇呢,更侧重分析spring中的设计模式的使用,以及和其他中间件中使用相同设计模式的相同点和差一点,对于常见的中间件中拦截器的设计的理解会很有帮助

下一篇,开始正是对事物拦截器进行发起总攻