书接上回
在上一篇中,详细介绍了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中的设计模式的使用,以及和其他中间件中使用相同设计模式的相同点和差一点,对于常见的中间件中拦截器的设计的理解会很有帮助
下一篇,开始正是对事物拦截器进行发起总攻