Skip to content

Latest commit

 

History

History
1314 lines (1024 loc) · 49.5 KB

2024-02-02-AOP.md

File metadata and controls

1314 lines (1024 loc) · 49.5 KB
layout title date categories tags series series_index comments copyrights mathjax
post
AOP
2024-02-02 00:00:00 +0800
编程
java spring
深入 Spring 源码
2
true
原创
true

Spring 是一个开源的轻量级 JavaEE 框架。它的核心是控制反转(IoC)和面向切面编程(AOP)。Spring 的 IoC 容器负责管理 JavaBean 的生命周期,而 AOP 容器负责管理切面。Spring 还提供了一系列的模块,如 Spring MVC、Spring JDBC、Spring Security 等。

AOP

为什么需要 AOP

我们再次回顾一下三层架构:

倒金字塔型

在实际开发中,我们的业务逻辑并不是完全自上而下的,日志、事务、权限控制等横向贯穿在各个层次之间。这些辅助功能被成为切面(Aspect)。

在严格的 OOP 中,如果要向业务代码中添加日志功能,需要这样做:

@Service
public class CalculatorServiceImpl implements CalculatorService {
    @Autowired
    private CalculatorDao calculatorDao;

    public int add(int a, int b) {
        System.out.println("[INFO] add method start");
        int result = calculatorDao.add(a, b);
        System.out.println("[INFO] add method end");
        return result;
    }

    public int subtract(int a, int b) {
        System.out.println("[INFO] subtract method start");
        int result = calculatorDao.subtract(a, b);
        System.out.println("[INFO] subtract method end");
        return result;
    }

    /* ... */
}

可以看到,这样做有几个问题:

  • 代码冗余

    每个方法都需要写高度雷同日志代码,导致了代码冗余。

  • 耦合度高

    业务代码和日志代码耦合在一起,导致了耦合度过高,不利于集中维护。

  • 横切关注点

    日志代码是横切关注点,它贯穿在各个方法中,但是却和业务逻辑无关。

AOP 的目的就是解决这些问题。它将横切关注点从业务代码中剥离出来,使得业务代码更加简洁、清晰。

它和 Python 中的装饰器有点类似,都是对原先的代码进行进一步包装,在其运行前、运行后、运行中额外执行一些代码。

AOP 术语

  • 横切关注点(Cross-cutting Concern)

    横切关注点是指那些和业务逻辑无关,但是贯穿在各个方法中的代码。例如日志、事务、权限控制等。

  • 增强(Advice)

    增强是指在横切关注点中需要执行的代码。例如输出日志、开启事务、检查权限等。它有以下几种类型:

    • 前置增强(Before):在目标方法执行前执行
    • 后置增强(After):在目标方法执行后执行
    • 返回增强(After Returning):在目标方法返回结果后执行
    • 异常增强(After Throwing):在目标方法抛出异常后执行
    • 环绕增强(Around):在目标方法执行前后执行,包括了前面四种增强
  • 切面(Aspect)

    切面是横切关注点和增强结合而成的类。

  • 目标对象(Target)

    目标对象是被增强的对象。例如 CalculatorServiceImpl

  • 代理对象(Proxy)

    代理对象是 Spring 生成的代理对象,它将目标对象和切面结合在一起。

  • 连接点(Join Point)

    连接点是指在程序执行过程中能够插入切面的点。例如方法调用、方法执行、异常抛出等。

  • 切入点(Pointcut)

    切入点是指用于定位连接点的表达式。

代理模式

在讲解 AOP 之前,我们先来看看代理模式。

静态代理

在静态代理中,代理类和目标类实现了同一个接口,代理类中持有目标类的引用,通过调用目标类的方法来实现代理:

public interface CalculatorService {
    int add(int a, int b);
}

public class CalculatorServiceImpl implements CalculatorService {
    public int add(int a, int b) {
        return a + b;
    }
}

public class CalculatorServiceProxy implements CalculatorService {
    private CalculatorService calculatorService;

    public CalculatorServiceProxy(calculatorService) {
        this.calculatorService = calculatorService;
    }

    public int add(int a, int b) {
        System.out.println("[INFO] add method start");
        int result = calculatorService.add(a, b);
        System.out.println("[INFO] add method end");
        return result;
    }
}

public class TestCalculator {
    @Test
    public void testCalculator() {
        CalculatorService calculatorService = new CalculatorServiceImpl();
        CalculatorService calculatorServiceProxy = new CalculatorServiceProxy(calculatorService);
        int result = calculatorServiceProxy.add(1, 2);
    }
}

这种方法只能代理一个接口,如果有多个接口需要代理,就需要写多个代理类。

动态代理

在动态代理中,代理类和目标类没有实现同一个接口,代理类通过实现 InvocationHandler 接口来实现代理:

public interface CalculatorService {
    int add(int a, int b);
}

