13、Spring源码之Aop拦截器链的原理

 背景:

理清楚这章的内容 先抛出两个问题。

1、通过cglib生成的代理类放入了Spring容器,当我们通过idea开发工具F7 debug进自己写的业务方法时,为什么都会进入CglibAopProxy这个类里面内部类DynamicAdvicedInterceptor的intercept方法呢?

2、我们自己应用程序可能会有N个通知器、 Spring可以控制在我们业务方法 之前、之后、异常。。。等等某个点执行、他们是有严格的顺序的、Spring是如果保证他们的执行顺序呢?


Spring生成cglib代理

如果要解决第一个问题 就要看Spring生成代理类的逻辑

1、上一个章节 已经说明了 Spring什么条件下会生成代理类和生成代理类的入口。主要是AspectjAwareAdvisorAutoProxyCreator.java这个类的功能。

2、创建出工厂对象ProxyFactory、通过getProxy方法、创造出代理对象。值得说明的是ProxyFacroty 继承  ProxyCreatorSupport  继承AdvicedSupport

ProxyFactory proxyFactory = new ProxyFactory();
...		
...
// 使用代理工厂去获取代理对象
return proxyFactory.getProxy(getProxyClassLoader());

3、创建AopProxyFactory工厂对象、创建aop代理  这里实际创造的AopProxyFactory对象是DefaultAopProxyFactory、

    
    // 构造方法
    public ProxyCreatorSupport() {
		this.aopProxyFactory = new DefaultAopProxyFactory();
	}

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

4、判断被代理类  是要jdk代理 还是cglib代理  返回具体某种方式AopProxy对象。这里我们只看ObjenesisCglibAopProxy这个分支、注意这个类是继承AopCglibProxy的。

@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)) {
				// jdk动态代理
				return new JdkDynamicAopProxy(config);
			}
			// cglib动态代理
			return new ObjenesisCglibAopProxy(config);
		}
		return new JdkDynamicAopProxy(config);
	}

5、通过AopProxy生成具体的代理类、这一个方法是最核心的代码逻辑。特别关注下啊callBacks和callBackFilter.

callBacks 数组的第一个元素就是DynamicAdvisedInterceptor对象。 所以这里就解释了开头第一个问题,根据cglib的callBacks的原理,执行自己业务方法、就会进去callback的intercept()方法。

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
	
			// Configure CGLIB Enhancer...
			Enhancer enhancer = createEnhancer();
			
			enhancer.setSuperclass(proxySuperClass);
			enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
			enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
			enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

			Callback[] callbacks = getCallbacks(rootClass);
			
			// fixedInterceptorMap only populated at this point, after getCallbacks call above
			enhancer.setCallbackFilter(new ProxyCallbackFilter(
					this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
			enhancer.setCallbackTypes(types);

			// Generate the proxy class and create a proxy instance.
			return createProxyClassAndInstance(enhancer, callbacks);
		}
		
}




private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
		
		Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

	
		// Choose a "direct to target" dispatcher (used for
		// unadvised calls to static targets that cannot return this).
		Callback targetDispatcher = (isStatic ?
				new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp());

		Callback[] mainCallbacks = new Callback[] {
				aopInterceptor,  // for normal advice
				targetInterceptor,  // invoke target without considering advice, if optimized
				new SerializableNoOp(),  // no override for methods mapped to this
				targetDispatcher, this.advisedDispatcher,
				new EqualsInterceptor(this.advised),
				new HashCodeInterceptor(this.advised)
		};

		
		return callbacks;
	}

5、上一步的结论是正确的 但是这里你必须要疑问 凭什么说“执行自己业务就会进入DynamicAdvisedInterceptor的intercept()方法呢”  Spring是如何控制的呢? 这里就要说到callbackFilter的作用,cglib生成代理类的字节码文件时会生成相同方法、方法内容里面会指定一个methodInterceptor。callbackFilter是为了给cglib生成字节码文件给方法选一个MethodInterceptor。自己业务的方法都会选用DynamicAdvisedInterceptor。

所以文章的第一个问题 应该知道答案了。

public final String saveUser(String var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? (String)var10000.intercept(this, CGLIB$saveUser$0$Method, new Object[]{var1}, CGLIB$saveUser$0$Proxy) : super.saveUser(var1);
    }

Aop拦截器链执行

这里要举一个场景、比如我们应用程序中配置了 应用前置通知 boforeAdvisor和后置afterAdvisor会被进spring容器,当执行我们的业务方法,会进入代理方法中、此时spring会从容器中获得到已经排好序的advisor进行执行。但是假如要处理第一个advisor时afterAdvisor、 spring能够进行具体通知调用嘛,答案时肯定不行!!因为我必须等自己业务的方法执行完,才能够after通知的调用。这里spring使用"链"的思路解决这个问题。下面我门具体看一下执行得逻辑。

1、进入DynamicAdvicedInterceptor得intercep方法。
// 获取MethodInterceptor  
//  注意 这里会多出来一个ExposeInvocationInterceptor 做链得桥接作用
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

// 开始执行
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

2、CglibMethodInvation 继承ReflectiveMethodInvocation, CglibMethodInvation.proceed里面又调用了父类得proceed 所以进入ReflectiveMethodInvocation的processd方法
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);
		}
	}

开始逐个遍历MethodInterceptors 进行Invoke逻辑

1、第一次进入这个方法获取的到的是ExposeInvocationInterceptor、我没看懂为什么spring要弄多一个额外的mathodInterceptor、但是不重要,整体逻辑不影响。这里Invoke方法 朝Theadlocal 放了CglibMethodInvocation对象。执行 proceed()方法、又回去了、是不是闻到了"套娃"模式了

2、CglibMethodInvocation里面开始调用父类 后面流程会疯狂进入这里的逻辑

3、第二次进入这个方法获取到是AspectJAfterAdvice。也是MehodInterceptor对象、进入Invoke方法、开始进入第二个逻辑、但是这里主要了 多了finally, 你前面再怎么套娃 after通知都会执行。

4、第三次进入这个方法获取到的是MethodBeforeAdviceInterceptor  进行before通知的调用。紧接着又回到步骤2

5、终于满足条件 开始taget方法的调用。

6、紧接着一层一层朝上返回、是不是很有意思、就是一个链。我觉得没必要追究spring为什么这样实现、spring作者就是用套娃模式 实现了一个链的调用、非常巧妙。

  • 23
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值