spring aop (下)调用拦截链
参考
1. JdkDynamicAopProxy
1.1 invoke
之前我们说到,当使用jdk动态代理时,会调用该类的getProxy
方法生成一个代理对象,返回给外界调用。该类继承了InvocationHandler,所有代理对象的方法调用都会被拦截到该对象的invoke
上。
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
try {
...
// 获取与方法相关的那组拦截器
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// 判断方法上是否有一组拦截器组
if (chain.isEmpty()) {
// 方法上没有拦截器,不必创建拦截器链,直接用反射机制调用方法
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 创建拦截器链
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 调用拦截器链
retVal = invocation.proceed();
}
...
}
finally {
...
}
}
}
invoke
所做的事情在上文代码注释已经写到。具体如下:
- 执行
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
。首先拿到方法上关联的一组拦截器(数组形式保存)- 如果
chain.isEmpty()
为真,数组长度等于0,说明没有拦截器。 只需执行retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); }
,调用该方法即可 - 如果
chain.isEmpty()
为假,则数组长度大于0,说明有拦截器,就需要:- 执行
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
。根据拦截器数组,和其它参数,创建拦截器链 - 执行
retVal = invocation.proceed();
。调用这个拦截器链,它会依次调用上面的每个拦截器(虽然有的拦截器是动态的,其执行与否要靠临时检查决定)。最后,它还会调用方法本体。
- 执行
- 如果
现在有三处代码不太明白,我将在1.2、1.3、1.4章对它们分别进行分析:
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
1.2 AopUtils.invokeJoinpointUsingReflection 反射机制调用方法
我们首先把最容易理解的方法给解决:
@Nullable
public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)
throws Throwable {
// Use reflection to invoke the method.
try {
ReflectionUtils.makeAccessible(method);
return method.invoke(target, args); // 用反射直接调用方法
}
catch (InvocationTargetException ex) {
...
}
...
}
显然就是用反射机制直接调用method
,没什么特别的。也确实没必要搞复杂,因为刚才提到,这个方法是在拦截器组长度为0的时候调用的。
方法没有拦截器链的时候,直接调用方法就行了。
1.3 getInterceptorsAndDynamicInterceptionAdvice 获取拦截器组
代理类的invoke
方法中,调用了List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
获取方法上的拦截器组。
该方法的返回值是List<Object>
数组,Object类型如此宽泛,似乎比较奇怪,我们后面会解释。
介绍advised:
成员变量JdkDynamicAopProxy.advised是AdvisedSupport
类型,它是一个配置类,是用来设置代理的,其内有各种配置参数。
/** Config used to configure this proxy. */
private final AdvisedSupport advised;
分析getInterceptorsAndDynamicInterceptionAdvice:
然后,this.advised.getInterceptorsAndDynamicInterceptionAdvice
调用的方法,属于AdvisedSupport
类:
// AdvisedSupport.java
/** Cache with Method as key and advisor chain List as value. */
private transient Map<MethodCacheKey, List<Object>> methodCache;
// AdvisedSupport.java
/**
* Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects
* for the given method, based on this configuration.
* @param method the proxied method
* @param targetClass the target class
* @return a List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
*/
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
MethodCacheKey cacheKey = new MethodCacheKey(method); // 根据method计算缓存key
List<Object> cached = this.methodCache.get(cacheKey); // 用method计算得到的key,试图获取缓存的拦截器组
if (cached == null) {
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass); // 没有缓存,要计算拦截器组。
this.methodCache.put(cacheKey, cached); // 将拦截器组缓存起来。
}
return cached;
}
方法里既保留了英文注释原文,也附加了中文注释。这个方法有以下三点:
- 用途。用途是获取方法上的拦截器组。
- 缓存技巧。每个方法上都会关联一组拦截器,这个关联关系可以用一个Map保存起来。这正是
methodCache
变量做的事。 我们为每个Method生成一个key,并将Method对应的拦截器组保存的key对应的value中。 用第一次获取拦截器组时,要进行计算,但会缓存起来。下次再获取时,直接取缓存即可。 - 将拦截器组以List<Object> 形式返回。为什么类型是Object呢?注释里说了:
@return a List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
这个数组包含的元素有两种:MethodInterceptor和InterceptorAndDynamicMethodMatcher。- 前者是静态拦截器,与方法有固定的关联关系。
- 后者是动态的拦截器,它是否被调用,需要在拦截器内进行临时判断。其类名中的
...DynamicMethodMatcher
也说明,它与方法的匹配是动态的,即使加入了某个方法上的拦截器组,也不一定每次都被调用。
篇幅关系,不便再深入this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice
,就先只讲到这个深度,之后会专门开个专题讲的。我们只需知道,这个函数能计算出方法对应的拦截器数组组即可。
1.4 创建、调用拦截链
JdkDynamicAopProxy的invoke
方法中,当获取的拦截器组长度大于0时,会调用如下代码,
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // 创建拦截链
retVal = invocation.proceed(); // 调用拦截链
1.4.1 创建拦截器链
我们看下构造函数:
protected ReflectiveMethodInvocation(
Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
@Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
this.proxy = proxy; // 代理对象
this.target = target; // 被代理对象
this.targetClass = targetClass; // 被代理对象的类
this.method = BridgeMethodResolver.findBridgedMethod(method); // 原方法
this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments); // 方法的参数
this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers; // 拦截器组。有静态和动态的
}
这里保存了一堆拦截链相关的变量,其含义已经说了。这里的method、arguments是原方法的本体和参数。interceptorsAndDynamicMethodMatchers是1.3章提过的,拦截器组,其中有静态和动态的拦截器。但你可能奇怪,target是真正要调用对象的本体,这个target怎么来的?请看下面我已经整理好的代码:
// JdkDynamicAopProxy.java
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
...
this.advised = config;
}
...
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
TargetSource targetSource = this.advised.targetSource;
...
target = targetSource.getTarget();
...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
}
target来自于targetSource,targetSource来自于this.advised,this.advised是在构造时就设置好的。 我们记得在上篇文章讲过,通过AnnotationAwareAspectJAutoProxyCreator的父类方法postProcessAfterInitialization,将原本的Bean替换成了代理对象。 而在wrapIfNecessary中,将bean本体封装成SingletonTargetSource,传入了createProxy生成代理对象,我们跟踪查看其中的代码:
// AbstractAutoProxyCreator.java
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
// AbstractAutoProxyCreator.java
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
...
ProxyFactory proxyFactory = new ProxyFactory();
...
proxyFactory.setTargetSource(targetSource);
...
return proxyFactory.getProxy(getProxyClassLoader());
}
// AdvisedSupport.java
// proxyFactory.setTargetSource(targetSource);调用至此
@Override
public void setTargetSource(@Nullable TargetSource targetSource) {
this.targetSource = (targetSource != null ? targetSource : EMPTY_TARGET_SOURCE);
}
// ProxyFactory.java
// proxyFactory.getProxy调用至此
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
// ProxyCreatorSupport.java
// createAopProxy()调用至此
protected final synchronized AopProxy createAopProxy() {
...
return getAopProxyFactory().createAopProxy(this);
}
// DefaultAopProxyFactory.java
// getAopProxyFactory().createAopProxy(this);调用至此
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (...) {
...
if (...) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
可见,原bean封装成SingletonTargetSource,传入了proxyFactory,而proxyFactory经过一番调用,把自己作为参数传给了createAopProxy,createAopProxy把它视为配置,创建了Aop对象,以便之后调用getProxy
获取代理对象。
图如下:
生成代理对象时,bean对象的传递.gif
至此,拦截链创建时的各个参数含义和来源,尤其是拦截器组和原bean对象的来源都已明了,该对象概览如下图。
ReflectiveMethodInvocation.jpg
1.4.2 调用拦截链
根据代码,我们创建好拦截链后,第一次调用proceed方法(代码如下)。我们可以猜到,拦截链中每个拦截器的行为,和最后方法本体的调用,都在这个方法内完成了。
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
proceed方法如下:
@Override
@Nullable
public Object proceed() throws Throwable {
// currentInterceptorIndex初始值为-1,且采用先自增的方式。
// 当它等于拦截器数组长度-1时,说明所有拦截器都已被递归调用,此时调用原方法。
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// 获取拦截器
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// 是动态的,需要动态匹配
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 {
// 匹配失败,调用后面的拦截器和方法本体
return proceed();
}
}
else {
// 是静态的,直接调用
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
proceed方法根据拦截器数组下标currentInterceptorIndex,取到拦截器。如果这个拦截器是动态的,需要动态匹配,并决定是否执行。 如果这个拦截器是静态的,则直接执行。 我们假设拦截器数组长度为3,存储的3个拦截器是[静态,动态,动态],第一次调用proceed时,currentInterceptorIndex为-1,先自增成为0,我们会取到第一个拦截器——静态拦截器,并直接调用之。 而第二次proceed的调用,我们交给MethodInterceptor执行。根据约定,它会在里面调用第二次proceed。
第一次proceed
MethodInterceptor调用第二次proceed后,我们来到proceed方法内。此时拦截器数组下标currentInterceptorIndex值为0,先自增成为1,取到第二个拦截器——动态拦截器。根据代码逻辑,我们要调用
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
进行动态匹配,若成功,则执行return dm.interceptor.invoke(this);
,调用这个动态拦截器;若失败,则执行return proceed();
,递归调用第三次proceed。我们假设此时匹配成功。
第二次proceed
MethodInterceptor调用第三次proceed后,我们再次来到proceed方法内,尽管进到的是同一个代码片段,但其实这三次proceed调用属于不同的调用。此时的方法调用栈如下:
调用栈
其实proceed被递归调用了,栈里面的方法都还没完全返回呢。
那么我们在第三次proceed时,拦截器数组下标currentInterceptorIndex为1,自增到2,我们会取到第三个拦截器——动态拦截器。既然是动态拦截器,就要进行动态匹配。我们假设此时匹配失败,那么会执行return proceed();
,进行第四次proceed的调用。
第三次proceed
第四次proceed是在刚才的第三次proceed中调用的,此时的方法调用栈如下:
此时拦截器数组下标currentInterceptorIndex为2,等于数组的最大下标,说明说有拦截器都已被检查过,最后我们要执行方法本体了。这会是最后一次proceed调用。我们会执行如下代码:
// ReflectiveMethodInvocation.java
@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();
}
...
}
@Nullable
protected Object invokeJoinpoint() throws Throwable {
return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}
显然是通过反射机制方法本体。
第四次proceed
最后,递归方法栈会依次退出,并提交返回值。最后第一次proceed会返回值给retVal
:
retVal = invocation.proceed();
总结
- 根据class缓存对应的增强器
- 根据method缓存对应的增强器数组,或者说拦截器数组。一个拦截器数组就相当于一个拦截链。换句话说,每个method都会缓存一个拦截链。
- 每次调用一个方法时,首先要拿到缓存好的方法的拦截链,依次调用链上的拦截器,最后才调用方法本身。 拦截链里有两种拦截器:动态的、静态的(其实指的是MethodInterceptor和InterceptorAndDynamicMethodMatcher)。动态的拦截器在调用拦截链时,要临时判断是否符合调用条件。静态的拦截器在调用时,不用判断,直接调用。
- 拦截链的调用时通过proceed和MethodInterceptor.invoke轮流递归调用的,在最后一次proceed中会调用方法本体。
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- spring之为级联集合属性进行赋值
- springmvc之HiddenHttpMethodFilter配置使用POST、GET、DELETE、PUT请求
- springmvc之使用@RequestParam绑定请求参数
- hadoop完全分布式之集群分发脚本
- 牛逼!9种方法让你访问Github提速到2MB/s!
- pyhton之如何将类的属性和方法设置成私有类型
- 【colab pytorch】查看gpu、cuda、cudnn信息
- 【colab pytorch】设置随机种子
- (三)django--带Template的网站
- spring之IOC(控制反转)和DI(依赖注入)
- 【colab pytorch】指定使用的显卡
- Python自学成才之路 迭代器的使用
- 回溯法--全排列
- 【colab pytorch】张量操作
- 『开发技巧』python :与-> 操作来给函数参数增加元信息