public class CalculatorServiceImpl implements CalculatorService {
    public int add(int a, int b) {
        return a + b;
    }
}

public class CalculatorServiceProxy implements InvocationHandler {
    private Object target;

    public CalculatorServiceProxy(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("[INFO] " + method.getName() + " method start, args: " + Arrays.toString(args));
        Object result = method.invoke(target, args);
        System.out.println("[INFO] " + method.getName() + " method end");
        return result;
    }
}

public class TestCalculator {
    @Test
    public void testCalculator() {
        CalculatorService calculatorService = new CalculatorServiceImpl();
        CalculatorService calculatorServiceProxy = (CalculatorService) Proxy.newProxyInstance(
                calculatorService.getClass().getClassLoader(),
                calculatorService.getClass().getInterfaces(),
                new CalculatorServiceProxy(calculatorService)
        );
        int result = calculatorServiceProxy.add(1, 2);
    }
}

这里的 Proxy.newProxyInstance 方法会返回一个代理对象,它实现了 CalculatorService 接口,通过 CalculatorServiceProxy 来实现代理。

在 Spring 中,AOP 使用的就是动态代理。它通过将以上内容封装进一个类,来实现代理。

AOP 使用

Spring 提供了两种 AOP 使用方式:基于 XML 配置和基于注解配置。我们先来看看基于注解配置的方式。

首先,我们需要在配置类中开启 AOP:

@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "com.example")
public class AppConfig {
}

这里,@EnableAspectJAutoProxy 注解用于开启 AOP,它会自动扫描 @Aspect 注解标记的类,并将其注册为切面,创建代理对象。

然后,我们需要定义一个切面类。我们将各种增强都实现了一下:

@Aspect
@Component
public class LogAspect {
    @Before("execution(* com.example.CalculatorService.*(..))")
    public void before(JoinPoint joinPoint) {
        System.out.println("[INFO] " + joinPoint.getSignature().getName() + " method start, args: " + Arrays.toString(joinPoint.getArgs()));
    }

    @After("execution(* com.example.CalculatorService.*(..))")
    public void after(JoinPoint joinPoint) {
        System.out.println("[INFO] " + joinPoint.getSignature().getName() + " method end");
    }

    @AfterReturning(pointcut = "execution(* com.example.CalculatorService.*(..))", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("[INFO] " + joinPoint.getSignature().getName() + " method return " + result);
    }

    @AfterThrowing(pointcut = "execution(* com.example.CalculatorService.*(..))", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Throwable e) {
        System.out.println("[ERROR] " + joinPoint.getSignature().getName() + " method throw " + e);
    }

    @Around("execution(* com.example.CalculatorService.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = null;

        try {
            System.out.println("[INFO] " + joinPoint.getSignature().getName() + " method start, args: " + Arrays.toString(joinPoint.getArgs()));
            result = joinPoint.proceed();
            System.out.println("[INFO] " + joinPoint.getSignature().getName() + " method return " + result);
        } catch (Throwable e) {
            System.out.println("[ERROR] " + joinPoint.getSignature().getName() + " method throw " + e);
            throw e;
        } finally {
            System.out.println("[INFO] " + joinPoint.getSignature().getName() + " method end");
        }

        return result;
    }
}

这里:

  • @Component 表明这依然是一个 Bean

  • @Aspect 表明这是一个切面

  • @Before@After@AfterReturning@AfterThrowing@Around 分别表示前置增强、后置增强、返回增强、异常增强和环绕增强

    这些增强都需要一个切入点表达式来定位连接点。切入点表达式使用的是 SpEL 表达式。我们会在下文中讲解。

然后,CalculatorServiceImpl 中什么也不用做,就能正常输出日志了:

@Service
public class CalculatorServiceImpl implements CalculatorService {
    public int add(int a, int b) {
        int result = a + b;
        return result;
    }

    public int subtract(int a, int b) {
        int result = a - b;
        return result;
    }

    /* ... */
}

这样,我们就实现了 AOP。

对于有多个切面的情况,可以使用 @Order 注解来指定切面的优先级:

@Aspect
@Component
@Order(1)
public class LogAspect {
    /* ... */
}

@Aspect
@Component
@Order(2)
public class TransactionAspect {
    /* ... */
}

这样,TransactionAspect 的优先级高于 LogAspect,会先执行 TransactionAspect

切入点表达式

