
1. 前言
我们知道aop实现的原理肯定是基于jdk动态代理和cglib代理,经过生成代理对象,对命中切面的方法进行增强。并将代理对象存放到ioc容器中,在其他对象需要的时候注入进去, 那么肯定 需要通过解析类 去解析 并收集 Advisor对象集合 : 需要增强的目标PointCut 以及 具体增强的逻辑Advice。 如果这个类是可以找到增强对象的,那么就应该生成代理。
2. 问题
2.1 生成的代理对象需要什么?
首先 需要进行 切面的收集和匹配
然后 把匹配到的切面 放入 生成的代理对象里,以便后续调用代理方法 来 决定调用哪个切面 来增强。
我们来看下wrapIfNecessary方法里是如何 进行切面的收集和匹配的。

3. Advisor增强对象的收集和匹配
3.1 Advisor增强对象的收集
该方法会先收集适合所有的Advisor增强对象,并过滤出匹配当前bean的Advisor对象

getAdvicesAndAdvisorsForBean

收集及匹配全在这

先看收集所有的切面findCandidateAdvisors()
这个方法是被他的子类重写并调用的,看子类AnnotationAwareAspectJAutoProxyCreator的该方法


spring自带的advisors可以不看,我们直接看我们自己写的@Aspect类

这里有两个缓存

buildAspectJAdvisors方法里会先判断缓存

第一次没有,肯定先解析再获取,我们看解析的逻辑
可以看到会遍历spring容器里的每一个beanName,然后根据beanName获取class,如果类上有Aspect注解,说明是需要解析的
将 beanFactory, beanName 封装成一个获取Advisor的工厂,然后调用 this.advisorFactory.getAdvisors(factory) 去获取advisors集合

3.1.1. 获取没有 @PointCut注解的方法集合
取出从获取advisors工厂里取出beanClass, 去遍历所有没有@PointCut注解的方法,包含什么注解也没有的方法

具体怎么获取,也很简单,遍历每一个方法,如果没有@Pointcut 注解就可以

3.1.2. 对要解析的方法集合排序
随后会对 没有@PointCut注解的方法集合进行排序, 按照这个顺序排序 : Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class

METHOD_COMPARATOR 比较器 :

3.1.3. 遍历没有@PointCut注解的方法的集合,开始创建Advisor对象
这里就开始要根据方法上面 aop相关的注解 来常见Advisor对象。
首先需要明确一点,Advisor定义的 是 增强的目标 与 增强的逻辑,
- 增强的目标 则是Pointcut对象定义的
- 增强的逻辑 则是Advice对象定义的
所以,Advisor 必须要包含Pointcut对象和 Advice对象。

3.1.3.1. Pointcut接口介绍
基于表达式的PointCut实现类是 AspectJExpressionPointcut类,它除了多持有一个表达式外,还会实现的 Pointcut接口。
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
Pointcut TRUE = TruePointcut.INSTANCE;
}
需要重写getClassFilter()来提供ClassFilter, ClassFilter是一个接口,重写matches方法,来定义是否与类匹配
@FunctionalInterface
public interface ClassFilter {
// 定义是否与类匹配
boolean matches(Class<?> clazz);
ClassFilter TRUE = TrueClassFilter.INSTANCE;
}
重写 getMethodMatcher()方法 来提供MethodMatcher 对象, MethodMatcher也是一个接口
public interface MethodMatcher {
// 定义是否与类匹配
boolean matches(Method method, Class<?> targetClass);
boolean isRuntime();
// 定义是否与方法匹配
boolean matches(Method method, Class<?> targetClass, Object... args);
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}
这里AspectJExpressionPointcut类,自己实现了ClassFilter接口,和IntroductionAwareMethodMatcher接口,可以看到实现至Pointcut接口接口的两个方法 返回的都是自身。


这里先这样,后面匹配的时候 再看这两个方法里具体的匹配逻辑。
3.1.3.2. PointCut对象的创建
进入 getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) 方法

进入 getPointcut方法,这里先获取Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class 这几个注解,匹配到就返回
不可能获取到@Pointcut注解
其实这里是不可能获取到 @Pointcut ,因为前面 遍历的整个方法的集合 就已经是排除掉了 @Pointcut注解的方法
获取需要遍历的整个方法集合代码 
getPointcut方法
注解获取到Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class注解里的其中任意一个,获取注解里的pointcutExpression属性,以及目标类的class对象,设置到创建的AspectJExpressionPointcut对象里,并返回

到这里pointCut对象已经创建完成,其实并不会去 扫描有@PointCut注解的方法,只是通过Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class里的pointcut属性,去指向@PointCut注解的方法,到时候可以 根据这个 去找 @PointCut注解的方法,再去找@PointCut里面具体配置的表达式,明确切点。
3.1.3.3. Advice对象的创建
代码回到入 getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) 方法。
会new出InstantiationModelAwarePointcutAdvisorImpl对象, 传入AspectJExpressionPointcut对象到构造方法里

InstantiationModelAwarePointcutAdvisorImpl构造方法
会先存PointCut对象以及原生Method对象,再在instantiateAdvice方法里创建Advice对象

instantiateAdvice方法里创建Advice对象

首先从原生method方法,取出 Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class 这五个注解中的一个(一个方法只能取到其中一个注解,一般只会配一个)

再判断注解,根据注解的不同,创建的Advice对象也不同

可以看到注解与创建的AspectJAroundAdvice类有如下对应关系
注解 | Aavice实现类 |
---|---|
@Around | AspectJAroundAdvice |
@Before | AspectJMethodBeforeAdvice |
@After | AspectJAfterAdvice |
@AfterReturning | AspectJAfterReturningAdvice |
@AfterThrowing | AspectJAfterThrowingAdvice |
MethodInterceptor接口
其中AspectJAroundAdvice,AspectJMethodBeforeAdvice,AspectJAfterAdvice这三个类是实现MethodInterceptor接口的
实现的invoke方法 持有 整个aop切面调用链上下文对象,以完成 切面调用链的回调,继续调用剩下的的 切面。
@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
public class AspectJAfterAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable {
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
}
public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable {
public Object invoke(){
// 继承的父类 AbstractAspectJAdvice的invoke
}
@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
public class AspectJAfterAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable {
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
}
而AspectJAfterReturningAdvice,AspectJAfterThrowingAdvice这两个切面没有,到时候aop调用的时候,会对这两个切面类进行 MethodInterceptor类型适配,以保证 调用和调用链 传递逻辑的 统一。
最终返回 各自的切面对象。

Advisor对象创建完成,存入缓存
直到所有方法都扫描完成,对应的Advisor对象 也创建成功,都存入缓存: beanName -> 对应类解析出来的 advisors

至此, 当容器内所有的beanName都遍历完成,对应的class 通过解析、创建出来的Advisor对象全部 收集完毕。

网友评论