AOP 链式调用
熟悉 JDK 动态代理的都知道通过代理对象调用方法时,会进入到 InvocationHandler 对象的 invoke 方法,所以我们直接从 JdkDynamicAopProxy 类的这个方法开始:
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
//从代理工厂中拿到TargetSource对象,该对象包装了被代理实例bean
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
//被代理对象的equals方法和hashCode方法是不能被代理的,不会走切面
//.......
Object retVal;
// 可以从当前线程中拿到代理对象
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
//这个target就是被代理实例
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
//从代理工厂中拿过滤器链 Object是一个MethodInterceptor类型的对象,其实就是一个advice对象
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
//如果该方法没有执行链,则说明这个方法不需要被拦截,则直接反射调用
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}
// Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
retVal = proxy;
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
}
首先来看 this.advised.exposeProxy 这个属性,这在@EnableAspectJAutoProxy 注解中可以配置,当为 true 时,会将该代理对象设置到当前线程的 ThreadLocal 对象中, 这样就可以通过 AopContext.currentProxy 拿到代理对象。
这个有什么用呢?我相信有经验的 Java 开发都遇到过这样一个 BUG,在 Service 实现类中调用本类中的另一个方法时,事务不会生效, 这是因为直接通过 this 调用就不会调用到代理对象的方法,而是原对象的,所以事务切面就没有生效。 因此这种情况下就可以从当前线程的 ThreadLocal 对象拿到代理对象,不过实际上直接使用@Autowired 注入自己本身也可以拿到代理对象。 接下来就是通过 getInterceptorsAndDynamicInterceptionAdvice 拿到执行链,看看具体做了哪些事情:
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
//从代理工厂中获得该被代理类的所有切面advisor,config就是代理工厂对象
Advisor[] advisors = config.getAdvisors();
List<Object> interceptorList = new ArrayList<>(advisors.length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
Boolean hasIntroductions = null;
for (Advisor advisor : advisors) {
//大部分走这里
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
//如果切面的pointCut和被代理对象是匹配的,说明是切面要拦截的对象
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
boolean match;
if (mm instanceof IntroductionAwareMethodMatcher) {
if (hasIntroductions == null) {
hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
}
match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
}
else {
//接下来判断方法是否是切面pointcut需要拦截的方法
match = mm.matches(method, actualClass);
}
//如果类和方法都匹配
if (match) {
//获取到切面advisor中的advice,并且包装成MethodInterceptor类型的对象
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
if (mm.isRuntime()) {
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;
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;
}
看关键的部分,因为之前我们创建的基本上都是 InstantiationModelAwarePointcutAdvisorImpl 对象, 该类是 PointcutAdvisor 的实现类,所以会进入第一个 if 判断里,这里首先进行匹配,看切点和当前对象以及该对象的哪些方法匹配,如果能匹配上,则调用 getInterceptors 获取执行链:
private final List<AdvisorAdapter> adapters = new ArrayList<>(3);
public DefaultAdvisorAdapterRegistry() {
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<>(3);
Advice advice = advisor.getAdvice();
//如果是MethodInterceptor类型的,如:AspectJAroundAdvice
//AspectJAfterAdvice
//AspectJAfterThrowingAdvice
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
//处理 AspectJMethodBeforeAdvice AspectJAfterReturningAdvice
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[0]);
}
这里我们可以看到如果是 MethodInterceptor 的实现类,则直接添加到链中,如果不是,则需要通过适配器去包装后添加, 刚好这里有 MethodBeforeAdviceAdapter 和 AfterReturningAdviceAdapter 两个适配器对应上文两个没有实现 MethodInterceptor 接口的类。最后将 Interceptors 返回。
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 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();
}
返回到 invoke 方法后,如果执行链为空,说明该方法不需要被增强,所以直接反射调用原对象的方法(注意传入的是 TargetSource 封装的被代理对象); 反之,则通过 ReflectiveMethodInvocation 类进行链式调用,关键方法就是 proceed:
private int currentInterceptorIndex = -1;
public Object proceed() throws Throwable {
//如果执行链中的advice全部执行完,则直接调用joinPoint方法,就是被代理方法
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 {
//调用MethodInterceptor中的invoke方法
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
这个方法的核心就在两个地方:invokeJoinpoint 和 interceptorOrInterceptionAdvice.invoke(this)。 当增强方法调用完后就会通过前者调用到被代理的方法,否则则是依次调用 Interceptor 的 invoke 方法。下面就分别看看每个 Interceptor 是怎么实现的。
这里的调用顺序是怎样的呢?其核心就是通过 proceed 方法控制流程,每执行完一个 Advice 就会回到 proceed 方法中调用下一个 Advice。
但是这只是只有一个切面类的情况,如果有多个@Aspect 类呢,这个调用过程又是怎样的?
AOP 扩展知识
自定义全局拦截器 Interceptor
在上文创建代理对象的时候有这样一个方法:
protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) {
//自定义MethodInterceptor.拿到setInterceptorNames方法注入的Interceptor对象
Advisor[] commonInterceptors = resolveInterceptorNames();
List<Object> allInterceptors = new ArrayList<>();
if (specificInterceptors != null) {
allInterceptors.addAll(Arrays.asList(specificInterceptors));
if (commonInterceptors.length > 0) {
if (this.applyCommonInterceptorsFirst) {
allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
}
else {
allInterceptors.addAll(Arrays.asList(commonInterceptors));
}
}
}
Advisor[] advisors = new Advisor[allInterceptors.size()];
for (int i = 0; i < allInterceptors.size(); i++) {
//对自定义的advice要进行包装,把advice包装成advisor对象,切面对象
advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
}
return advisors;
}
这个方法的作用就在于我们可以扩展我们自己的 Interceptor,首先通过 resolveInterceptorNames 方法获取到通过 setInterceptorNames 方法设置的 Interceptor, 然后调用 DefaultAdvisorAdapterRegistry.wrap 方法将其包装为 DefaultPointcutAdvisor 对象并返回:
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
if (adviceObject instanceof Advisor) {
return (Advisor) adviceObject;
}
if (!(adviceObject instanceof Advice)) {
throw new UnknownAdviceTypeException(adviceObject);
}
Advice advice = (Advice) adviceObject;
if (advice instanceof MethodInterceptor) {
return new DefaultPointcutAdvisor(advice);
}
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
return new DefaultPointcutAdvisor(advice);
}
}
throw new UnknownAdviceTypeException(advice);
}
public DefaultPointcutAdvisor(Advice advice) {
this(Pointcut.TRUE, advice);
}
需要注意 DefaultPointcutAdvisor 构造器里面传入了一个 Pointcut.TRUE,表示这种扩展的 Interceptor 是全局的拦截器。下面来看看如何使用:
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("自定义拦截器");
return invocation.proceed();
}
}
首先写一个类实现 MethodInterceptor 接口,在 invoke 方法中实现我们的拦截逻辑,然后通过下面的方式测试,只要 UserService 有 AOP 拦截就会发现自定义的 MyMethodInterceptor 也生效了。
public void costomInterceptorTest() {
AnnotationAwareAspectJAutoProxyCreator bean = applicationContext.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
bean.setInterceptorNames("myMethodInterceptor");
UserService userService = applicationContext.getBean(UserService.class);
userService.queryUser("dark");
}
但是如果换个顺序,像下面这样:
public void costomInterceptorTest() {
UserService userService = applicationContext.getBean(UserService.class);
AnnotationAwareAspectJAutoProxyCreator bean = applicationContext.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
bean.setInterceptorNames("myMethodInterceptor ");
userService.queryUser("dark");
}
这时自定义的全局拦截器就没有作用了,这是为什么呢?
因为当执行 getBean 的时候,如果有切面匹配就会通过 ProxyFactory 去创建代理对象,注意 Interceptor 是存到这个 Factory 对象中的, 而这个对象和代理对象是一一对应的,因此调用 getBean 时,还没有 myMethodInterceptor 这个对象,自定义拦截器就没有效果了, 也就是说要想自定义拦截器生效,就必须在代理对象生成之前注册进去。
其他博客参考:https://blog.csdn.net/l6108003/article/details/106577515/