在上面的例子中,我们使用了切入点表达式来定位连接点。切入点表达式是一个字符串,它有以下几种:

  • execution:用于匹配方法执行的连接点

    语法:execution([访问修饰符] 返回类型 [包名.类名].方法名(参数) [异常])

    • 访问修饰符:publicprotectedprivate、省略

    • 返回类型:*、具体类型

    • 包名:* 表示一层任意包,.. 表示当前包及其子包

      例如,*.example 表示某一层包下的 example 包,com.example..* 表示 com.example 包及其子包下的所有类

    • 类名:* 表示任意类,*Service 表示以 Service 结尾的类

    • 方法名:* 表示任意方法,add* 表示以 add 开头的方法

    • 参数:(..) 表示任意参数,(*) 表示一个参数,(*, *) 表示两个参数,(*, String) 表示第一个参数任意,第二个参数为 String 类型

  • within:用于匹配指定类型内的方法执行连接点,例如 within(com.example..*) 表示 com.example 包及其子包下的所有类

    语法:within([包名.类名])

  • this:用于匹配当前代理对象类型的方法执行连接点,例如 this(com.example.CalculatorService) 表示当前代理对象为 CalculatorService 类型的方法执行连接点

    语法:this([包名.类名])

  • target:用于匹配当前目标对象类型的方法执行连接点,例如 target(com.example.CalculatorService) 表示当前目标对象为 CalculatorService 类型的方法执行连接点

    语法:target([包名.类名])

  • args:用于匹配参数类型的方法执行连接点,例如 args(int) 表示参数为 int 类型的方法执行连接点

    语法:args([参数类型])

  • @annotation:用于匹配标注了指定注解的方法执行连接点,例如 @annotation(com.example.Log) 表示标注了 Log 注解的方法执行连接点

    语法:@annotation([注解类型])

  • bean:用于匹配指定 Bean 的方法执行连接点,例如 bean(calculatorService) 表示 Bean 名称为 calculatorService 的方法执行连接点

    语法:bean([Bean 名称])

切入点表达式也可以一次定义,多次使用:

@Pointcut("execution(* com.example.CalculatorService.*(..))")
public void pointcut() {}

@Before("pointcut()")
public void before(JoinPoint joinPoint) {
    System.out.println("[INFO] " + joinPoint.getSignature().getName() + " method start, args: " + Arrays.toString(joinPoint.getArgs()));
}

这里的 pointcut() 方法被称为切入点方法,它的返回值类型可以是 voidStringObject,也可以没有返回值。

如果要在不同的切面中使用,可以:

@Before("com.example.LogAspect.pointcut()")
public void before(JoinPoint joinPoint) {
    System.out.println("[INFO] " + joinPoint.getSignature().getName() + " method start, args: " + Arrays.toString(joinPoint.getArgs()));
}

基于 XML 配置

点击展开

如果不适用配置类,也可以在 XML 配置文件中开启 AOP:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
  
      <aop:aspectj-autoproxy/>
      <context:component-scan base-package="com.example"/>

</beans>

AOP 同样能够完全基于 XML 配置:

<bean id="logAspect" class="com.example.LogAspect"/>

<aop:config>
    <aop:aspect ref="logAspect">
        <aop:pointcut id="pointcut" expression="execution(* com.example.CalculatorService.*(..))"/>
        <aop:before method="before" pointcut-ref="pointcut"/>
        <aop:after method="after" pointcut-ref="pointcut"/>
        <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
        <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
        <aop:around method="around" pointcut-ref="pointcut"/>
    </aop:aspect>
</aop:config>

AOP 源码解读

我们来看看 Spring 是如何实现 AOP 的。

在这之前,我们先回顾一下之前在 IoC 的部分提到,AOP 的执行是在属性注入和初始化方法之后、Bean 就绪之前进行的。

AutoProxyCreator

代理创建器是 AOP 的核心,它负责创建代理对象。

它也会和其它类一样,一起被注册为 BeanDefinition。然后,Spring 会先注册一个 AutoProxyCreator,然后再初始化其它 Bean。到了 postProcessAfterInitialization 阶段,代理创建器会扫描所有的 Bean,找到需要代理的 Bean,然后为其创建代理对象。创建后,代理后的 Bean 会被注册到容器中。

我们这里就是扫描 @EnableAspectJAutoProxy 注解,然后注册 AutoProxyCreator 为 BeanDefinition。

点击查看 AutoProxyCreator 源码解读

先来看 @EnableAspectJAutoProxy 注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
    // 是否使用 CGLIB 代理(false 表示 JDK 代理)
    boolean proxyTargetClass() default false;
    // 是否暴露代理对象,即支持目标内部调用
    boolean exposeProxy() default false;
}

这里的 @Import 注解表明要导入 AspectJAutoProxyRegistrar 类。我们来看看这个类:

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(
        AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 注册 AutoProxyCreator
        // 这个类是一个 BeanPostProcessor,负责扫描带有 @Aspect 的 Bean,并为需要代理的 Bean 创建 AOP 代理
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

        // 解析 @EnableAspectJAutoProxy 注解的属性
        // 配置 proxyTargetClass 和 exposeProxy
        AnnotationAttributes enableAspectJAutoProxy =
            AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }
}

