Spring源码分析之AOP从解析到调用(二)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: Spring源码分析之AOP从解析到调用

小结

其实解析切面本身并不复杂,只是Spring中将切面类封装来封装去容易使人混乱,如buildAspectJAdvisors方法中,封装了一个AspectMetadata amd = new AspectMetadata(beanType, beanName);,又立即发起判定amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON,其实这里完全可以变为AjTypeSystem.getAjType(currClass).getPerClause().getKind() == PerClauseKind.SINGLETONAjTypeSystem.getAjType(currClass)new AspectMetadata的一部分逻辑,笔者这里给大家总结一下吧。

首先,循环所有的beanName,找到带有@Aspectj注解的class, 获取到class中的所有方法进行遍历解析,取出方法注解上的值(切点:pointcut),然后把方法,切点表达式,封装了BeanFactory,BeanName的factory封装成相应的SpringAdvice, 由SpringAdvice和pointcut组合成一个advisor。

创建代理对象

切面已经解析完毕,接下来,我们就来看看如何把解析出的切面织入到目标方法中吧

但,在这之前,还有必要给小伙伴们补充一点前置知识。

我们知道,一个bean是否能够被aop代理,取决于它是否满足代理条件,即为是否能够被切点表达式所命中,而在Spring AOP中,bean与切点表达式进行匹配的是AspectJ实现的,并非Spring所完成的,所以我们先来看看AspectJ如何匹配出合适的bean的吧

栗子

首先需要引入org.aspectj:aspectjweaver依赖

一个Service,包名为com.my.spring.test.aop

package com.my.spring.test.aop;
/**
 * 切点表达式可以匹配的类
 *
 */
public class ServiceImpl{
  /**
   * 切点表达式可以匹配的方法
   */
  public void doService() {
    System.out.println("do service ...");
  }
  public void matchMethod() {
    System.out.println("ServiceImpl.notMatchMethod");
  }
}

然后,我们自己封装一个用于匹配的工具类,具体功能大家看注释哈哈

package com.my.spring.test.aspectj;
import org.aspectj.weaver.tools.PointcutExpression;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.ShadowMatch;
import java.lang.reflect.Method;
/**
 * aop工具
 */
public class AOPUtils {
  // AspectJ的固定写法,获取一个切点解析器
  static PointcutParser parser = PointcutParser
      .getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
          PointcutParser.getAllSupportedPointcutPrimitives(), ClassLoader.getSystemClassLoader());
  // 切点表达式
  private static PointcutExpression pointcutExpression;
  /**
   * 初始化工具类,我们需要先获取一个切点表达式
   *
   * @param expression 表达式
   */
  public static void init(String expression){
    // 解析出一个切点表达式
    pointcutExpression =  parser.parsePointcutExpression(expression);
  }
  /**
   * 第一次筛选,根据类筛选,也叫做粗筛
   *
   * @param targetClass 目标类
   * @return 是否匹配
   */
  public static boolean firstMatch(Class<?> targetClass){
    // 根据类筛选
    return pointcutExpression.couldMatchJoinPointsInType(targetClass);
  }
  /**
   * 第二次筛选,根据方法筛选,也叫做精筛,精筛通过则说明完全匹配
   * ps: 也可以使用该方法进行精筛,粗筛的目的是提高性能,第一次直接过滤掉不合适的类再慢慢精筛
   * 
   * @param method 方法
   * @return 是否匹配
   */
  public static boolean lastMatch(Method method){
    // 根据方法筛选
    ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method);
    return shadowMatch.alwaysMatches();
  }
}

测试

public class AOPUtilsTest {
  public static void main(String[] args) throws NoSuchMethodException {
    // 定义表达式
    String expression = "execution(* com.my.spring.test.aop.*.*(..))";
    // 初始化工具类
    AOPUtils.init(expression);
    // 粗筛
    boolean firstMatch = AOPUtils.firstMatch(ServiceImpl.class);
    if(firstMatch){
      System.out.println("第一次筛选通过");
      // 正常情况应该是获取所有方法进行遍历,我这里偷懒了~
      Method doService = ServiceImpl.class.getDeclaredMethod("doService");
      // 精筛
      boolean lastMatch = AOPUtils.lastMatch(doService);
      if(lastMatch){
        System.out.println("第二次筛选通过");
      }
      else{
        System.out.println("第二次筛选未通过");
      }
    }
    else {
      System.out.println("第一次筛选未通过");
    }
  }
}

结果(就不截图了,怀疑的小伙伴可以自己试试~)

第一次筛选通过
第二次筛选通过

当我们新建一个类Test,把切点表达式换成

execution(* com.my.spring.test.aop.Test.*(..))

测试结果为

第一次筛选未通过

再把切点表达式换成指定的方法

execution(* com.my.spring.test.aop.*.matchMethod(..))

结果

第一次筛选通过
第二次筛选未通过

到这里,小伙伴们应该明白了AspectJ的使用方法吧

代理对象创建过程

接下来,我们就来看看Spring是如何使用AspectJ匹配出相应的advisor并创建代理对象的吧,以下为创建代理对象的大致路程图

创建代理对象.png


创建代理对象是在bean初始化后完成的,所以对应的beanPostProcessor调用时机为postProcessAfterInitialization

