通过阅读这篇文章,可以了解到以下几个问题:
下面我们可以带着这些疑问来看
本文依据JdkDynamicAopProxy
来分析,对CGLIB感兴趣的同学看一看ObjenesisCglibAopProxy
相关代码。 JdkDynamicAopProxy实现了InvocationHandler
接口,我们来看下invoke()
方法:
//JdkDynamicAopProxy.java
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
//如果目标对象没有定义equals()方法的话,就会直接调用而不会增强
<1> if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
//如果目标对象没有定义hashCode()方法的话,就会直接调用而不会增强
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.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,则暴露代理对象
// exposeProxy 是 @EnableAspectJAutoProxy 注解的属性之一
<2> if (this.advised.exposeProxy) {
// Make invocation available if necessary.
// 向 AopContext 中设置代理对象
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
//获得目标对象的类
<3> target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
//获取可以应用到此方法上的 Interceptor 拦截器 列表,并且排序
<4> List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
//如果没有可以应用到此方法的通知(Interceptor),此直接反射调用 method.invoke(target, args)
<5> 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);
}
<6> else {
//创建 MethodInvocation,将拦截器链放入
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 执行拦截器链
retVal = invocation.proceed();
}
// 获取方法返回值类型
<7> Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// 如果方法返回值为 this,即 return this; 则将代理对象 proxy 赋值给 retVal
retVal = proxy;
}
// 如果返回值类型为基础类型,比如 int,long 等,当返回值为 null,抛出异常
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);
}
}
}
这个invoke()方法主要有以下几个步骤:
equals()、hashCode()
等方法进行判断exposeProxy
属性,代理的暴露方式我们重点关注第<4>步和第<6>步,这两个地方非常重要,第<2>步涉及比较多,最后我们再分析,先来看下第<4>步。
代码:
//AdvisedSupport.java
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
MethodCacheKey cacheKey = new MethodCacheKey(method);
//从缓存中获取
List<Object> cached = this.methodCache.get(cacheKey);
if (cached == null) {
//获取所有的拦截器
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached);
}
return cached;
}
继续深入:
//DefaultAdvisorChainFactory.java
/**
* 从提供的配置实例config中获取advisor列表,遍历处理这些advisor.如果是IntroductionAdvisor,
* 则判断此Advisor能否应用到目标类targetClass上.如果是PointcutAdvisor,则判断
* 此Advisor能否应用到目标方法method上.将满足条件的Advisor通过AdvisorAdaptor转化成Interceptor列表返回.
*/
@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
List<Object> interceptorList = new ArrayList<>(config.getAdvisors().length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
//查看是否包含IntroductionAdvisor
boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
//这里实际上注册一系列AdvisorAdapter,用于将 通知Advisor 转化成 方法拦截器MethodInterceptor
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
//遍历
for (Advisor advisor : config.getAdvisors()) {
if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
//通过ClassFilter对当前Bean类型进行匹配
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
//将 通知Advisor 转化成 拦截器Interceptor
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
//检查当前advisor的pointcut是否可以匹配当前方法
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
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) {
//加入拦截器链
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
//加入拦截器链
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
// IntroductionAdvisor 类型的通知器,仅进行类级别的匹配即可
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;
}
这里的主逻辑不是很复杂:
这里返回的拦截器链是有序的,按照afterReturn、after、around、before排好序,(具体排序是在获取所有通知的时候sortAdvisors(eligibleAdvisors)
),方便后面执行。
现在有了拦截器链,接下来再看下怎么执行的:
//ReflectiveMethodInvocation.java
//当前拦截器下标
private int currentInterceptorIndex = -1;
//拦截器链集合
protected final List<?> interceptorsAndDynamicMethodMatchers;
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// 拦截器链中的最后一个拦截器执行完
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
//执行目标方法
return invokeJoinpoint();
}
//每次执行新的拦截器,下标+1
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;
// 如果是动态切点,需要进行参数匹配,以确定是否需要执行该动态横切逻辑
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
//动态切点匹配失败,略过当前Intercetpor,调用下一个Interceptor
return proceed();
}
}
else {
//执行当前Intercetpor
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
proceed 根据currentInterceptorIndex
按照倒序执行拦截器链,每次执行完+1,当所有拦截器执行完后,再执行目标方法。 这里我们可以提出几个疑问:
我们带着这2个疑问往下看,
proceed中的invoke(this)
方法根据通知的类型,有不同的实现类,如:
由于拦截器链是按照倒序排列的,我们先来看下 后置通知 的代码:
//AspectJAfterAdvice.java
public class AspectJAfterAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable {
public AspectJAfterAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
//执行下一个拦截器链
return mi.proceed();
}
finally {
//执行后置逻辑
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
@Override
public boolean isBeforeAdvice() {
return false;
}
@Override
public boolean isAfterAdvice() {
return true;
}
}
可以看到,后置拦截器是会先继续执行下一个拦截器,当拦截器链执行完毕之后,proceed()
再执行目标方法,最后执行后置逻辑。
我们再来看下环绕拦截器:
//AspectJAroundAdvice.java
public Object invoke(MethodInvocation mi) throws Throwable {
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
JoinPointMatch jpm = getJoinPointMatch(pmi);
//执行环绕逻辑
return invokeAdviceMethod(pjp, jpm, null, null);
}
环绕拦截器会直接执行环绕逻辑,而 由于我们之前在LogAspect
中配置了如下代码:
@Aspect
@Component
@EnableAspectJAutoProxy
public class LogAspect {
@Pointcut("execution(* com.mydemo.work.StudentController.getName(..))")
private void log(){}
@Before("log()")
public void doBefore() {
System.out.println("===before");
}
@After("execution(* com.mydemo.work.StudentController.getName(..))")
public void doAfter() {
System.out.println("===after");
}
@AfterReturning("execution(* com.mydemo.work.StudentController.getName(..))")
public void doAfterReturn() {
System.out.println("===afterReturn");
}
@Around("execution(* com.mydemo.work.StudentController.getName(..))")
public void doAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("===around before");
pjp.proceed();
System.out.println("===around after");
}
}
所以该拦截器会先执行========around before
,然后再执行下一个拦截器。
我们再来看下前置拦截器:
//MethodBeforeAdviceInterceptor.java
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
private MethodBeforeAdvice advice;
/**
* Create a new MethodBeforeAdviceInterceptor for the given advice.
* @param advice the MethodBeforeAdvice to wrap
*/
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
//执行前置逻辑
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
//执行下一个拦截器
return mi.proceed();
}
}
可以看到前置拦截器在执行完 前置方法 之后,又调用MethodInvocation#proceed()
方法,继续去执行下一个拦截器。
到这里大家可能有点绕,我们来捋一下通知的执行顺序,也就是拦截器的执行顺序。 首先,前面说过拦截器链是按照倒序排序的,如下:
可以看到这里的通知是按照:afterReturn、after、around、before排序的。
画一下执行流程图:
再用单元测试跑一遍LogAspect
的相关代码,打印结果如下:
===around before
===before
do getName
===around after
===after
===afterReturn
和我们猜测的一样。现在我们可以回答前面的问题了,就是一个方法匹配多个通知时,按照什么顺序执行。
但是,在实际中,通常一个方法可能被多个Aspect拦截,比如我们想让业务方法先被 日志Aspect 拦截,然后被 异常Aspect拦截。
Spring框架已经替我们想到了这个问题,如果我们有多个Aspect,实际上它们会随机执行,没有明确的顺序。但是Spring提供了@Order
注解,可以让我们指定Aspect的执行顺序。 比如我们新加一个处理异常的Aspect:
@Aspect
@Component
@EnableAspectJAutoProxy
@Order(2)
public class ErrorAspect {
@Pointcut("execution(* com.mydemo.work.StudentController.getName(..))")
private void log(){}
@Before("log()")
public void doBeforeError() {
System.out.println("=== error before");
}
@After("execution(* com.mydemo.work.StudentController.getName(..))")
public void doAfterError() {
System.out.println("=== error after");
}
@AfterReturning("execution(* com.mydemo.work.StudentController.getName(..))")
public void doAfterReturnError() {
System.out.println("=== error afterReturn");
}
@Around("execution(* com.mydemo.work.StudentController.getName(..))")
public void doAroundError(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("=== error around before");
pjp.proceed();
System.out.println("=== error around after");
}
}
同时给LogAspect
加上注解@Order(1)
,也就意味着LogAspect
的通知要比ErrorAspect
先执行。 先来单元测试跑一下看看结果:
===around before
===before
=== error around before
=== error before
do getName
=== error around after
=== error after
=== error afterReturn
===around after
===after
===afterReturn
可以看到,确实是LogAspect
的通知先执行了,这是为什么呢?我们来debug源码看一下,主要看拦截器链,如图:
可以看到,拦截器链已经按照顺序排列好了,所以代码只要按照这个拦截器链顺序执行,就能保证2个切面有序拦截。 那么它为什么要这样排序呢?我们来画个图:
如上图所示,这2个Aspect就像2个圆圈在外面拦截,中间是目标方法。 当一个请求进来要执行目标方法:
@Order(1)
拦截器拦截@Order(2)
拦截器拦截@Order(2)
的后置拦截器@Order(1)
的后置拦截器到这里我们多个通知的执行顺序就分析完了,这里面还是要去从源码层面理解,不能死记硬背,这样才能记得牢固。
上面我们分析invoke()
方法的时候,有下面这段代码:
// 如果 exposeProxy 属性为 true,则暴露代理对象
// exposeProxy 是 @EnableAspectJAutoProxy 注解的属性之一
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
// 向 AopContext 中设置代理对象
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
本小节我们就来详细分析一下这里是什么意思。
首先 exposeProxy 是注解@EnableAspectJAutoProxy
中的一个属性,它可以被设置为true或false,默认是false。
这个属性是为了解决目标方法调用同对象中其他方法时,其他方法的切面逻辑无法执行的问题。
啥意思呢?我们来举个例子:
public class Student implements Person {
@Override
public void haveFun() {
System.out.println("篮球");
this.hello("足球");
}
@Override
public void haveFun(String action) {
System.out.println("haveFun " + action);
}
}
在同一个类Student.class
中,一个haveFun()
方法调用了本类中的另一个haveFun(String action)
方法,此时haveFun(String action)
上的切面逻辑就无法执行了。
那为什么会这样呢?
我们知道AOP的本质就是给目标对象生成一个代理对象,对原本的方法进行增强,之后执行的方法都是我们代理类中的方法。 而
haveFun()
方法中使用的是this.hello("足球")
,这里的this不是代理对象,而是原始对象,因此通过原始对象调用haveFun(String action)
是不会被增强的,所以切面就不会起作用。
那么问题又来了,为什么这里的this不是代理对象,而是原始对象呢?
上面代码中有一个ReflectiveMethodInvocation#proceed()
方法如下:
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// 拦截器链中的最后一个拦截器执行完
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
//执行目标方法
return invokeJoinpoint();
}
//...省略
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
//...省略
}
else {
//执行当前Intercetpor
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
在所有拦截器执行完之后,会去执行目标方法,我们跟踪这个方法invokeJoinpoint()
//ReflectiveMethodInvocation.java
protected Object invokeJoinpoint() throws Throwable {
//重点 this.target
return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}
//ReflectiveMethodInvocation.java
public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)
throws Throwable {
// Use reflection to invoke the method.
try {
ReflectionUtils.makeAccessible(method);
//重点 target
return method.invoke(target, args);
}
//...省略
}
可以看到,这里调用的是原始目标对象target
来执行我们的目标方法,所以此时haveFun()
方法中的this.hello("足球")
的这个this其实是原始目标对象。
所以exposeProxy
这个属性就是来解决这个问题的,那么它是如何起作用的呢? 我们回过头来看代码:
// 如果 exposeProxy 属性为 true,则暴露代理对象
// exposeProxy 是 @EnableAspectJAutoProxy 注解的属性之一
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
// 向 AopContext 中设置代理对象
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
继续跟踪:
//AopContext.java
//存储代理对象 ThreadLocal
private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy");
static Object setCurrentProxy(@Nullable Object proxy) {
Object old = currentProxy.get();
if (proxy != null) {
//存储代理对象到 ThreadLocal
currentProxy.set(proxy);
}
else {
currentProxy.remove();
}
return old;
}
如果exposeProxy
被设置为了true,会把代理对象存到ThreadLocal
中,而在本类中调用的时候,会从ThreadLocal
中获取代理类来调用目标方法,就可以解决本来调用的问题。
最后再来看下面这种情况:
public class Student implements Person {
@Override
@Transactional
public void haveFun() {
System.out.println("篮球");
this.hello("足球");
}
@Override
@Transactional
public void haveFun(String action) {
System.out.println("haveFun " + action);
}
}
这种情况下,haveFun(String action)
的事务不会生效,原因就是我们刚才分析的。实际上@Transactional
本质上也是AOP实现的,所以也会有这个问题。
解决方案就是给@EnableAspectJAutoProxy
注解设置exposeProxy=true
总结
时序图:
到这里SpringAOP的源码分析就告一段落了,由于本人经验和技术水平有限,所以只能先了解这么多,如有错误,欢迎提出意见。