这里只是处理了下之前提到的两个属性,proxyTargetClassexposeProxy

我们继续跟踪 registerAspectJAnnotationAutoProxyCreatorIfNecessary 方法,最终来到

@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
    Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {

    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

    // 如果已经注册了
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        // 判断优先级,如果更高,则替换
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            int requiredPriority = findPriorityForClass(cls);
            if (currentPriority < requiredPriority) {
                apcDefinition.setBeanClassName(cls.getName());
            }
        }
        return null;
    }

    // 将代理后的 Bean 注册到容器中
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    // 设置为最高优先级,确保 BeanPostProcessor 优先执行
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    // 设置为基础设施 Bean,不给应用代码暴露
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}

总体来讲,就是创建了一个 AutoProxyCreator 的 BeanDefinition,并将其注册到容器中。

postProcessBeforeInstantiation

在初始化 Bean 之前,会调用 postProcessBeforeInstantiation 方法。

对于 AOP 流程来讲,这时会加载项目中的增强方法。

点击查看 postProcessBeforeInstantiation 源码解读

我们先从 postProcessBeforeInstantiation 方法开始。它有多个实现类,我们这里需要看的是 AbstractAutoProxyCreator 类的实现:

@Override
@Nullable
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    // 结合 Bean 类和名称生成唯一标识
    Object cacheKey = getCacheKey(beanClass, beanName);

    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
        // 如果已经找到过这个基础类或切面类,则跳过
        if (this.advisedBeans.containsKey(cacheKey)) {
            return null;
        }
        // 如果是基础类(Adivce 类等)或者是 @Aspect 注解的切面类,放入 advisedBeans,并跳过(下文会详细讲解)
        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return null;
        }
    }

    // 如果有自定义的 TargetSource(例如池化、懒加载等),赶紧创建代理
    // 我们这里不关心这块
    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
    if (targetSource != null) {
        // 标记为已处理
        if (StringUtils.hasLength(beanName)) {
            this.targetSourcedBeans.add(beanName);
        }

        // 获取和当前 Bean 相关的增强器
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
        // 创建代理
        Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
        // 将代理类型缓存
        this.proxyTypes.put(cacheKey, proxy.getClass());
        // 返回代理后的 Bean
        return proxy;
    }

    return null;
}
  • isInfrastructureClass(beanClass) 方法:

    这个方法判断当前 Bean 是否是基础设施类,这里主要判断 Bean 是否是 AdvicePointcutAdvisorAopInfrastructureBean 这四个类的子类。

    protected boolean isInfrastructureClass(Class<?> beanClass) {
        // 判断当前 Bean 是否是基础设施类,包括
        // Advice
        // Pointcut
        // Advisor
        // AopInfrastructureBean
        boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
            Pointcut.class.isAssignableFrom(beanClass) ||
            Advisor.class.isAssignableFrom(beanClass) ||
            AopInfrastructureBean.class.isAssignableFrom(beanClass);
        if (retVal && logger.isTraceEnabled()) {
            logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
        }
        return retVal;
    }
  • shouldSkip(beanClass, beanName)

    注意,我们不要直接一路跟踪进去。这个函数在 AbstractJAwareAdvisorAutoProxyCreator 中被重写了:

    @Override
    protected boolean shouldSkip(Class<?> beanClass, String beanName) {
        // 获取候选的 Advisor 列表(下文会详细讲解)
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        for (Advisor advisor : candidateAdvisors) {
            // 检查切面匹配
            // 筛选出 @Aspect 注解的切面,并且切面名称和当前 Bean 名称相同
            if (advisor instanceof AspectJPointcutAdvisor pointcutAdvisor &&
                pointcutAdvisor.getAspectName().equals(beanName)) {
                return true;
            }
        }
        // 检查是否以 .ORIGIN 结尾,即已经被代理过的原始类
        return super.shouldSkip(beanClass, beanName);
    }

    这个函数主要是检查当前 Bean 是否是 @Aspect 注解的切面

    我们跟踪进 findCandidateAdvisors() 方法看一下。注意,我们先进入其覆写方法:

    @Override
    protected List<Advisor> findCandidateAdvisors() {
        // 根据缓存的 Advisor Bean 名称来查找所有的 Advisor(下文会详细讲解)
        List<Advisor> advisors = super.findCandidateAdvisors();
        if (this.aspectJAdvisorsBuilder != null) {
            // 扫描容器中所有 @Aspect 注解的 Bean,并生成对应的 Advisor(下文会详细讲解)
            advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        }
        return advisors;
    }

    挨个来看:

    • super.findCandidateAdvisors()

      这个方法会根据缓存的 Advisor Bean 名称来查找所有的 Advisor:

      public List<Advisor> findAdvisorBeans() {
          // 获取所有的 Advisor Bean 名称
          String[] advisorNames = this.cachedAdvisorBeanNames;
      
          if (advisorNames == null) {
              // 查找 Bean 名称
              advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false);
              this.cachedAdvisorBeanNames = advisorNames;
          }
      
          if (advisorNames.length == 0) {
              return new ArrayList<>();
          }
      
          // 遍历并筛选出符合条件的 Advisor
          List<Advisor> advisors = new ArrayList<>();
          for (String name : advisorNames) {
              // 过滤符合条件的 Advisor(可能会指定前缀)
              if (isEligibleBean(name)) {
                  // 跳过当前正在创建的 Advisor
                  if (this.beanFactory.isCurrentlyInCreation(name)) {
                      if (logger.isTraceEnabled()) {
                          logger.trace("Skipping currently created advisor '" + name + "'");
                      }
                  }
                  else {
                      try {
                          // 添加到候选列表
                          advisors.add(this.beanFactory.getBean(name, Advisor.class));
                      }
                      catch (BeanCreationException ex) {
                          Throwable rootCause = ex.getMostSpecificCause();
                          if (rootCause instanceof BeanCurrentlyInCreationException bce) {
                              String bceBeanName = bce.getBeanName();
                              if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
                                  if (logger.isTraceEnabled()) {
                                      logger.trace("Skipping advisor '" + name +
                                          "' with dependency on currently created bean: " + ex.getMessage());
                                  }
                                  continue;
                              }
                          }
                          throw ex;
                      }
                  }
              }
          }
          return advisors;
      }
    • buildAspectJAdvisors()

      这个方法会扫描容器中所有 @Aspect 注解的 Bean,并生成对应的 Advisor:

      public List<Advisor> buildAspectJAdvisors() {
          // 缓存优化,如果首次调用则没用
          List<String> aspectNames = this.aspectBeanNames;
      
          // 没有缓存的情况
          if (aspectNames == null) {
              synchronized (this) {
                  aspectNames = this.aspectBeanNames;
                  if (aspectNames == null) {
                      // ​获取所有 Bean 名称
                      List<Advisor> advisors = new ArrayList<>();
                      aspectNames = new ArrayList<>();
                      String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
      
                      // 遍历并筛选出符合条件的 Bean
                      for (String beanName : beanNames) {
                          if (!isEligibleBean(beanName)) {
                              continue;
                          }
      
                          // 获取 Bean 的类型并判断是否被 @Aspect 注解标记
                          Class<?> beanType = this.beanFactory.getType(beanName, false);
                          if (beanType == null) {
                              continue;
                          }
                          if (this.advisorFactory.isAspect(beanType)) {
                              try {
                                  // 解析 @Aspect 注解
                                  AspectMetadata amd = new AspectMetadata(beanType, beanName);
      
                                  // 处理单例 Aspect
                                  if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                                      // 创建 Aspect 实例工厂
                                      MetadataAwareAspectInstanceFactory factory =
                                          new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
      
                                      // 解析通知方法并生成 Advisor(下文会详细讲解)
                                      List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
      
                                      // 处理缓存
                                      if (this.beanFactory.isSingleton(beanName)) {
                                          this.advisorsCache.put(beanName, classAdvisors);
                                      }
                                      else {
                                          this.aspectFactoryCache.put(beanName, factory);
                                      }
                                      advisors.addAll(classAdvisors);
                                  }
      
                                  // 处理非单例 Aspect
                                  else {
                                      if (this.beanFactory.isSingleton(beanName)) {
                                          throw new IllegalArgumentException("Bean with name '" + beanName +
                                              "' is a singleton, but aspect instantiation model is not singleton");
                                      }
                                      MetadataAwareAspectInstanceFactory factory =
                                          new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                                      this.aspectFactoryCache.put(beanName, factory);
                                      advisors.addAll(this.advisorFactory.getAdvisors(factory));
                                  }
                                  aspectNames.add(beanName);
                              }
                              catch (IllegalArgumentException | IllegalStateException | AopConfigException ex) {
                                  if (logger.isDebugEnabled()) {
                                      logger.debug("Ignoring incompatible aspect [" + beanType.getName() + "]: " + ex);
                                  }
                              }
                          }
                      }
                      this.aspectBeanNames = aspectNames;
                      return advisors;
                  }
              }
          }
      
          if (aspectNames.isEmpty()) {
              return Collections.emptyList();
          }
      
          // 已经有缓存的情况,直接从缓存中获取
          List<Advisor> advisors = new ArrayList<>();
          for (String aspectName : aspectNames) {
              List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
              if (cachedAdvisors != null) {
                  advisors.addAll(cachedAdvisors);
              }
              else {
                  MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
                  advisors.addAll(this.advisorFactory.getAdvisors(factory));
              }
          }
          return advisors;
      }

      跟踪进核心的 getAdvisors() 方法。它负责将 Aspect 类中的通知方法(如 @Before@Around)和字段声明(如 @DeclareParents)转换为 Advisor 对象,并处理懒加载切面的实例化逻辑

      @Override
      public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
          // 获取 Aspect 类和名称
          Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
          String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
          validate(aspectClass);
      
          // 懒加载切面实例工厂
          MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
              new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
      
          // 返回切面类中的所有通知方法(下文会详细讲解)
          List<Advisor> advisors = new ArrayList<>();
          for (Method method : getAdvisorMethods(aspectClass)) {
              if (method.equals(ClassUtils.getMostSpecificMethod(method, aspectClass))) {
                  // 转换为 Advisor 对象(下文会详细讲解)
                  Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
                  if (advisor != null) {
                      advisors.add(advisor);
                  }
              }
          }
      
          // 处理懒加载
          if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
              Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
              advisors.add(0, instantiationAdvisor);
          }
      
          // 处理 @DeclareParents 注解
          for (Field field : aspectClass.getDeclaredFields()) {
              Advisor advisor = getDeclareParentsAdvisor(field);
              if (advisor != null) {
                  advisors.add(advisor);
              }
          }
      
          return advisors;
      }
      • getAdvisorMethods(aspectClass)

        这个方法会获取 Aspect 类中的所有通知方法

        private List<Method> getAdvisorMethods(Class<?> aspectClass) {
            List<Method> methods = new ArrayList<>();
            ReflectionUtils.doWithMethods(aspectClass, methods::add, adviceMethodFilter);
            if (methods.size() > 1) {
                methods.sort(adviceMethodComparator);
            }
            return methods;
        }
      • getAdvisor

        这个方法用于将 Aspect 类中的通知方法(如 @Before@Around)转换为 Advisor 对象

        @Override
        @Nullable
        public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
            int declarationOrderInAspect, String aspectName) {
        
            validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
        
            // 解析切点表达式
            AspectJExpressionPointcut expressionPointcut = getPointcut(
                candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
            if (expressionPointcut == null) {
                return null;
            }
        
            try {
                // 实例化 Advisor 对象
                // 包含了切点、通知方法、切面实例工厂、声明顺序和切面名称
                return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
                    this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
            }
            catch (IllegalArgumentException | IllegalStateException ex) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Ignoring incompatible advice method: " + candidateAdviceMethod, ex);
                }
                return null;
            }
        }

        最后实例化 Advisor 对象一步我懒得贴代码了,其实就是设置了几个属性,然后将其实例化。

    还记得之前在看哪个函数吗?shouldSkip!现在,我们明白了,这个函数其实是将 @Aspect 注解的切面类给实例化成 Advisor 了,并返回是不是传入的 Bean 是不是已经处理过的 Advisor。