AbstractAutoProxyCreator#postProcessAfterInitialization

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
      // 获取缓存key值,其实就是beanName
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      // 判断缓存中是否有该对象,有则说明该对象已被动态代理,跳过
      if (this.earlyProxyReferences.remove(cacheKey) != bean) {
        return wrapIfNecessary(bean, beanName, cacheKey);
      }
    }
    return bean;
  }

wrapIfNecessary

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
  // 根据bean获取到匹配的advisor
  Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
  if (specificInterceptors != DO_NOT_PROXY) {
    // 创建代理对象
    Object proxy = createProxy(
      bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    return proxy;
  }
  return bean;
}

getAdvicesAndAdvisorsForBean

protected Object[] getAdvicesAndAdvisorsForBean(
      Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
  // 获取合适的advisor
  List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
  return advisors.toArray();
}

findEligibleAdvisors

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
  // 先获取到所有的advisor, 这里和解析过程相同,由于已经解析好,所以会直接从缓存中取出
  List<Advisor> candidateAdvisors = findCandidateAdvisors();
  // 筛选出匹配的advisor
  List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
  // 增加一个默认的advisor
  extendAdvisors(eligibleAdvisors);
  if (!eligibleAdvisors.isEmpty()) {
    // 排序
    eligibleAdvisors = sortAdvisors(eligibleAdvisors);
  }
  return eligibleAdvisors;
}

findAdvisorsThatCanApply

protected List<Advisor> findAdvisorsThatCanApply(
      List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
  // 查找匹配的advisor
  return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}

findAdvisorsThatCanApply

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz){
  List<Advisor> eligibleAdvisors = new ArrayList<>();
  for (Advisor candidate : candidateAdvisors) {
    // 判断是否匹配
    if (canApply(candidate, clazz, hasIntroductions)) {
      // 加入到合适的advisors集合中
      eligibleAdvisors.add(candidate);
    }
  }
  return eligibleAdvisors;
}

canApply

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
  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;
  }
}

canApply

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
  // 第一次筛选,对class筛选判断是否满足匹配条件
  // 这里将会初始化切点表达式
  if (!pc.getClassFilter().matches(targetClass)) {
    return false;
  }
  IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
  if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
    introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
  }
  for (Class<?> clazz : classes) {
    Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
    // 循环所有方法进行第二次筛选,判断是否有方法满足匹配条件
    for (Method method : methods) {
      if (introductionAwareMethodMatcher != null ?
          introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
          methodMatcher.matches(method, targetClass)) {
        return true;
      }
    }
  }
  return false;
}

pc.getClassFilter()

public ClassFilter getClassFilter() {
  obtainPointcutExpression();
  return this;
}

obtainPointcutExpression

private PointcutExpression obtainPointcutExpression() {
  if (this.pointcutExpression == null) {
    // 确认类加载器
    this.pointcutClassLoader = determinePointcutClassLoader();
    // 创建切点表达式
    this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
  }
  return this.pointcutExpression;
}

buildPointcutExpression

private PointcutExpression buildPointcutExpression(@Nullable ClassLoader classLoader) {
  // 初始化切点解析器
  PointcutParser parser = initializePointcutParser(classLoader);
  PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length];
  for (int i = 0; i < pointcutParameters.length; i++) {
    pointcutParameters[i] = parser.createPointcutParameter(
      this.pointcutParameterNames[i], this.pointcutParameterTypes[i]);
  }
  // 使用切点解析器进行解析表达式获取切点表达式
  return parser.parsePointcutExpression(replaceBooleanOperators(resolveExpression()),
                                        this.pointcutDeclarationScope, pointcutParameters);
}

initializePointcutParser

private PointcutParser initializePointcutParser(@Nullable ClassLoader classLoader) {
  // 获得切点解析器
  PointcutParser parser = PointcutParser
    .getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
    SUPPORTED_PRIMITIVES, classLoader);
  parser.registerPointcutDesignatorHandler(new BeanPointcutDesignatorHandler());
  return parser;
}

pc.getClassFilter便是完成了以上事情,此时再进行调用matchs方法

public boolean matches(Class<?> targetClass) {
  PointcutExpression pointcutExpression = obtainPointcutExpression();
  // 使用切点表达式进行粗筛
  return pointcutExpression.couldMatchJoinPointsInType(targetClass);
}

introductionAwareMethodMatcher.matches 同样如此

以上便是寻找合适的advisor的过程,下面,就是通过这些advisor进行创建动态代理了

目录
相关文章
|
10天前
|
搜索推荐 Java Spring
Spring Filter深度解析
【10月更文挑战第21天】Spring Filter 是 Spring 框架中非常重要的一部分,它为请求处理提供了灵活的控制和扩展机制。通过合理配置和使用 Filter,可以实现各种个性化的功能,提升应用的安全性、可靠性和性能。还可以结合具体的代码示例和实际应用案例,进一步深入探讨 Spring Filter 的具体应用和优化技巧,使对它的理解更加全面和深入。
|
17天前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
27 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
2天前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
10 1
|
24天前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
|
24天前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
19天前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
46 0
|
24天前
|
XML Java 数据格式
手动开发-简单的Spring基于注解配置的程序--源码解析
手动开发-简单的Spring基于注解配置的程序--源码解析
38 0
|
24天前
|
XML Java 数据格式
手动开发-简单的Spring基于XML配置的程序--源码解析
手动开发-简单的Spring基于XML配置的程序--源码解析
72 0
|
24天前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
59 0
|
24天前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
49 0

推荐镜像

更多