基于最新Spring 5.x,介绍了Spring AOP中代理对象的方法的调用与增强的源码和流程!
上一篇文章:Spring AOP源码(2)—AspectJAwareAdvisorAutoProxyCreator创建代理对象,我们讲解了代理对象的创建工作。我们还说过,JDK动态代理对象在调用方法时,将会调用代理对象JdkDynamicAopProxy的invoke方法进行增强,CGLIB动态代理对象在调用方法时,将会首先调用第一个拦截器也就是DynamicAdvisedInterceptor的intercept方法!
现在我们就来看看代理对象具体是怎么调用通知方法以及对原始方法进行增强的!
Spring AOP源码 系列文章
Spring AOP源码(1)—<aop:config/>AOP配置标签解析
Spring AOP源码(2)—AspectJAwareAdvisorAutoProxyCreator创建代理对象
Spring AOP源码(3)—invoke代理方法的调用与执行增强
Spring AOP源码(4)—基于注解的AOP源码解析以及AnnotationAwareAspectJAutoProxyCreator
Spring AOP源码(5)—DefaultAdvisorAutoProxyCreator自动代理创建者
文章目录
- Spring AOP源码 系列文章
- 1 JDK动态代理的调用
- 1.1 getInterceptorsAndDynamicInterceptionAdvice获取拦截器链
- 1.2 new ReflectiveMethodInvocation创建方法调用服务
- 1.3 proceed执行调用
- 1.3.1 invokeJoinpoint反射调用目标方法
- 1.3.2 常见方法拦截器的invoke方法
- 2 CGLIB动态代理的调用
- 3 总结
1 JDK动态代理的调用
JdkDynamicAopProxy.invoke方法是JDK动态代理的调用入口,总结起来该方法做要做两件事:
- 通过getInterceptorsAndDynamicInterceptionAdvice方法获取适合该方法的拦截器链集合。
- 创建一个MethodInvocation方法调用服务,随后使用MethodInvocation调用proceed()方法,通过责任链模式和方法递归调用来实现方法的代理和增强的逻辑。
/**
* AOP配置,就是前面的proxyFactory对象
*/
private final AdvisedSupport advised;
/**
* 接口集合(interfaces)中是否存在某个集合有equals方法
*/
private boolean equalsDefined;
/**
* 接口集合(interfaces)中是否存在某个集合有hashCode方法
*/
private boolean hashCodeDefined;
/**
* JdkDynamicAopProxy的方法
* InvocationHandler.invoke方法的实现
* <p>
* JDK动态代理方法的执行
* 调用者将完全看到目标抛出的异常,除非钩子方法引发异常
*
* @param proxy 代理对象实例
* @param method 当前执行的被代理对象真实的方法的Method对象
* @param args 当前执行方法时所传递的实际参数数组
*/
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
//是否设置代理上下文
boolean setProxyContext = false;
//获取TargetSource,targetSource实际上就是对目标对象实例进行封装的对象
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
/*
* 一些特殊方法和情况的处理,它们不需要调用增强的方法
*/
/*
* 如果代理接口集中不存在equals方法,并且当前拦截的方法是equals方法
* 这说明目标类没有实现接口的equals方法本身
*/
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
//调用JdkDynamicAopProxy自己的equals方法比较,因此可能会产生歧义
return equals(args[0]);
}
/*
* 如果代理接口集中不存在hashCode方法,并且当前拦截的方法是hashCode方法
* 这说明目标类没有实现接口的hashCode方法本身
*/
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
//调用JdkDynamicAopProxy自己的equals方法比较,因此可能会产生歧义
return hashCode();
}
/*
* 如果当前拦截的方法属于DecoratingProxy接口
* 那么这个方法肯定就是getDecoratedClass方法了
*/
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
//直接调用AopProxyUtils.ultimateTargetClass方法返回代理的最终目标类型
//并没有调用实现的getDecoratedClass方法
return AopProxyUtils.ultimateTargetClass(this.advised);
}
/*
* 如果opaque属性为false(默认false),并且当前拦截的方法属于一个接口
* 并且方法当前拦截的方法所属的接口是Advised接口或者Advised的父接口
*/
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
//直接反射调用原始方法
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
/*
* 如果exposeProxy属性为true(默认false),表示需要暴露代理对象
* 可通过<aop:config/>标签的expose-proxy属性或者@EnableAspectJAutoProxy注解的exposeProxy属性控制
*/
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
/*
* 将proxy也就是代理对象,通过AopContext.setCurrentProxy设置到AopContext内部的ThreadLocal线程本地变量中
* 这样我们就能在原始方法中通过AopContext.currentProxy()方法直接获取当前的代理对象
* 这个设置主要用来解决同一个目标类的方法互相调用时代理不生效的问题
*/
oldProxy = AopContext.setCurrentProxy(proxy);
//setProxyContext置为true
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.
/*
* 对此前设置到proxyFactory中的advisors集合中的Advisor进行筛选,因为advisors中的拦截器
* 只能说一定是匹配当前方法所属的类以及类中的某些方法,但并不一定匹配当前的方法,比如expression不匹配
* 所以这里需要获取匹配当前方法的Advisor中的拦截器链列表,用于拦截当前方法并进行增强
*
* 对于AnnotationAwareAspectJAutoProxyCreator和AspectJAwareAdvisorAutoProxyCreator自动代理创建者
* 第一个拦截器就是ExposeInvocationInterceptor,它是后面在extendAdvisors中加入进去的,其规则匹配所有方法
*/
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
//如果针对当前方法的拦截器链为空
if (chain.isEmpty()) {
//如有必要,根据给定方法中的需要参数类型调整参数数组类型
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
//直接反射通过目标类调用当前方法
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
//如果针对当前方法的拦截器链不为空
else {
// We need to create a method invocation...
/*创建一个MethodInvocation方法调用服务,参数为代理对象、目标对象、方法参数、目标类型、该方法的拦截器链集合*/
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
/*
* 调用方法调用服务的proceed方法,进行方法的代理和增强
* 一般的AOP都是通过该方法执行的,这是核心方法
*/
retVal = invocation.proceed();
}
/*
* 对于方法返回值进行必要的类型转换,比如返回代理对象
*/
// Massage return value if necessary.
//获取方法返回值类型
Class<?> returnType = method.getReturnType();
//如果执行结果不为null,并且返回值等于目标对象,并且返回值类型不是Object,并且返回值类型和代理类型兼容
//并且方法所属的类的类型不是RawTargetAccess类型及其子类型
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;
}
//如果返回值为null,并且返回值类型不是void,并且返回值类型是基本类型,那么抛出异常
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.
//替换oldProxy
AopContext.setCurrentProxy(oldProxy);
}
}
}
1.1 getInterceptorsAndDynamicInterceptionAdvice获取拦截器链
对此前设置到proxyFactory中的advisors集合中的Advisor进行筛选,因为advisors中的拦截器,只能说一定是匹配当前方法所属的类以及类中的某些方法,但并不一定匹配当前的方法,比如expression不匹配。所以这里需要获取匹配当前方法的Advisor中的拦截器链列表,用于拦截当前方法并进行增强。
获取到的拦截器列表将会存入内部的proxyFactory内部的methodCache缓存中(位于父类AdvisedSupport中),当新增Advisor的时候会清空,否则后续对于同一个方法的拦截将直接从缓存中获取拦截器链。
对于AnnotationAwareAspectJAutoProxyCreator和AspectJAwareAdvisorAutoProxyCreator自动代理创建者。第一个拦截器就是ExposeInvocationInterceptor,它是后面在extendAdvisors中加入进去的,其规则匹配所有方法。
//-------AdvisedSupport的相关属性---------
/**
* 将方法作为key,将该方法的拦截器链集合值的缓存
*/
private transient Map<MethodCacheKey, List<Object>> methodCache;
/**
* 获取方法拦截器链的工厂
*/
AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();
/**
* AdvisedSupport的方法
* <p>
* 确定适合给定方法的拦截器调用链列表
*
* @param method 目标方法
* @param targetClass 目标类
* @return 方法拦截器列表(可能还包括InterceptorAndDynamicMethodMatcher)
*/
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
//根据当前方法生成要给缓存key
MethodCacheKey cacheKey = new MethodCacheKey(method);
//从缓存中获取此前解析的适合给定方法的拦截器调用链列表
List<Object> cached = this.methodCache.get(cacheKey);
//如果为null,说明是第一次解析该方法
if (cached == null) {
//调用advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice方法解析,默认是DefaultAdvisorChainFactory
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
//解析结果存入methodCache缓存,下一次直接从缓存获取
this.methodCache.put(cacheKey, cached);
}
//返回结果
return cached;
}
1.1.1 getInterceptorsAndDynamicInterceptionAdvice从工厂获取拦截器链
DefaultAdvisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice方法用于获取对应方法的拦截器链。
/**
* 默认的Advisor链的工厂接口实现
*/
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
/**
* 确定给定方法的拦截器链列表
*
* @param config AOP配置,就是前面的proxyFactory对象
* @param method 目标方法
* @param targetClass 目标类
* @return 方法拦截器列表(可能还包括InterceptorAndDynamicMethodMatcher)
*/
@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
// This is somewhat tricky... We have to process introductions first,
// but we need to preserve order in the ultimate list.
//获取Advisor适配器注册表,默认获取的是一个DefaultAdvisorAdapterRegistry类型的单例
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
//从proxyFactory中获取此前设置的全部advisors
Advisor[] advisors = config.getAdvisors();
//拦截器列表
List<Object> interceptorList = new ArrayList<>(advisors.length);
//实际目标类型
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
//是否具有引介增强
Boolean hasIntroductions = null;
//遍历advisors集合
for (Advisor advisor : advisors) {
//如果属于PointcutAdvisor,即切入点通知器。比如<aop:advisor/>标签的DefaultBeanFactoryPointcutAdvisor
//以及通知标签的AspectJPointcutAdvisor,都是PointcutAdvisor类型
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
//判断切入点增强器是否匹配当前方法
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
//获取方法匹配器,比如AspectJExpressionPointcut切入点
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
boolean match;
//AspectJExpressionPointcut属于IntroductionAwareMethodMatcher,适配引介增强
if (mm instanceof IntroductionAwareMethodMatcher) {
if (hasIntroductions == null) {
hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
}
match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
} else {
match = mm.matches(method, actualClass);
}
//如果匹配当前方法
if (match) {
//将当前advisor通知器转换为MethodInterceptor方法拦截器
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
//是否需要动态匹配,比如expression中使用args()等可以传递参数的PCD类型,那么就需要动态匹配,可以匹配的某个参数的运行时传递的类型及其子类型
//动态匹配开销比较大,一般都不是也不需要使用
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
for (MethodInterceptor interceptor : interceptors) {
//封装成为InterceptorAndDynamicMethodMatcher
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
} else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
//如果属于IntroductionAdvisor,即引介增强通知器
//比如<aop:declare-parents/>标签的DeclareParentsAdvisor就是IntroductionAdvisor类型
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
//否则,默认匹配
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
/**
* 确定Advisors是否包含匹配的引介增强
*/
private static boolean hasMatchingIntroductions(Advisor[] advisors, Class<?> actualClass) {
for (Advisor advisor : advisors) {
if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
//引介匹配,也就是比较types-matching属性
if (ia.getClassFilter().matches(actualClass)) {
return true;
}
}
}
return false;
}
}
1.1.1.1 DefaultAdvisorAdapterRegistry通知器适配器注册表
DefaultAdvisorAdapterRegistry就是GlobalAdvisorAdapterRegistry.getInstance()的默认返回对象。
DefaultAdvisorAdapterRegistry用于使用AdvisorAdapter通知器适配器将通知器Advisor转换为拦截器MethodInterceptor。
名为注册表,因此DefaultAdvisorAdapterRegistry本身没有转换功能,该功能是内部的通知器适配器完成的!DefaultAdvisorAdapterRegistry初始化的时候,就会默认注册3个通知器适配器到内部缓存adapters中,分别是MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter、ThrowsAdviceAdapter。
/**
* DefaultAdvisorAdapterRegistry的属性
* <p>
* 通知器适配器的缓存,用于将对应类型的通知器转换为对应类型的适配器
*/
private final List<AdvisorAdapter> adapters = new ArrayList<>(3);
/**
* DefaultAdvisorAdapterRegistry的构造器
* <p>
* 创建新的DefaultAdvisorAdapterRegistry,注册一批通知器适配器
*/
public DefaultAdvisorAdapterRegistry() {
//注册MethodBeforeAdvice类型的通知器的配置器,常见通知器实现就是AspectJMethodBeforeAdvice
//也就是转换前置通知器,<aop:before/>通知
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
//注册AfterReturningAdvice类型的通知器的配置器,常见通知器实现就是AspectJAfterReturningAdvice
//也就是转换后置通知器,<aop:after-returning/>
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
//注册ThrowsAdvice类型的通知器的适配器,没有常见通知器实现
//有趣的是该适配器的类注释有误(基于5.2.8 RELEASE版本),或许是因为没有实现而这么写的
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
/**
* DefaultAdvisorAdapterRegistry的方法
* <p>
* 注册通知器适配器
*
* @param adapter 通知器适配器
*/
@Override
public void registerAdvisorAdapter(AdvisorAdapter adapter) {
this.adapters.add(adapter);
}
1.1.1.2 getInterceptors获取拦截器
获取给定通知器对应的拦截器,以允许拦截方法。
/**
* DefaultAdvisorAdapterRegistry的方法
* <p>
* 获取给定通知器对应的拦截器,以允许拦截方法
*
* @param advisor 通知器
* @return 拦截器数组
*/
@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
//拦截器集合
List<MethodInterceptor> interceptors = new ArrayList<>(3);
//获取通知器内部的通知
//<aop:before/> -> AspectJMethodBeforeAdvice -> MethodBeforeAdvice
//<aop:after/> -> AspectJAfterAdvice -> MethodInterceptor
//<aop:after-returning/> -> AspectJAfterReturningAdvice -> AfterReturningAdvice
//<aop:after-throwing/> -> AspectJAfterThrowingAdvice -> MethodInterceptor
//<aop:around/> -> AspectJAroundAdvice -> MethodInterceptor
Advice advice = advisor.getAdvice();
//如果通知本身就属于MethodInterceptor
//AspectJAfterAdvice、AspectJAfterThrowingAdvice、AspectJAroundAdvice它们都属于MethodInterceptor
if (advice instanceof MethodInterceptor) {
//直接添加当前通知
interceptors.add((MethodInterceptor) advice);
}
//尝试通过适配器转换
for (AdvisorAdapter adapter : this.adapters) {
//如果该是配置支持当前通知
if (adapter.supportsAdvice(advice)) {
//MethodBeforeAdviceAdapter支持AspectJMethodBeforeAdvice通知,适配成MethodBeforeAdviceInterceptor
//AfterReturningAdviceAdapter支持AspectJAfterReturningAdvice通知,适配成AfterReturningAdviceInterceptor
interceptors.add(adapter.getInterceptor(advisor));
}
}
//如果为空集合,抛出异常
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[0]);
}
1.2 new ReflectiveMethodInvocation创建方法调用服务
创建一个MethodInvocation方法调用服务,参数为代理对象、目标对象、方法参数、目标类型、该方法的拦截器链集合。
这个对象主要就是用来进行方法调用和增强的!
//------------ReflectiveMethodInvocation的相关属性-------------
protected final Object proxy;
@Nullable
protected final Object target;
protected final Method method;
protected Object[] arguments;
@Nullable
private final Class<?> targetClass;
/**
* 需要动态检查的MethodInterceptor和InterceptorAndDynamicMethodMatcher列表
*/
protected final List<?> interceptorsAndDynamicMethodMatchers;
/**
* 使用给定的参数构造新的ReflectiveMethodInvocation
*
* @param proxy 调用的代理对象
* @param target 要调用的目标对象
* @param method 调用的方法
* @param arguments 调用方法的参数
* @param targetClass 目标类,用于MethodMatcher调用
* @param interceptorsAndDynamicMethodMatchers 需要应用的拦截器链集合
*/
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;
}
1.3 proceed执行调用
基于责任链模式,按顺序递归的执行拦截器链中拦截器的invoke方法以及目标方法。invoke方法中就会执行对目标方法前后或者抛出异常时的增强(通知)的逻辑。
该方法中并没有进行循环和递归调用,那么所谓的递归怎么来的呢?关键就是currentInterceptorIndex索引属性和拦截器的invoke方法。currentInterceptorIndex属性在每一次调用proceed()方法时都会自增1,这样每一次执行proceed()方法时都会获取下一个拦截器,而所谓proceed()方法的递归调用,就是在拦截器的invoke方法的具体实现中调用的。
当所有拦截器都调用完毕,此时所有的前置通知都被执行完毕,此时就递归到了最底层,最终将会执行invokeJoinpoint()方法,这个方法就是反射执行被代理的方法,也就是原始方法的逻辑,该方法执行完毕,递归调用开始返回。随机执行后置通知和异常通知的逻辑,这两种通知执行完毕,最后会执行最终通知,我们在后面解析invoke源码的时候会知道,最终通知都是在finally块中调用的,所以会最后执行。
不同类型的通知的执行顺序,就这样通过try catch finally代码块和递归这种方法调用方式非常巧妙的实现了!
//------------ReflectiveMethodInvocation的相关属性-------------
/**
* 拦截器链
*/
protected final List<?> interceptorsAndDynamicMethodMatchers;
/**
* 我们调用的当前拦截器的索引,从-1开始
*/
private int currentInterceptorIndex = -1;
/**
* ReflectiveMethodInvocation的方法
* <p>
* 按顺序递归的执行拦截器链中拦截器的invoke方法以及最后的目标方法
* 责任链模式的应用
*
* @return 最终返回结果
*/
@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
//判断最后一个拦截器是否调用完毕,这里的currentInterceptorIndex是从-1开始的,因此集合size需要减去1
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
//如果拦截器调用完毕,那么反射执行被代理的方法,也就是原始方法
return invokeJoinpoint();
}
//获取下一个要调用的拦截器,currentInterceptorIndex从-1开始,先自增再获取值
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
/*如果是需要动态检查的拦截器,比如具有args()、@annotation()等动态参数的PCD,需要动态的分析方法参数等信息,性能较低,一般都是走下面的逻辑*/
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 {
/*
* 调用拦截器自己的invoke方法,这个invoke方法中就有在目标方法前后的后增强的逻辑
* 这个this指的是当前的ReflectiveMethodInvocation对象
*/
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
1.3.1 invokeJoinpoint反射调用目标方法
如果拦截器链中的最后一个拦截器调用完毕,那么使用反射调用目标方法(被代理的方法),参数为目标对象(被代理原始对象)和传递的方法参数。
该方法执行完毕,递归调用开始返回。
//------------ReflectiveMethodInvocation的相关属性-------------
@Nullable
protected final Object target;
protected final Method method;
protected Object[] arguments;
/**
* 使用反射调用连接点方法,子类可以重写它以使用自定义调用。
*
* @return 连接点方法调用的返回值
*/
@Nullable
protected Object invokeJoinpoint() throws Throwable {
return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}
/**
* 作为 AOP 方法调用的一部分,通过反射调用给定目标的原始方法
*
* @param target 被代理目标对象
* @param method 调用的方法
* @param args 方法的参数
* @return 调用结果,可能为null
*/
@Nullable
public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)
throws Throwable {
// Use reflection to invoke the method.
try {
//设置方法的可访问属性,method.setAccessible(true)
ReflectionUtils.makeAccessible(method);
//反射调用目标对象的原始方法并获取返回值
return method.invoke(target, args);
} catch (InvocationTargetException ex) {
// Invoked method threw a checked exception.
// We must rethrow it. The client won't see the interceptor.
throw ex.getTargetException();
} catch (IllegalArgumentException ex) {
throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
method + "] on target [" + target + "]", ex);
} catch (IllegalAccessException ex) {
throw new AopInvocationException("Could not access method [" + method + "]", ex);
}
}
1.3.2 常见方法拦截器的invoke方法
1.3.2.1 ExposeInvocationInterceptor暴露Invocation拦截器
对于AnnotationAwareAspectJAutoProxyCreator和AspectJAwareAdvisorAutoProxyCreator自动代理创建者,第一个拦截器就是ExposeInvocationInterceptor,它是在此前尝试创建代理对象的wrapIfNecessary方法中通过extendAdvisors扩展方法加入进去的。因此它也是第一个执行invoke方法的拦截器。
该拦截器不属于通知方法的拦截器,主要目的是将当前MethodInvocation,也就是ReflectiveMethodInvocation对象设置到线程本地变量属性invocation中(暴露当前MethodInvocation),方便后续拦截器可以快速获取。
/**
* ExposeInvocationInterceptor的属性
* <p>
* 一个ThreadLocal对象,内部保存着当前线程递归调用中的ReflectiveMethodInvocation对象
* 用于将MethodInvocation暴露出来,后面的拦截器可以方便的通过ExposeInvocationInterceptor.currentInvocation()
* 静态方法快速获取当前ReflectiveMethodInvocation对象
*/
private static final ThreadLocal<MethodInvocation> invocation =
new NamedThreadLocal<>("Current AOP method invocation");
/**
* ExposeInvocationInterceptor拦截器的invoke方法
* <p>
* 对于AnnotationAwareAspectJAutoProxyCreator和AspectJAwareAdvisorAutoProxyCreator自动代理创建者
* 第一个拦截器就是ExposeInvocationInterceptor
* <p>
* 主要目的是将当前MethodInvocation,也就是ReflectiveMethodInvocation对象设置到线程本地变量中
* 暴露当前MethodInvocation,方便后续拦截器可以快速获取
*
* @param mi 当前ReflectiveMethodInvocation对象
* @return 调用下一次proceed方法的返回结果
*/
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
MethodInvocation oldInvocation = invocation.get();
//将当前MethodInvocation设置到线程本地变量中
invocation.set(mi);
try {
/*
* 继续调用mi.proceed()
* 这里的mi就是当前的ReflectiveMethodInvocation对象,也就是递归调用的逻辑
* 下一次调用,将会调用第二个拦截器的invoke方法(如果存在)
* 当最后一个拦截器执行完毕时,才会通过invokeJoinpoint()反射执行被代理的方法(目标方法)
* 然后开始返回
*/
return mi.proceed();
} finally {
invocation.set(oldInvocation);
}
}
1.3.2.2 MethodBeforeAdviceInterceptor前置通知拦截器
MethodBeforeAdviceInterceptor作为前置通知拦截器。它的invoke方法很简单,即在目标方法(proceed方法)调用之前调用前置通知方法。
/**
* MethodBeforeAdviceInterceptor的属性
* <p>
* 前置通知,AspectJMethodBeforeAdvice
*/
private final MethodBeforeAdvice advice;
/**
* MethodBeforeAdviceInterceptor前置通知拦截器的方法
* <p>
* 原理很简单,在目标方法调用之前调用前置通知方法即可
*
* @param mi 当前ReflectiveMethodInvocation对象
* @return 调用下一次proceed方法的返回结果
*/
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
//通过当前通知实例调用前置通知方法,此时目标方法未被执行
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
/*
* 继续调用mi.proceed()
* 这里的mi就是当前的ReflectiveMethodInvocation对象,也就是递归调用的逻辑
* 下一次调用,将会调用下一个拦截器的invoke方法(如果存在)
* 当最后一个拦截器执行完毕时,才会通过invokeJoinpoint()反射执行被代理的方法(目标方法)
* 然后开始返回
*/
return mi.proceed();
}
1.3.2.2.1 before调用前置通知
该方法用于调用前置通知,其内部最终会调用invokeAdviceMethodWithGivenArgs方法反射执行通知方法,这个方法也是通用的通知的执行方法。
这里的getJoinPointMatch()实际上是将expression中用于传递参数的参数名和调用目标方法传递的实际参数值绑定,用于辅助后面的argBinding方法进行通知方法的参数传递,一般都是null。如果expression中配置了args()的PCD并且需要通知方法需要传递参数,那么JoinPointMatch不为null,实际上如果有其他能够传参的PCD,那么jpMatch也不会为null,比如@annotation()的PCD就能传递方法上的注解作为参数。
/**
* AspectJMethodBeforeAdvice的方法
* <p>
* 调用前置通知
*
* @param method 目标方法
* @param args 方法参数
* @param target 目标对象
*/
@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
//内部调用的invokeAdviceMethod方法
//getJoinPointMatch用于获取当前连接点匹配结果,用于通知方法的参数匹配和传递,一般都是null
//如果expression中配置了args()的PCD并且需要通知方法需要传递参数,那么JoinPointMatch不为null
//如果有其他能够传参的PCD,那么jpMatch也不会为null,比如@annotation()的PCD就能传递方法上的注解作为参数
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
/**
* AbstractAspectJAdvice的方法
* <p>
* 通用回调通知的方法
*
* @param jpMatch 匹配此执行连接点的JoinPointMatch,用于通知方法的参数匹配和传递,一般为null
* 如果expression中配置了args()的PCD并且需要通知方法需要传递参数,那么不为null
* 如果有其他能够传参的PCD,那么jpMatch也不会为null,比如@annotation()的PCD就能传递方法上的注解作为参数
* @param returnValue 方法执行的返回值(一般都是目标方法的返回值,可能为null)
* 前置通知不能获取方法执行的返回值
* @param ex 方法执行引发的异常(一般都是目标方法抛出的异常,可能为null)
* 前置通知不能获取方法执行抛出的异常
* @return 调用结果
* @throws Throwable in case of invocation failure
*/
protected Object invokeAdviceMethod(
@Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex)
throws Throwable {
//getJoinPoint()用于获取当前执行的连接点,argBinding方法用于获取当前通知方法需要传递的参数数组
//调用最终调用invokeAdviceMethodWithGivenArgs方法反射执行通知方法
return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
}
1.3.2.2.2 getJoinPoint获取当前连接点
获取当前方法连接点,也就是JoinPoint对象,这个也就是我们所说的可以绑定到通知方法上的JoinPoint参数。
这个对象实际类型为MethodInvocationProceedingJoinPoint,并且封装了当前的ReflectiveMethodInvocation对象,因此我们可以通过JoinPoint中访问当前被通知方法的目标对象、代理对象、方法参数等各种数据。
/**
* AbstractAspectJAdvice的方法
* <p>
* 获取当前方法连接点
*/
protected JoinPoint getJoinPoint() {
return currentJoinPoint();
}
/**
* AbstractAspectJAdvice的属性
* <p>
* 当前连接点用于userAttributes缓存map中的key
* 这个key的值就是 org.aspectj.lang.JoinPoint
*/
protected static final String JOIN_POINT_KEY = JoinPoint.class.getName();
/**
1. AbstractAspectJAdvice的方法
2. <p>
3. 对于当前调用,采用懒加载的方式实例化连接点。
4. 5. @return 当前 Aspectj 连接点
*/
public static JoinPoint currentJoinPoint() {
//获取第一个拦截器存入的MethodInvocation,也就是创建的ReflectiveMethodInvocation对象
MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
//获取该名称的属性值
JoinPoint jp = (JoinPoint) pmi.getUserAttribute(JOIN_POINT_KEY);
//如果为null
if (jp == null) {
//新建一个MethodInvocationProceedingJoinPoint,内部持有当前的ReflectiveMethodInvocation对象
jp = new MethodInvocationProceedingJoinPoint(pmi);
//当前连接点设置到userAttributes缓存中,key固定为"org.aspectj.lang.JoinPoint"
//当后面的通知方法被回调而需要获取连接点时,直接从缓存中获取
pmi.setUserAttribute(JOIN_POINT_KEY, jp);
}
//返回方法连接点
return jp;
}
1.3.2.2.3 argBinding确定通知方法的参数
在调用invokeAdviceMethodWithGivenArgs 之前,会调用argBinding获取该通知方法需要传递的参数数组。
对于JoinPoint连接点参数、普通参数、目标方法返回值以及抛出的异常这四种参数的封装都是该方法完成的,这是一个通用方法,所有类型的通知都会调用该方法。
该方法传递四个参数:
- JoinPoint:当前方法连接点,一般用于传递到通知方法的第一个参数。用于通知方法的第一个连接点参数传递。可以使用的类型为JoinPoint、ProceedingJoinPoint(环绕通知专用)、JoinPoint.StaticPart。
- JoinPointMatch:匹配此执行连接点的JoinPointMatch,用于通知方法的其他普通参数的匹配和传递,一般为null。如果expression中配置了args()的PCD并且需要通知方法需要传递普通参数,那么不为null。如果有其他能够传参的PCD,那么jpMatch也不会为null,比如@annotation()的PCD就能传递方法上的注解作为参数。
- 对于普通参数传递,一般我们需要在execution表达式中配置args()PCD,args()里面的参数位置就是对应着目标方法的参数位置,PointcutParameter[]就是对应着封装的。而设置的参数名最好和通知方法的参数名一致,如果不一致,那么需要手动配置arg-names属性等于这个参数名。
- 对于其他参数传递,比如方法上的注解,我们可以设置@annotation()的PCD,注意一个@annotation()的格式只能传递一个注解,如果需要传递多个注解那么需要多个@annotation()。关于更多参数传递,可以看此前Spring AOP学习时的文章。
- returnValue:方法执行的返回值(一般都是目标方法的返回值,可能为null)。用于通知方法的方法执行返回值参数传递。
- ex:方法执行引发的异常(一般都是目标方法抛出的异常,可能为null)。用于通知方法的方法执行异常参数传递。
//--------AbstractAspectJAdvice的相关属性-----------
/**
* JoinPoint参数在通知方法参数列表中的索引位置
* 当前仅支持索引 0(如果存在),也就是只能放在第一个参数位置
*/
private int joinPointArgumentIndex = -1;
/**
* 此JoinPoint内部的StaticPart 对象作为参数绑定时的索引位置
* 当前仅支持索引 0(如果存在),也就是只能放在第一个参数位置
*/
private int joinPointStaticPartArgumentIndex = -1;
/**
* 异常通知的throwing属性的值
*/
@Nullable
private String throwingName;
/**
* 后置通知的returning属性的值
*/
@Nullable
private String returningName;
/**
* AbstractAspectJAdvice的属性
* <p>
* 参数名 -> 通知方法参数索引的map,用于辅助参数绑定
* 不包括首位的JoinPoint参数名(如果存在JoinPoint参数)
*/
@Nullable
private Map<String, Integer> argumentBindings;
/**
1. AbstractAspectJAdvice的方法
2. <p>
3. 获取执行通知方法需要传递的参数数组,这是一个通用的方法
4. <p>
5. 我们在学习Spring AOP的时候就说过,通知方法中可以接收各种目标方法参数以及JoinPoint连接点
6. 7. @param jp 当前方法连接点
8. @param jpMatch 匹配此执行连接点的JoinPointMatch,用于通知方法的参数匹配和传递,一般为null
9. 如果expression中配置了args()的PCD并且需要通知方法需要传递参数,那么不为null
10. 如果有其他能够传参的PCD,那么jpMatch也不会为null,比如@annotation()的PCD就能传递方法上的注解作为参数
11. @param returnValue 方法执行的返回值(一般都是目标方法的返回值,可能为null)
12. @param ex 方法执行引发的异常(一般都是目标方法抛出的异常,可能为null)
13. @return 如果没有参数,则返回空数组
*/
protected Object[] argBinding(JoinPoint jp, @Nullable JoinPointMatch jpMatch,
@Nullable Object returnValue, @Nullable Throwable ex) {
//优化操作,方便后面的通知的参数绑定更快的完成
calculateArgumentBindings();
//绑定的参数数组
Object[] adviceInvocationArgs = new Object[this.parameterTypes.length];
//绑定的个数
int numBound = 0;
/*
* 绑定第一个参数,JoinPoint或者JoinPoint.StaticPart
*/
//如果joinPointArgumentIndex值不为-1(通过calculateArgumentBindings方法可以设置为0)
//那么第一参数就是JoinPoint
if (this.joinPointArgumentIndex != -1) {
adviceInvocationArgs[this.joinPointArgumentIndex] = jp;
numBound++;
}
//如果joinPointStaticPartArgumentIndex值不为-1(通过calculateArgumentBindings方法可以设置为0)
//那么第一参数就是JoinPoint.StaticPart
else if (this.joinPointStaticPartArgumentIndex != -1) {
adviceInvocationArgs[this.joinPointStaticPartArgumentIndex] = jp.getStaticPart();
numBound++;
}
/*
* 操作argumentBindings,确定具体传递的参数
*/
if (!CollectionUtils.isEmpty(this.argumentBindings)) {
/*
* 从切入点匹配绑定,对于普通参数或者其他参数(比如方法的注解)传递,我们需要在execution中配置传参的PCD
* 比如args()、@annotation(),对于普通参数,这些PCD里面的参数位置就是对应着目标方法的参数位置
* PointcutParameter[]就是对应着位置进行 参数名 -> 传递的参数值 封装的
* 这些PCD中设置的参数名最好和通知方法的参数名一致,如果不一致,那么需要手动配置arg-names属性等于这个参数名
*/
if (jpMatch != null) {
//获取PointcutParameter数组,这里面就是参数名 -> 传递的参数值的映射
PointcutParameter[] parameterBindings = jpMatch.getParameterBindings();
//遍历parameterBindings
for (PointcutParameter parameter : parameterBindings) {
//获取name
String name = parameter.getName();
//获取该name对应的索引
Integer index = this.argumentBindings.get(name);
//在给定索引位置设置参数值
adviceInvocationArgs[index] = parameter.getBinding();
numBound++;
}
}
//绑定返回值参数
if (this.returningName != null) {
Integer index = this.argumentBindings.get(this.returningName);
adviceInvocationArgs[index] = returnValue;
numBound++;
}
//绑定异常值参数
if (this.throwingName != null) {
Integer index = this.argumentBindings.get(this.throwingName);
adviceInvocationArgs[index] = ex;
numBound++;
}
}
if (numBound != this.parameterTypes.length) {
throw new IllegalStateException("Required to bind " + this.parameterTypes.length +
" arguments, but only bound " + numBound + " (JoinPointMatch " +
(jpMatch == null ? "was NOT" : "WAS") + " bound in invocation)");
}
return adviceInvocationArgs;
}
1.3.2.2.3.1. calculateArgumentBindings辅助参数绑定
该方法用于辅助参数绑定,以便对后续通知调用绑定的参数可以尽可能快地完成。
- 如果第一个参数的类型为JoinPoint或ProceedingJoinPoint,则我们在该位置传递JoinPoint(ProceedingJoinPoint用于环绕通知),该方法会将joinPointArgumentIndex属性置为0。
- 如果第一个参数的类型为JoinPoint.StaticPart,那么我们将在该位置传递JoinPoint.StaticPart。该方法会将joinPointStaticPartArgumentIndex属性置为0。
- 剩余参数根据argumentNames数组中的参数名以及起始索引设置argumentBinding缓存,这个而缓存保存了参数名到参数索引的映射,随后还会配置expression切入点表达式,如果我们自己设置了和真参数名不用的参数名,那么一般需要设置的参数名和切入点表达式中的args()类型的PCD中的参数名对应。
- 这个argumentNames就是解析通知标签的arg-names属性或者通知注解中的argNames属性得到的,我们传递的时候通过","分隔参数名。
- 这里还用于绑定returning属性,即返回值参数,用于接收目标方法的返回值;还用于绑定throwing属性,就抛出异常参数,用于接收抛出的异常。
//----------AbstractAspectJAdvice的相关属性------------
/**
* 参数自省标志,默认为false
*/
private boolean argumentsIntrospected = false;
/**
* 通知方法的参数类型数组
*/
private final Class<?>[] parameterTypes;
/**
* 通知方法
*/
protected transient Method aspectJAdviceMethod;
/**
* AbstractAspectJAdvice的方法
* <p>
* 优化操作,方便后面的通知的参数绑定更快的完成
* <p>
* 作为设置的一部分,尽可能多地做工作,以便对后续通知调用绑定的参数可以尽可能快地完成
* <p>
* 1 如果第一个参数的类型为JoinPoint或ProceedingJoinPoint,则我们在该位置传递JoinPoint(ProceedingJoinPoint用于环绕通知)
* 该方法会将joinPointArgumentIndex属性置为0
* 2 如果第一个参数的类型为JoinPoint.StaticPart,那么我们将在该位置传递JoinPoint.StaticPart
* 该方法会将joinPointStaticPartArgumentIndex属性置为0
* 3 剩余参数根据argumentNames数组中的参数名到argumentBinding中查找参数名和参数索引位置的关系
* 并且计算哪个通知参数需要绑定到哪个参数名称。有多种策略用于确定此绑定,这些策略排列在责任链中,通过责任链模式确定
* 这个argumentNames就是解析通知标签的arg-names属性或者通知注解中的argNames属性得到的,我们传递的时候通过","分隔参数名
*/
public final synchronized void calculateArgumentBindings() {
// 如果通知方法参数类型数组长度为0,这说明通知方法没有参数,这种情况什么都不用操作,直接返回
if (this.argumentsIntrospected || this.parameterTypes.length == 0) {
return;
}
//获取通知方法参数个数,作为需要绑定的参数个数
int numUnboundArgs = this.parameterTypes.length;
//获取通知方法的参数类型数组
Class<?>[] parameterTypes = this.aspectJAdviceMethod.getParameterTypes();
/*
* 如果第一个参数类型为JoinPoint,则设置joinPointArgumentIndex属性值为0
* 或者第一个参数类型为ProceedingJoinPoint,同样设置joinPointArgumentIndex属性值为0
* 因为该类型的joinPoint可以调用proceed()方法,因此Spring固定该参数只有后置通知支持
* 其他通知方法如果设置则抛出异常:"ProceedingJoinPoint is only supported for around advice"
* 或者第一个参数类型为JoinPoint.StaticPart,设置joinPointStaticPartArgumentIndex属性值为0
*/
if (maybeBindJoinPoint(parameterTypes[0]) || maybeBindProceedingJoinPoint(parameterTypes[0]) ||
maybeBindJoinPointStaticPart(parameterTypes[0])) {
//三个条件满足任意一个,则需要绑定的参数个数自减一
numUnboundArgs--;
}
//如果大于0,说明还有其他参数需要绑定
if (numUnboundArgs > 0) {
//需要按名称绑定参数从切入点返回的匹配项
bindArgumentsByName(numUnboundArgs);
}
//参数自省,设置为true,表示当前连接点的参数已经通过calculateArgumentBindings优化了
this.argumentsIntrospected = true;
}
//----------AbstractAspectJAdvice的相关属性------------
/**
* AbstractAspectJAdvice的属性
* <p>
* 需要绑定的参数名数组
* 这个argumentNames就是解析通知标签的arg-names属性或者通知注解中的argNames属性得到的,我们传递的时候通过","分隔参数名
* 如果没有设置,那么Spring会自动获取
*/
@Nullable
private String[] argumentNames;
/**
* AbstractAspectJAdvice的方法
* <p>
* 通过argumentNames的参数名匹配argumentBinding确定需要传递的参数
*
* @param numArgumentsExpectingToBind 需要绑定的参数个数
*/
private void bindArgumentsByName(int numArgumentsExpectingToBind) {
//如果没有手动设置参数名
if (this.argumentNames == null) {
//那么通过构建一个ParameterNameDiscoverer(参数名称发现器)并且通过多个不同的发现器来自动获取
//获取的参数名数组通常就是通知方法参数名数组
this.argumentNames = createParameterNameDiscoverer().getParameterNames(this.aspectJAdviceMethod);
}
if (this.argumentNames != null) {
//根据参数名数组绑定参数
bindExplicitArguments(numArgumentsExpectingToBind);
} else {
throw new IllegalStateException("Advice method [" + this.aspectJAdviceMethod.getName() + "] " +
"requires " + numArgumentsExpectingToBind + " arguments to be bound by name, but " +
"the argument names were not specified and could not be discovered.");
}
}
/**
* AbstractAspectJAdvice的属性
* <p>
* 参数名到参数索引的map,用于辅助参数绑定
*/
@Nullable
private Map<String, Integer> argumentBindings;
/**
* 绑定参数
*
* @param numArgumentsLeftToBind 需要绑定的参数个数
*/
private void bindExplicitArguments(int numArgumentsLeftToBind) {
Assert.state(this.argumentNames != null, "No argument names available");
this.argumentBindings = new HashMap<>();
int numExpectedArgumentNames = this.aspectJAdviceMethod.getParameterCount();
if (this.argumentNames.length != numExpectedArgumentNames) {
throw new IllegalStateException("Expecting to find " + numExpectedArgumentNames +
" arguments to bind by name in advice, but actually found " +
this.argumentNames.length + " arguments.");
}
//从指定位置开始绑定
// So we match in number...
int argumentIndexOffset = this.parameterTypes.length - numArgumentsLeftToBind;
for (int i = argumentIndexOffset; i < this.argumentNames.length; i++) {
this.argumentBindings.put(this.argumentNames[i], i);
}
//绑定返回值参数,用于接收目标方法的返回值
// Check that returning and throwing were in the argument names list if
// specified, and find the discovered argument types.
if (this.returningName != null) {
if (!this.argumentBindings.containsKey(this.returningName)) {
throw new IllegalStateException("Returning argument name '" + this.returningName +
"' was not bound in advice arguments");
} else {
Integer index = this.argumentBindings.get(this.returningName);
this.discoveredReturningType = this.aspectJAdviceMethod.getParameterTypes()[index];
this.discoveredReturningGenericType = this.aspectJAdviceMethod.getGenericParameterTypes()[index];
}
}
//绑定抛出异常参数,用于接收抛出的异常
if (this.throwingName != null) {
if (!this.argumentBindings.containsKey(this.throwingName)) {
throw new IllegalStateException("Throwing argument name '" + this.throwingName +
"' was not bound in advice arguments");
} else {
Integer index = this.argumentBindings.get(this.throwingName);
this.discoveredThrowingType = this.aspectJAdviceMethod.getParameterTypes()[index];
}
}
//相应地配置切入点表达式,一般需要参数名和切入点表达式中的args()类型的PCD中的参数名对应
// configure the pointcut expression accordingly.
configurePointcutParameters(this.argumentNames, argumentIndexOffset);
}
1.3.2.2.4 invokeAdviceMethodWithGivenArgs调用通知方法
在确定需要传递的参数之后,执行invokeAdviceMethodWithGivenArgs方法,该方法的逻辑很简单,就是根据传递的参数反射调用切面类对象的通知方法并获取返回值,也就是执行增强的逻辑。
//-----------AbstractAspectJAdvice的相关属性------------
/**
* 通知方法
*/
protected transient Method aspectJAdviceMethod;
/**
* 切面实例工厂,可以获取切面类实例
*/
private final AspectInstanceFactory aspectInstanceFactory;
/**
* AbstractAspectJAdvice的方法
* <p>
* 反射执行通知方法
*
* @param args 通知方法参数
* @return
* @throws Throwable
*/
protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
Object[] actualArgs = args;
//如果通知方法参数个数为0,那么参数数组置为null
if (this.aspectJAdviceMethod.getParameterCount() == 0) {
actualArgs = null;
}
try {
//设置方法的可访问属性,即aspectJAdviceMethod.setAccessible(true)
ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
// TODO AopUtils.invokeJoinpointUsingReflection
//反射调用切面类对象的通知方法并获取返回值
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
} catch (IllegalArgumentException ex) {
throw new AopInvocationException("Mismatch on arguments to advice method [" +
this.aspectJAdviceMethod + "]; pointcut expression [" +
this.pointcut.getPointcutExpression() + "]", ex);
} catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
1.3.2.3 AfterReturningAdviceInterceptor后置通知拦截器
AfterReturningAdviceInterceptor作为后置通知拦截器。它的invoke方法很简单,即在目标方法(proceed方法)调用成功并且没有抛出异常之后,才会调用后置通知方法。
/**
* AfterReturningAdviceInterceptor的属性
* <p>
* 后置通知,AspectJAfterReturningAdvice
*/
private final AfterReturningAdvice advice;
/**
* AfterReturningAdviceInterceptor后置通知拦截器的方法
* <p>
* 原理很简单,在目标方法调用成功之后才调用后置通知方法即可
*
* @param mi 当前ReflectiveMethodInvocation对象
* @return 调用下一次proceed方法的返回结果
*/
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
/*
* 首先就继续调用mi.proceed()
* 这里的mi就是当前的ReflectiveMethodInvocation对象,也就是递归调用的逻辑
* 下一次调用,将会调用下一个拦截器的invoke方法(如果存在)
* 当最后一个拦截器执行完毕时,才会通过invokeJoinpoint()反射执行被代理的方法(目标方法)
* 然后开始返回
*/
Object retVal = mi.proceed();
/*
* 当递归方法返回时,说明目标方法已被执行,这是开始执行后置方法
*/
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
//返回默认就是目标方法的返回值
return retVal;
}
1.3.2.3.1 afterReturning调用后置通知
该方法用于调用后置通知,内部调用的invokeAdviceMethod方法,最终会调用invokeAdviceMethodWithGivenArgs方法反射执行通知方法,这个方法也是通用的通知的执行方法。
在执行后置通知之前还会调用shouldInvokeOnReturnValueOf方法,该方法会校验返回值类型和当前通知方法参数需要的返回值类型是否匹配,如果匹配那么可以调用当前后置通知方法,否则不会调用。也就是说,如果我们的后置通知需要接收目标方法的返回值作为参数,那么接收的类型要和返回值类型兼容,否则,不会执行这个后置通知方法,这种情况也不会报错,因此需要谨慎接收返回值!
/**
* AspectJAfterReturningAdvice的方法
* <p>
* 回调后置通知
*
* @param returnValue 目标方法的返回值
* @param method 目标方法
* @param args 方法参数
* @param target 目标对象
*/
@Override
public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
//如果返回值类型和当前通知方法参数需要的返回值类型匹配,那么可以调用当前通知方法,否则不会调用
if (shouldInvokeOnReturnValueOf(method, returnValue)) {
//内部调用的invokeAdviceMethod方法,最终会调用invokeAdviceMethodWithGivenArgs方法
invokeAdviceMethod(getJoinPointMatch(), returnValue, null);
}
}
1.3.2.4 AspectJAfterThrowingAdvice异常通知拦截器
可以看到异常通知拦截器就是异常通知本身,因为该类直接实现了MethodInterceptor接口,不需要被适配成为方法拦截器。
它的invoke方法很简单,使用try catch块,将mi.proceed()方法置于try块中,将异常通知方法的调用置于catch块中,当目标方法或者前面的通知方法调用抛出异常时,该异常就会被捕获,就可能会执行异常通知,即在目标方法调用抛出异常之后才调用异常通知方法即可。
在执行异常通知之前还会调用shouldInvokeOnThrowing方法,该方法会校验抛出异常类型和当前通知方法参数需要的异常值类型是否匹配,如果匹配那么可以调用当前异常通知方法,否则不会调用。也就是说,如果我们的异常通知需要接收抛出的异常值作为参数,那么接收的类型要和实际抛出异常类型兼容,否则,不会执行这个异常通知方法,这种情况也不会报错,因此需要谨慎接收异常值!
/**
* AspectJAfterThrowingAdvice异常通知拦截器的方法
* <p>
* 使用try catch块,将mi.proceed()置于try块中,将异常通知方法的调用置于catch块中
* 当目标方法或者前面的通知方法调用抛出异常时,就可能会执行异常通知
* 原理很简单,在目标方法调用抛出异常之后才调用异常通知方法即可
*
* @param mi 当前ReflectiveMethodInvocation对象
* @return 调用下一次proceed方法的返回结果
*/
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
/*
* 首先就继续调用mi.proceed()
* 这里的mi就是当前的ReflectiveMethodInvocation对象,也就是递归调用的逻辑
* 下一次调用,将会调用下一个拦截器的invoke方法(如果存在)
* 当最后一个拦截器执行完毕时,才会通过invokeJoinpoint()反射执行被代理的方法(目标方法)
* 然后开始返回
*/
return mi.proceed();
}
//当目标方法或者前面的通知方法调用抛出异常时,异常会被捕获
//就可能会执行异常通知
catch (Throwable ex) {
//如果抛出的异常类型和当前通知方法参数需要的异常类型匹配,那么可以调用当前通知方法,否则不会调用
if (shouldInvokeOnThrowing(ex)) {
//内部调用的invokeAdviceMethod方法,最终会调用invokeAdviceMethodWithGivenArgs方法
invokeAdviceMethod(getJoinPointMatch(), null, ex);
}
//继续抛出异常
throw ex;
}
}
1.3.2.5 AspectJAfterAdvice最终通知拦截器
可以看到最终通知拦截器就是最终通知本身,因为该类直接实现了MethodInterceptor接口,不需要被适配成为方法拦截器。
它的invoke方法很简单,使用try finally块,将mi.proceed()方法置于try块中,将最终通知方法的调用置于finally块中,无论目标方法或者前面的通知方法调用成功还是抛出异常,都会执行最终通知,因为它在finally块中,并且,有程序执行规则可知,最终通知在前置通知、目标方法、后置/异常通知执行之后才会执行。
/**
* AspectJAfterAdvice最终通知拦截器的方法
* <p>
* 使用try finally块,将mi.proceed()置于try块中,将最终通知方法的调用置于finally块中
* 当无论目标方法或者前面的通知方法调用成功还是抛出异常,都会执行最终通知,因为它在finally块中
* 并且最终通知在前置通知、目标方法、后置/异常通知执行之后才会执行
*
* @param mi 当前ReflectiveMethodInvocation对象
* @return 调用下一次proceed方法的返回结果
*/
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
/*
* 首先就继续调用mi.proceed()
* 这里的mi就是当前的ReflectiveMethodInvocation对象,也就是递归调用的逻辑
* 下一次调用,将会调用下一个拦截器的invoke方法(如果存在)
* 当最后一个拦截器执行完毕时,才会通过invokeJoinpoint()反射执行被代理的方法(目标方法)
* 然后开始返回
*/
return mi.proceed();
}
//在前置通知、目标方法、后置/异常通知执行之后才会执行最终通知,因为它在finally块中
finally {
//内部调用的invokeAdviceMethod方法,最终会调用invokeAdviceMethodWithGivenArgs方法
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
1.3.2.6 AspectJAroundAdvice环绕通知拦截器
它和其他通知的较大区别就是,invoke方法内部并没有直接调用MethodInvocation的proceed()方法,而是将MethodInvocation包装成为一个ProceedingJoinPoint,作为环绕通知的参数给使用者。然后使用者在通知方法中可以调用ProceedingJoinPoint的proceed()方法,其内部还是调用被包装的MethodInvocation的proceed()方法,这样就将递归调用正常延续了下去。
/**
* AspectJAroundAdvice环绕通知拦截器
* <p>
* 它和其他通知的较大区别就是,invoke方法内部并没有调用MethodInvocation的proceed()方法
* 而是将MethodInvocation包装成为一个ProceedingJoinPoint,作为环绕通知的参数给使用者
* 然后使用者在通知方法中可以调用ProceedingJoinPoint的proceed()方法,其内部还是调用被包装的
* MethodInvocation的proceed()方法,这样就将递归调用正常延续了下去
*
* @param mi 当前ReflectiveMethodInvocation对象
* @return 调用环绕通知的返回结果
*/
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
//如果不属于ProxyMethodInvocation,那么抛出异常
//ReflectiveMethodInvocation属于该类型
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
//将当前MethodInvocation封装成为一个ProceedingJoinPoint连接点
//ProceedingJoinPoint类型的连接点只能是环绕通知被使用,因为该连接点可以调用proceed()方法
//其内部还是调用被包装的MethodInvocation的proceed()方法,这样就将递归调用正常延续了下去
ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
//获取当前jpMatch
JoinPointMatch jpm = getJoinPointMatch(pmi);
//调用invokeAdviceMethod方法,最终会调用invokeAdviceMethodWithGivenArgs方法
return invokeAdviceMethod(pjp, jpm, null, null);
}
/**
* @param rmi 当前的ReflectiveMethodInvocation,将用于参数绑定
* @return 环绕通知的ProceedingJoinPoint参数
*/
protected ProceedingJoinPoint lazyGetProceedingJoinPoint(ProxyMethodInvocation rmi) {
//实际类型为MethodInvocationProceedingJoinPoint
return new MethodInvocationProceedingJoinPoint(rmi);
}
/**
* MethodInvocationProceedingJoinPoint的属性
* ProxyMethodInvocation代理方法调用对象
*/
private final ProxyMethodInvocation methodInvocation;
/**
* 创建新的MethodInvocationProceedingJoinPoint,包装给定的ProxyMethodInvocation
*
* @param methodInvocation ProxyMethodInvocation代理方法调用对象
*/
public MethodInvocationProceedingJoinPoint(ProxyMethodInvocation methodInvocation) {
Assert.notNull(methodInvocation, "MethodInvocation must not be null");
this.methodInvocation = methodInvocation;
}
/**
1. MethodInvocationProceedingJoinPoint的方法
2. <p>
3. 继续执行下一个通知或目标方法调用
4. 5. @return 下一个通知或目标方法调用结果
*/
@Override
public Object proceed() throws Throwable {
//内部还是委托的methodInvocation调用的proceed方法
return this.methodInvocation.invocableClone().proceed();
}
2 CGLIB动态代理的调用
DynamicAdvisedInterceptor的intercept方法是CGLIB动态代理的调用入口,这个方法和我们上面学习了JDK动态代理的invoke方法非常相似:
- 通过getInterceptorsAndDynamicInterceptionAdvice方法获取适合该方法的拦截器链集合。
- 创建一个MethodInvocation方法调用服务,随后使用MethodInvocation调用proceed()方法,通过责任链模式和方法递归调用来实现方法的代理和增强的逻辑。
/**
* AOP配置,就是前面的proxyFactory对象
*/
private final AdvisedSupport advised;
/**
* DynamicAdvisedInterceptor的方法
* CGLIB的MethodInterceptor的intercept方法实现
* <p>
* DynamicAdvisedInterceptor拦截器位于CGLIB代理拦截器链头部
* 它的invoke方法就是对目标方法进行代理和增强的逻辑
*
* @param proxy 代理对象
* @param method 目标方法
* @param args 方法参数
* @param methodProxy 方法代理
* @return 返回值
*/
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
//是否设置代理上下文
boolean setProxyContext = false;
Object target = null;
//获取TargetSource,targetSource实际上就是对目标对象实例进行封装的对象
TargetSource targetSource = this.advised.getTargetSource();
try {
/*
* 如果exposeProxy属性为true(默认false),表示需要暴露代理对象
* 可通过<aop:config/>标签的expose-proxy属性或者@EnableAspectJAutoProxy注解的exposeProxy属性控制
*/
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
/*
* 将proxy也就是代理对象,通过AopContext.setCurrentProxy设置到AopContext内部的ThreadLocal线程本地变量中
* 这样我们就能在原始方法中通过AopContext.currentProxy()方法直接获取当前的代理对象
* 这个设置主要用来解决同一个目标类的方法互相调用时代理不生效的问题
*/
oldProxy = AopContext.setCurrentProxy(proxy);
//setProxyContext置为true
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);
/*
* 对此前设置到proxyFactory中的advisors集合中的Advisor进行筛选,因为advisors中的拦截器
* 只能说一定是匹配当前方法所属的类以及类中的某些方法,但并不一定匹配当前的方法,比如expression不匹配
* 所以这里需要获取匹配当前方法的Advisor中的拦截器链列表,用于拦截当前方法并进行增强
*
* 对于AnnotationAwareAspectJAutoProxyCreator和AspectJAwareAdvisorAutoProxyCreator自动代理创建者
* 第一个拦截器就是ExposeInvocationInterceptor,它是后面在extendAdvisors中加入进去的,其规则匹配所有方法
*/
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
//如果针对当前方法的拦截器链为空
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// 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 = methodProxy.invoke(target, argsToUse);
}
//如果针对当前方法的拦截器链不为空
else {
// We need to create a method invocation...
/*
* 创建一个CglibMethodInvocation方法调用服务,ReflectiveMethodInvocation继承了ReflectiveMethodInvocation
* 随后调用proceed方法,进行方法的代理和增强一般的AOP都是通过该方法执行的,这是核心方法
*/
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
//如果有可能,将返回值替换为代理对象本身
retVal = processReturnType(proxy, target, method, retVal);
//返回返回值
return retVal;
} finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
/**
* CglibMethodInvocation的proceed方法
* <p>
* 方法的代理和增强
*/
@Override
@Nullable
public Object proceed() throws Throwable {
try {
//内部还是调用父类ReflectiveMethodInvocation的proceed方法
return super.proceed();
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
if (ReflectionUtils.declaresException(getMethod(), ex.getClass())) {
throw ex;
} else {
throw new UndeclaredThrowableException(ex);
}
}
}
3 总结
本次我们学习了JDK和CGLIB动态代理对象的调用,也就是具体怎么调用通知方法以及对原始方法进行增强的逻辑,理解的关键就基于责任链模式的拦截器调用、方法的递归调用、try catch finally块的语义。 如果理解了这三个知识点,那么Spring AOP动态代理的执行以及各种通知的先后顺序就比较好理解了。
本文是Spring AOP源码的第三篇,也是整个通用流程最后一篇,Sping AOP的源码相比于IoC的源码确实“少了很多”,但是如果想要搞明白,我们还是必须懂得IoC容器初始化的流程,因为这里的涉及到这种回调接口,Spring AOP的前两步:AOP配置的解析以及代理对象的创建就是通过各种回调方法在IoC容器的初始化的过程中一并完成的,而最后一步,即代理方法的调用,则是由我们的程序在调用代理对象的方法时触发的!
到目前为止,通用Spring AOP的源码流程已经讲解的差不多了,但是还剩下最后一点,那就是基于注解的Spring AOP的原理,我们在第一篇的AOP文章开头就说过:基于注解和基于XML的AOP配置最终都是殊途同归的。如果我们理解了这三篇文章,那么我们只需要一篇文章就能把它说明白,下一篇文章我们将见识到基于注解的Spring AOP解析的源码,包括< aop:aspectj-autoproxy/>标签、Aspect注解、以及@EnableAspectJAutoProxy注解!
相关文章:
https://spring.io/
Spring Framework 5.x 学习
Spring Framework 5.x 源码
如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!