我们再回溯!postProcessBeforeInstantiation 方法主要就是做了 shouldSkip 的方法,剩下的东西都无关紧要了。

postProcessAfterInitialization

在实例化过 Bean 并注入属性后,我们已经找出了所有的 Advisor。接下来,所有 Bean 将会依次初始化,然后我们要为这些 Advisor 创建代理对象。

点击查看 postProcessAfterInitialization 源码解读

我们要看的是 AbstractAutoProxyCreator 类的 postProcessAfterInitialization 方法:

@Override
@Nullable
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        // 生成唯一标识
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyBeanReferences.remove(cacheKey) != bean) {
            // 包装 Bean
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

这个函数看不出什么。往下看 wrapIfNecessary 方法:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 如果已经被显示代理过了,则直接返回
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }

    // 如果已经增强过了,则直接返回
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }

    // 又来了,同上,找出 @Aspect 注解的切面类
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // 获取适用的增强器
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

    // 创建代理
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

很明显,这个函数主要是做了两件事:

  • 获取这个 Bean 需要使用哪些增强器
  • 用这些增强器创建代理

接下来,我们分别看一下这两个方法。

  • getAdvicesAndAdvisorsForBean() 方法:

    @Override
    @Nullable
    protected Object[] getAdvicesAndAdvisorsForBean(
        Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    
        List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
        if (advisors.isEmpty()) {
            return DO_NOT_PROXY;
        }
        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);
    
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            try {
                eligibleAdvisors = sortAdvisors(eligibleAdvisors);
            }
            catch (BeanCreationException ex) {
                throw new AopConfigException("Advisor sorting failed with unexpected bean creation, probably due " +
                    "to custom use of the Ordered interface. Consider using the @Order annotation instead.", ex);
            }
        }
        return eligibleAdvisors;
    }

    这个函数就是找到所有的候选 Advisor,然后筛选出符合条件的 Advisor。

    我们一路跟踪 findAdvisorsThatCanApply() 方法:

    public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
        if (candidateAdvisors.isEmpty()) {
            return candidateAdvisors;
        }
    
        // 单独处理 IntroductionAdvisor,因为它可能为目标类引入新接口,影响后续 Advisor 的筛选
        List<Advisor> eligibleAdvisors = new ArrayList<>();
        for (Advisor candidate : candidateAdvisors) {
            if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
                eligibleAdvisors.add(candidate);
            }
        }
    
        // 处理其他 Advisor(下文会详细讲解)
        boolean hasIntroductions = !eligibleAdvisors.isEmpty();
        for (Advisor candidate : candidateAdvisors) {
            if (candidate instanceof IntroductionAdvisor) {
                continue;
            }
            if (canApply(candidate, clazz, hasIntroductions)) {
                eligibleAdvisors.add(candidate);
            }
        }
        return eligibleAdvisors;
    }

    显然,这里的重点就是 canApply() 方法。我们来看看这个方法:

    public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
        // 对于 IntroductionAdvisor,检查是否匹配类过滤器
        if (advisor instanceof IntroductionAdvisor ia) {
            return ia.getClassFilter().matches(targetClass);
        }
        // 对于 PointcutAdvisor,检查是否匹配切点
        else if (advisor instanceof PointcutAdvisor pca) {
            return canApply(pca.getPointcut(), targetClass, hasIntroductions);
        }
        // 对于其他类型的 Advisor,直接返回 true
        else {
            return true;
        }
    }

    跟踪进下一个 canApply() 方法:

    public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
        Assert.notNull(pc, "Pointcut must not be null");
    
        // 首先检查 Pointcut 的类过滤器是否匹配目标类
        if (!pc.getClassFilter().matches(targetClass)) {
            return false;
        }
    
        // 如果匹配所有方法,则直接返回 true
        MethodMatcher methodMatcher = pc.getMethodMatcher();
        if (methodMatcher == MethodMatcher.TRUE) {
            return true;
        }
    
        // 针对 IntroductionAwareMethodMatcher,检查是否匹配目标类的方法
        IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
        if (methodMatcher instanceof IntroductionAwareMethodMatcher iamm) {
            introductionAwareMethodMatcher = iamm;
        }
    
        // 收集待检查的类和接口
        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) {
                if (introductionAwareMethodMatcher != null ?
                    introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
                    methodMatcher.matches(method, targetClass)) {
                    return true;
                }
            }
        }
    
        return false;
    }

    这里往下看越看越深,懒得看了。总之,这个函数就是在判断当前 Bean 需要使用哪些增强器。

  • createProxy 方法:

    private Object buildProxy(Class<?> beanClass, @Nullable String beanName,
          @Nullable Object[] specificInterceptors, TargetSource targetSource, boolean classOnly) {
    
        if (this.beanFactory instanceof ConfigurableListableBeanFactory clbf) {
            AutoProxyUtils.exposeTargetClass(clbf, beanName, beanClass);
        }
    
        // 初始化 ProxyFactory,并从当前对象复制代理配置
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.copyFrom(this);
    
        // 如果使用 CGLIB 代理
        if (proxyFactory.isProxyTargetClass()) {
            // 如果是代理类或者 Lambda,则直接添加接口
            if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {
                for (Class<?> ifc : beanClass.getInterfaces()) {
                    proxyFactory.addInterface(ifc);
                }
            }
        }
        // 如果是 JDK 代理
        else {
            // 找到合适的接口
            if (shouldProxyTargetClass(beanClass, beanName)) {
                proxyFactory.setProxyTargetClass(true);
            }
            else {
                evaluateProxyInterfaces(beanClass, proxyFactory);
            }
        }
    
        // 收集所有的拦截器
        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        proxyFactory.addAdvisors(advisors);
        // 配置目标源和钩子方法
        proxyFactory.setTargetSource(targetSource);
        customizeProxyFactory(proxyFactory);
    
        // 冻结代理配置
        proxyFactory.setFrozen(this.freezeProxy);
        if (advisorsPreFiltered()) {
            proxyFactory.setPreFiltered(true);
        }
    
        // 获取类加载器
        ClassLoader classLoader = getProxyClassLoader();
        if (classLoader instanceof SmartClassLoader smartClassLoader && classLoader != beanClass.getClassLoader()) {
            classLoader = smartClassLoader.getOriginalClassLoader();
        }
    
        // 生成代理工厂
        return (classOnly ? proxyFactory.getProxyClass(classLoader) : proxyFactory.getProxy(classLoader));
    }

    这个函数主要将拦截器添加到 ProxyFactory 中,然后创建代理类。

    • buildAdvisors() 方法:

      protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) {
          // 通用拦截器
          Advisor[] commonInterceptors = resolveInterceptorNames();
      
          // 所有拦截器
          List<Object> allInterceptors = new ArrayList<>();
          if (specificInterceptors != null) {
              if (specificInterceptors.length > 0) {
                  allInterceptors.addAll(Arrays.asList(specificInterceptors));
              }
              // 默认情况下特定拦截器先执行、通用拦截器后执行
              if (commonInterceptors.length > 0) {
                  if (this.applyCommonInterceptorsFirst) {
                      allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
                  }
                  else {
                      allInterceptors.addAll(Arrays.asList(commonInterceptors));
                  }
              }
          }
          if (logger.isTraceEnabled()) {
              int nrOfCommonInterceptors = commonInterceptors.length;
              int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);
              logger.trace("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors +
                  " common interceptors and " + nrOfSpecificInterceptors + " specific interceptors");
          }
      
          // 将所有拦截器转换为 Advisor
          Advisor[] advisors = new Advisor[allInterceptors.size()];
          for (int i = 0; i < allInterceptors.size(); i++) {
              advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
          }
          return advisors;
      }

      这个函数干了两件事:

      • 处理了通用拦截器和特定拦截器的顺序问题

        • 通用拦截器是例如 @Bean 注解的拦截器
        • 特定拦截器是例如 @Aspect 注解定义切面,然后在 @Pointcut 注解中定义的拦截器
      • 将所有拦截器转换为 Advisor

        例如,@Before 注解原来是 Advice,现在变成了 DefaultPointcutAdvisor

    • getProxy()getProxyClass() 方法:

      这两个方法一层又一层的嵌套调用,当然都没干什么实事。我们直接看最终调用的 DefaultAopProxyFactory 类的 createAopProxy 方法:

      @Override
      public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
          // 判断是否满足使用 CGLIB 代理的条件
          if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
              Class<?> targetClass = config.getTargetClass();
              if (targetClass == null) {
                  throw new AopConfigException("TargetSource cannot determine target class: " +
                      "Either an interface or a target is required for proxy creation.");
              }
              // 如果是接口 / 代理类 / Lambda 表达式,则回退到 JDK 代理
              if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
                  return new JdkDynamicAopProxy(config);
              }
              // 使用 CGLIB 代理
              return new ObjenesisCglibAopProxy(config);
          }
          else {
              // 使用 JDK 代理
              return new JdkDynamicAopProxy(config);
          }
      }

      这个函数区分了 CGLIB 代理和 JDK 代理的情况,并分别进行了处理。

