【Spring AOP】【五】Spring AOP源码解析-筛选合适的通知器
1 前言
我们了解过AOP配置解析以及AOP的切入时机,那么这篇我们看看,AOP是如何给某个Bean筛选合适的通知器。
2 源码分析
2.1 方法通读
我们从AOP切入时机中,为当前bean筛选通知器开始继续看起:
protected Object[] getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) { // 为当前的bean筛选合适的通知器 List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); // 如果集合为空表示不需要代理 if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray(); } protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { // 获取所有的通知器 List<Advisor> candidateAdvisors = findCandidateAdvisors(); // 筛选哪些是可以作用到当前bean的 // 通过 ClassFilter 和 MethodMatcher 对目标类和方法进行匹配 List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); // 扩展操作 extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { // 通知器排序根据order eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; }
2.2 findAdvisorsThatCanApply
(1)派小弟AopUtils来筛选,我们继续往里走
protected List<Advisor> findAdvisorsThatCanApply( List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) { ProxyCreationContext.setCurrentProxiedBeanName(beanName); try { // 派AopUtils来进行筛选 return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass); } finally { ProxyCreationContext.setCurrentProxiedBeanName(null); } }
(2)进入findAdvisorsThatCanApply,接着进入canApply,判断通知器是否能作用到当前bean
// candidateAdvisors所有的通知器集合 public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) { if (candidateAdvisors.isEmpty()) { return candidateAdvisors; } // eligibleAdvisors存放IntroductionAdvisor类型的通知器并且可以用于当前bean的 // IntroductionAdvisor这个类型的具体是干什么的暂时未知 先略过 List<Advisor> eligibleAdvisors = new ArrayList<>(); for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); } } // hasIntroductions表示是否有元素 boolean hasIntroductions = !eligibleAdvisors.isEmpty(); for (Advisor candidate : candidateAdvisors) { // IntroductionAdvisor忽略IntroductionAdvisor类型的通知器 if (candidate instanceof IntroductionAdvisor) { // already processed continue; } // 筛选除了IntroductionAdvisor类型的通知器并且可以作用到当前bean的 if (canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); } } return eligibleAdvisors; }
(3)canApply,判断通知器是否能作用到当前bean
上一篇解析AOP的时候,不知道你是否还记得我们的每个通知都被封装成了什么对象么,我们回忆下:
我们继续看源码:
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) { if (advisor instanceof IntroductionAdvisor) { /* 从通知器中获取类型过滤器 ClassFilter,并调用 matchers 方法进行匹配。 ClassFilter 接口的实现类 AspectJExpressionPointcut 为例,该类的 匹配工作由 AspectJ 表达式解析器负责,也就是解析表达式判断当前bean符不符合,具体怎么解析的我们就不看了奥 */ return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass); } else if (advisor instanceof PointcutAdvisor) { PointcutAdvisor pca = (PointcutAdvisor) advisor; // 对于PointcutAdvisor类型的通知器,这里继续调用重载方法进行筛选 return canApply(pca.getPointcut(), targetClass, hasIntroductions); } else { // It doesn't have a pointcut so we assume it applies. return true; } }
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) { Assert.notNull(pc, "Pointcut must not be null"); // 通知器的classFilter先进行匹配 也就是类级别的先匹配才能继续匹配方法 if (!pc.getClassFilter().matches(targetClass)) { return false; } // 方法的匹配器 MethodMatcher methodMatcher = pc.getMethodMatcher(); if (methodMatcher == MethodMatcher.TRUE) { return true; } IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null; if (methodMatcher instanceof IntroductionAwareMethodMatcher) { introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher; } /* 查找当前类及其父类(以及父类的父类等等)所实现的接口,由于接口中的方法是 public, 所以当前类可以继承其父类,和父类的父类中所有的接口方法 */ Set<Class<?>> classes = new LinkedHashSet<>(); if (!Proxy.isProxyClass(targetClass)) { classes.add(ClassUtils.getUserClass(targetClass)); } classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); for (Class<?> clazz : classes) { // 获取当前类的方法列表,包括从父类中继承的方法 Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); for (Method method : methods) { // 使用 methodMatcher 匹配方法,匹配成功即可立即返回 if (introductionAwareMethodMatcher != null ? introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) : methodMatcher.matches(method, targetClass)) { return true; } } } return false; }
以上是通知器筛选的过程,筛选的工作主要由 ClassFilter 和 MethodMatcher 完成。在 AOP 中,切点 Pointcut 是用来匹配连接点(看成是方法),以 AspectJExpressionPointcut 类型的切点为例。该类型切点实现了ClassFilter 和 MethodMatcher 接口,匹配的工作则是由 AspectJ 表达式解析器复杂。除了使用 AspectJ 表达式进行匹配,Spring 还提供了基于正则表达式的切点类,以及更简单的根据方法名进行匹配的切点类。
2.3 extendAdvisors拓展通知器
关于通知器的拓展我们看看里边做了什么:
// 派小弟AspectJProxyUtils来拓展 protected void extendAdvisors(List<Advisor> candidateAdvisors) { AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors); }
public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) { // 如果通知器列表是一个空列表,直接返回 if (!advisors.isEmpty()) { boolean foundAspectJAdvice = false; for (Advisor advisor : advisors) { // 检测 每个通知器advisor是否存在类型是否正确 if (isAspectJAdvice(advisor)) { foundAspectJAdvice = true; break; } } // 往头部添加了一个默认的通知器DefaultPointcutAdvisor 我猜的哈 我怀疑是多个通知器组成链表 这个充当头吧 我猜的哈 if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) { advisors.add(0, ExposeInvocationInterceptor.ADVISOR); return true; } } return false; }
可以看到拓展主要是做了一个类型检查,主要是往通知器集合里放了一个默认的通知器,暂时不知道起到什么作用,留着等会儿研究下。
3 小结
筛选通知器,可以看到我们的通知都是根据里边的表达式进行解析匹配的,优先匹配类,如果类都不匹配则不会匹配每个方法去,行,暂时先到这里,有理解不到位的地方欢迎大家指正,我去研究下他为啥要往通知器集合里放个默认的,以及我还没看到比如一个方法有多个通知器,这多个通知器是怎么串起来的,我研究去了哈。