背景:
理清楚这章的内容 先抛出两个问题。
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作者就是用套娃模式 实现了一个链的调用、非常巧妙。