一、简介:
AOP源码分析主要分为5个篇章:前面2篇已经讲了
1. 注册、实例化、初始化AnnotationAwareAspectJAutoProxyCreator
【十】Spring源码分析之AOP----注册、实例化、初始化AnnotationAwareAspectJAutoProxyCreator
2. 扫描容器中的切面,创建PointcutAdvisor对象
【十一】Spring源码分析之AOP----AnnotationAwareAspectJAutoProxyCreator扫描@Aspect,创建Advisor3. 目标bean和每个Advisor匹配,找出该bean在被代理的时候需要用到哪些Advisor。就是本篇。
4. 创建代理对象
5. 被代理类的方法一次调用流程。
这一篇讲匹配出作用于被代理类Advisor
入口:AbstractAdvisorAutoProxyCreator的getAdvicesAndAdvisorsForBean方法
方法调用栈:
getAdvicesAndAdvisorsForBean方法
源码:
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
该方法调用findeEligibleAdvisors方法获得该目标bean对应的Advisor然后返回
二、 findeEligibleAdvisors
源码:
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 找到所有的advisor
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 从所有的adivisor中选出可用的
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
// 提供的hook方法,用于对目标Advisor进行扩展
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
// 对该bean用到的Advisor排序
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
做了4件事:
1.findCandidateAdvisors方法,得到所有的Advisor,该方法在上一章中介绍过。虽然在之前的shouldSkip()方法中已为当前bean生成Advisor放在缓存中,在这里获取时可以直接从缓存中获取Advisor。
2.findAdvisorsThatCanApply方法从所有的Advisor中找到可用于当前bean的Advisor。
3. 提供的hook方法,用于对目标Advisor进行扩展.
4.对该Bean用到的Advisor排序
三、 findAdvisorsThatCanApply
找出能够应用于该bean的Advisor
源码:
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
// 标记为当前代理的BeanName
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
// 调用AopUitls.findAdvisorsThatCanApply方法得到可用的的Advisor
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
// 标记为当前代理的BeanName=null
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
做了3件事:
1.标记为当前代理的BeanName
2.调用AopUitls.findAdvisorsThatCanApply方法得到可应用于该bean的Advisor
3.标记为当前代理的BeanName=null
四、 AopUtils类的findAdvisorsThatCanApply方法
源码:
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
// 如果候选Advisor为空直接返回
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
// 遍历候选Advisor
for (Advisor candidate : candidateAdvisors) {
// 如果Advisor是IntroductionAdvisor 的实例并且是可用
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
// 加入到返回列表
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
// 遍历候选Advisor
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
// 如果不是IntroductionAdvisor的实例,但是可用
if (canApply(candidate, clazz, hasIntroductions)) {
// 加入到返回列表
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
做了3件事:
1.如果候选Advisor为空,直接返回
2.遍历候选Advisor,如果Advisor是IntroductionAdvisor的实例并且是该目标Bean要用的(canAppliy返回为true,这个方法后面介绍),加入到返回列表中。
3.遍历候选Advisor,如果不是IntroductionAdvisor的实例,但是该目标Bean要用的(canAppliy返回为true,这个方法后面介绍),加入到返回列表中。
找出能够应用于该bean的Advisor
五、 AopUtils的canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions)方法
找出能够应用于该bean的Advisor
源码:
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
做了3件事:
1.如果Advisor是IntroductionAdvisor的实例,判断advisor的ClassFilter是否和被代理的类匹配,并且返回判断结果。
2.如果Advisor是PointcutAdvisor的实例,调用另一个canApply方法 。
3.既不是IntroductionAdvisor的实例也不是PointcutAdvisor的实例,直接返回true。
六、 canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions)方法
源码:
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
// 如果pointcut的classfilter和被代理的类不匹配,返回false
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
// 如果这个Advisor对任何方法都匹配,则直接返回true
MethodMatcher methodMatcher = pc.getMethodMatcher();
if (methodMatcher == MethodMatcher.TRUE) {
// No need to iterate the methods if we're matching any method anyway...
return true;
}
// 如果methodMatcher是IntroductionAwareMethodMatcher的实例,把methodMatcher赋值给IntroductionAwareMethodMatcher。
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
// 得到这个bean的所有接口
Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
// 把该bean也加入到它自己的接口集合中。
classes.add(targetClass);
// 遍历接口集合
for (Class<?> clazz : classes) {
// 得到实现的接口类的所有方法(包括父类方法),
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
// 遍历这些方法
for (Method method : methods) {
// 如果 IntroductionAwareMethodMatcher或者methodMatcher和目标bean的方法匹配,返回true。
if ((introductionAwareMethodMatcher != null &&
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
做了5件事:
1.如果pointcut的ClassFilter(这里ClassFilter指的@Aspect注解中使用的切点表达式)和被代理的类不匹配,返回false。
2.得到该pointcut切点中配置的所有方法,即是到底哪些方法是这个pointcut切点。如果这个Advisor对任何方法都匹配,则直接返回true。
3.如果methodMatcher是IntroductionAwareMethodMatcher的实例,把methodMatcher赋值给IntroductionAwareMethodMatcher。
4.得到这个目标bean的所有接口集合,把该目标bean也加入到它自己的接口集合中。遍历接口集合,得到自己或实现的接口类的所有方法(包括父类方法)。
5.遍历这些方法,如果 IntroductionAwareMethodMatcher或者methodMatcher(MethodMatcher主要指的是@Before,@After等注解中使用的切点表达式)和目标bean的方法匹配,返回true。
七、 match
判断被代理的目标bean的方法是否使用这个Advisor。
比如说被代理类叫testController,该类有个方法叫test。切面@Aspect类有2个advice增强方法,before方法叫testBefore,after方法叫testAfter。那么这个被代理类testController的test方法到底是在调用test前会调用testBefore?还是在test方法被调用后调用testAfter方法?还是test在被调用前和被调用后都需要增强?就是在这个match方法中匹配的。
AspectJExpressionPointcut类的matches方法
7.1 matches(Class<?> targetClass)
源码:
public boolean matches(Class<?> targetClass) {
checkReadyToMatch();
try {
try {
return this.pointcutExpression.couldMatchJoinPointsInType(targetClass);
}
catch (ReflectionWorldException ex) {
logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);
// Actually this is still a "maybe" - treat the pointcut as dynamic if we don't know enough yet
PointcutExpression fallbackExpression = getFallbackPointcutExpression(targetClass);
if (fallbackExpression != null) {
return fallbackExpression.couldMatchJoinPointsInType(targetClass);
}
}
}
catch (Throwable ex) {
logger.debug("PointcutExpression matching rejected target class", ex);
}
return false;
}
做了1件事:
对类进行过滤匹配,这里ClassFilter指的@Aspect注解中使用的切点表达式
7.2 matches(Method method, Class<?> targetClass, boolean beanHasIntroductions)
对方法进行过滤匹配,MethodMatcher主要指的是@Before,@After等注解中使用的切点表达式
源码:
public boolean matches(Method method, Class<?> targetClass, boolean beanHasIntroductions) {
checkReadyToMatch();
// 得到特殊方法,比如被重写的方法、接口类的实现方法、桥接方法
Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// 将对切点表达式解析后的结果与匹配的目标方法封装为一个ShadowMatch对象,并且对目标方法进行匹配,匹配的结果将存储在ShadowMatch.match参数中
ShadowMatch shadowMatch = getShadowMatch(targetMethod, method);
// Special handling for this, target, @this, @target, @annotation
// in Spring - we can optimize since we know we have exactly this class,
// and there will never be matching subclass at runtime.
if (shadowMatch.alwaysMatches()) {
return true;
}
else if (shadowMatch.neverMatches()) {
return false;
}
else {
// the maybe case
// 不确认能否匹配的时候,判断是否有Introduction类型的Advisor
if (beanHasIntroductions) {
return true;
}
// A match test returned maybe - if there are any subtype sensitive variables
// involved in the test (this, target, at_this, at_target, at_annotation) then
// we say this is not a match as in Spring there will never be a different
// runtime subtype.
// 如果不确认能否匹配,则将匹配结果封装为一个RuntimeTestWalker,在方法运行时可进行动态匹配
RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch);
return (!walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass));
}
}
做了5件事:
1. 得到特殊方法,比如被子类重写的方法、接口类的实现方法、桥接方法
2. 将对切点表达式解析后的结果与匹配的目标方法封装为一个ShadowMatch对象,并且对目标方法进行匹配,匹配的结果将存储在ShadowMatch.match参数中
3. 不确认能否匹配的时候,判断是否有Introduction类型的Advisor
4. 不确认能否匹配的时候,判断是否有Introduction类型的Advisor
5.如果不确认能否匹配,则将匹配结果封装为一个RuntimeTestWalker,在方法运行时可进行动态匹配
7.2.1 getMostSpecificMethod
源码:
public static Method getMostSpecificMethod(Method method, Class<?> targetClass) {
// 解析Method 得到子类覆盖的方法
Method resolvedMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
// If we are dealing with method with generic parameters, find the original method.
// 这是一个桥接方法
return BridgeMethodResolver.findBridgedMethod(resolvedMethod);
}
做了2件事:
1. ClassUtils.getMostSpecificMethod()得到子类覆盖的方法
2.BridgeMethodResolver.findBridgedMethod()。返回桥接方法。
java编译器采用bridge方法来兼容本该使用泛型的地方使用了非泛型的用法的问题。
7.2.2 getShadowMatch方法
真正做对切点节点匹配的逻辑的地方
private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) {
// Avoid lock contention for known Methods through concurrent access...
// 从缓存中获取ShadowMatch数据,如果缓存中存在则直接返回
ShadowMatch shadowMatch = this.shadowMatchCache.get(targetMethod);
if (shadowMatch == null) {
synchronized (this.shadowMatchCache) {
// Not found - now check again with full lock...
PointcutExpression fallbackExpression = null;
Method methodToMatch = targetMethod;
shadowMatch = this.shadowMatchCache.get(targetMethod);
if (shadowMatch == null) {
try {
try {
// 解析切点表达式与当前方法进行匹配,将匹配后的结果封装成ShadowMatch返回
shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch);
}
catch (ReflectionWorldException ex) {
// Failed to introspect target method, probably because it has been loaded
// in a special ClassLoader. Let's try the declaring ClassLoader instead...
// 如果匹配失败,则在目标方法上找切点表达式,组装成为一个回调切点表达式,并且对回调切点表达式进行解析
try {
fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
if (fallbackExpression != null) {
// 使用回调切点表达与目标方法进行匹配
shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
}
}
catch (ReflectionWorldException ex2) {
fallbackExpression = null;
}
}
if (shadowMatch == null && targetMethod != originalMethod) {
// 如果目标方法与当前切点表达式匹配失败,则用其原始方法与切点表达式匹配
methodToMatch = originalMethod;
try {
shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch);
}
catch (ReflectionWorldException ex3) {
// Could neither introspect the target class nor the proxy class ->
// let's try the original method's declaring class before we give up...
try {
fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
if (fallbackExpression != null) {
shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
}
}
catch (ReflectionWorldException ex4) {
fallbackExpression = null;
}
}
}
}
catch (Throwable ex) {
// Possibly AspectJ 1.8.10 encountering an invalid signature
logger.debug("PointcutExpression matching rejected target method", ex);
fallbackExpression = null;
}
if (shadowMatch == null) {
// 如果目标方法和原始方法都与切点表达式匹配失败,就封装一个不匹配的结果
shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);
}
// 如果通过匹配结果无法判断,就将匹配得到的ShadowMatch和回调的ShadowMatch封装到DefensiveShadowMatch中
else if (shadowMatch.maybeMatches() && fallbackExpression != null) {
shadowMatch = new DefensiveShadowMatch(shadowMatch,
fallbackExpression.matchesMethodExecution(methodToMatch));
}
// 将匹配结果缓存起来
this.shadowMatchCache.put(targetMethod, shadowMatch);
}
}
}
return shadowMatch;
}
做了6件事:
1.从缓存中获取ShadowMatch数据,如果缓存中存在则直接返回
2.如果缓存中不存在,解析切点表达式与当前方法进行匹配,将匹配后的结果封装成ShadowMatch返回。如果匹配失败,则在目标方法上找切点表达式,组装成为一个回调切点表达式,并且对回调切点表达式进行解析,使用回调切点表达与目标方法进行匹配。
3.如果目标方法与当前切点表达式匹配失败,则用其原始方法与切点表达式匹配。如果匹配失败,则在目标方法上找切点表达式,组装成为一个回调切点表达式,并且对回调切点表达式进行解析,使用回调切点表达与目标方法进行匹配。
4. 如果目标方法和原始方法都与切点表达式匹配失败,就封装一个不匹配的结果。
5.如果通过匹配结果无法判断,就将匹配得到的ShadowMatch和回调的ShadowMatch封装到DefensiveShadowMatch中。
6.将匹配结果缓存起来。