至此,全部分析完。CGLIB 代理和 JDK 代理具体怎么实现的就不讲了,感兴趣的可以去看 CglibAopProxyJdkDynamicAopProxy 这两个方法。

总结

我们上一篇在谈 Bean 生命周期的时候,对 AOP 还未如此深入的了解。现在我们可以把 AOP 的流程合并进 Bean 的生命周期了:

  1. BeanDefinition 注册(不属于 Bean 生命周期)

    通过 @ComponentScan 扫描到的 Bean 会被注册到容器中

    执行 ApplicationContextAwareLoadTimeWeaverAware

    AOP:创建 AnnotationAwareAspectJAutoProxyCreator 的 BeanDefinition,并注册到容器中

  2. 实例化

    根据包扫描的结果,通过构造函数或工厂方法创建 Bean 实例

    为解决循环依赖,Spring 可能提前暴露未初始化的 Bean

  3. 依赖注入

    根据 @Value@Autowired@Resource 等注解注入属性值和依赖

    如果存在循环依赖,从三级缓存获取提前暴露的 Bean 引用

  4. Aware 接口回调

    执行 Aware 接口的回调方法

    按顺序执行 BeanNameAware $$\to$$ BeanClassLoaderAware $$\to$$ BeanFactoryAware

  5. BeanPostProcess 前置处理

    执行 BeanPostProcessorpostProcessBeforeInitialization 方法

    AOP:扫描带有 @Aspect 注解的 Bean,创建 Advisor

  6. 初始化方法

    依次处理

    1. @PostConstruct 注解的方法
    2. InitializingBean 接口的 afterPropertiesSet() 方法
    3. 自定义的初始化方法
  7. BeanPostProcess 后置处理

    执行 BeanPostProcessorpostProcessAfterInitialization 方法

    AOP:为 Bean 创建代理对象

  8. Bean 就绪

    所有 Bean 都放入一级缓存,以供使用

  9. 销毁

    依次处理

    1. @PreDestroy 注解的方法
    2. DisposableBean 接口的 destroy() 方法
    3. 自定义的销毁方法

很多人讲,AOP 是在初始化后才处理的。现在,我们看到这种说法并不严谨。尽管在初始化后才创建代理对象,但在 BeanPostProcess 前置处理的时候,已经开始扫描所有的切面了并且创建了 Advisor。

更进一步说,在进入 Bean 生命周期之前,Spring 就已经根据 @EnableAspectJAutoProxy 注解创建了 AnnotationAwareAspectJAutoProxyCreator 的 BeanDefinition,并注册到容器中了。