Spring AOP @EnableAspectJAutoProxy 实现原理分析

简介: 介绍@EnableAspectJAutoProxy 注解是 Spring AOP 框架提供给用户开启 AspectJ 注解支持的一个开关。将其添加到 Spring 配置类上,然后就可以在 Spring Bean 上使用 AspectJ 注解,将 bean 配置为一个 Aspect。

介绍


@EnableAspectJAutoProxy 注解是 Spring AOP 框架提供给用户开启 AspectJ 注解支持的一个开关。将其添加到 Spring 配置类上,然后就可以在 Spring Bean 上使用 AspectJ 注解,将 bean 配置为一个 Aspect。


Spring AOP 对 AspectJ 的依赖是可选的,如果使用这个注解还需要显式的引入 aspectjweaver.jar。如果你的项目中用到了 spring-boot-starter-aop,Spring 会自动开启对 AspectJ 的支持,无需额外操作。


实现分析


在《Spring AOP 与 AspectJ 》一篇中,我们提到,Spring AOP 是通过在运行时创建代理来实现的,那么这里就跟着 @EnableAspectJAutoProxy 注解的实现来进行分析。Spring @Enable* 注解的实现大多相同,可以参考《Spring 框架中的 @Enable* 注解是怎样实现的?》,这里将侧重 AOP 相关的实现部分。


注解说明


@EnableAspectJAutoProxy 源码如下。


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
  // 是否使用 CGLIB 创建代理
  boolean proxyTargetClass() default false;
  // 代理是否暴露在 ThreadLocal 中,以便通过 AopContext 获取
  boolean exposeProxy() default false;
}


注解定义了两个属性。


proxyTargetClass 属性用于指示是否优先使用 CGLIB 创建代理,Spring 内部会进行检测,即便配置为 false,如果不能使用 JDK 动态代理创建代理对象,那么 Spring 仍将选择 CGLIB,通常情况保持默认配置即可。


exposeProxy 属性用于配置是否在 ThreadLocal 中暴露代理对象,如果配置为 true,则可以在目标类的方法中使用 AopContext#currentProxy 获取代理对象。通常情况,目标类对代理类应该是无感知的,因此这里一般也保持默认配置。


@EnableAspectJAutoProxy 注解上添加了元注解 @Import,这意味着将使用 AspectJAutoProxyRegistrar 新引入一些 bean 到 Spring 的上下文中。


Bean 注册


跟踪 AspectJAutoProxyRegistrar 源码。

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
  @Override
  public void registerBeanDefinitions(
      AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
    AnnotationAttributes enableAspectJAutoProxy =
        AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
    if (enableAspectJAutoProxy != null) {
      if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
        AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
      }
      if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
        AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
      }
    }
  }
}


AspectJAutoProxyRegistrar 实现了接口 ImportBeanDefinitionRegistrar,主要利用 AopConfigUtils 这个工具类做了3步。


向 BeanDefinitionRegistry 注册了某个 bean。

强制配置是否使用 CGLIB 创建代理。

强制配置是否在目标方法执行前将代理对象暴露到 ThreadLocal。

后面两个步骤较为简单,先分析 AopConfigUtils。registerAspectJAnnotationAutoProxyCreatorIfNecessary 注册 bean 的逻辑,跟踪代码如下。


public abstract class AopConfigUtils {
  @Nullable
  public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
    return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
  }
  @Nullable
  public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
      BeanDefinitionRegistry registry, @Nullable Object source) {
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
  } 
}


到了这里,我们可以小结如下,@EnableAspectJAutoProxy 注解主要向 Spring 应用上下文中注册了一个类型为 AnnotationAwareAspectJAutoProxyCreator 的 bean。


自动代理创建分析


下面分析 AnnotationAwareAspectJAutoProxyCreator 如何与 IOC 容器整合创建代理的。这个类自身相对简单,实现逻辑主要在父类中。查看类图如下。


image.png


看到这里的小伙伴,想必心里一脸懵逼,这个类层次结构怎么如此深?不用过于担心,下面我会对主要的流程进行分析。


如果想创建 bean 的代理,Spring 一定会在 bean 的生命周期中处理,如果你对 Spring Bean 的生命周期不熟悉,可以先参考《Java 面试必备的 Spring Bean 生命周期总结》。Spring 提供了 InstantiationAwareBeanPostProcessor,Spring 会使用这个接口创建的 bean 取代用户定义的 bean。


通过类图可以看到 AnnotationAwareAspectJAutoProxyCreator 正是间接实现了这个接口,直接实现这个接口的父类 AbstractAutoProxyCreator 创建代理 bean 对象的代码如下。


public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
    implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
  @Override
  public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    Object cacheKey = getCacheKey(beanClass, beanName);
    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
      if (this.advisedBeans.containsKey(cacheKey)) {
        return null;
      }
      if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return null;
      }
    }
    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
    if (targetSource != null) {
      // TargetSource 不为空才创建代理
      if (StringUtils.hasLength(beanName)) {
        this.targetSourcedBeans.add(beanName);
      }
      Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
      Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
    }
    return null;
  }
}


AbstractAutoProxyCreator 在 bean 实例化前判断是否定义了获取 TargetSource 的 TargetSourceCreator,如果是则创建代理作为 bean 实例。不过很明显,我们使用 Spring 时并不会自己定义一个 TargetSourceCreator。因此这里通常也会返回一个 null。


不过也不用担心,AbstractAutoProxyCreator 还实现了接口 SmartInstantiationAwareBeanPostProcessor,Spring 还会通过这个接口获取 bean 的早期引用,Spring AOP 通过这个接口为早期创建的 bean 创建了代理对象。跟踪源码。


public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
    implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
  @Override
  public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    return wrapIfNecessary(bean, beanName, cacheKey);
  }
}


这里将原始 bean 进行包装,继续跟踪包装原始 bean 的 #wrapIfNecessary 方法。


public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
    implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
  protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
      // 给定 bean 已经被包装为代理,无需再次包装
      return bean;
    }
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      // 给定 bean 无需被代理,不再包装
      return bean;
    }
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      // 给定 bean 无需被代理,不再包装
      return bean;
    }
    // Create proxy if we have advice.
    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;
  }
}


#wrapIfNecessary 方法主要判断是否需要创建代理,如果需要则调用 #getAdvicesAndAdvisorsForBean 收集 Advisor,然后调用 #createProxy 方法创建代理。创建代理的逻辑比较简单,主要是使用 PointcutAdvisor 中的 Pointcut 确定要拦截的目标方法,然后将 Advisor 中的 Advice 转换为 MethodInvocation 拦截目标方法的执行。这里我们将重点放到与 AspectJ 相关的收集 Advisor 的代码。


#getAdvicesAndAdvisorsForBean 是一个抽象方法,查看子类实现如下。


public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {
  @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 方法收集 Advisor,继续跟踪。


public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {
  protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
      eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
  }
}


#findEligibleAdvisors 方法主要调用 #findCandidateAdvisors 收集可以应用到目标对象 的 Advisor。而AnnotationAwareAspectJAutoProxyCreator 覆盖了 #findCandidateAdvisors 这个方法通过 AspectJ 的注解来收集 Advisor 。查看源码如下。


public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {
  @Override
  protected List<Advisor> findCandidateAdvisors() {
    // Add all the Spring advisors found according to superclass rules.
    List<Advisor> advisors = super.findCandidateAdvisors();
    // Build Advisors for all AspectJ aspects in the bean factory.
    if (this.aspectJAdvisorsBuilder != null) {
      advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    return advisors;
  }
}


aspectJAdvisorsBuilder.buildAspectJAdvisors 方法就是用来通过 AspectJ 注解收集 Advisor 的,其实现相对复杂一些,这里不再分析。


总结

到了这里,总结如下。


Spring @EnableAspectJAutoProxy 注解引入类型为 AnnotationAwareAspectJAutoProxyCreator 的 bean,这个 bean 会根据 AspectJ 的注解收集 Advisor ,通过收集的 Advisor 创建代理,底层又会把 Advisor 中的 Advice 转换为 MethodInvocation 拦截目标方法的执行,感兴趣的可以自行阅读相关源码。


目录
相关文章
|
4月前
|
缓存 Java 开发者
【Spring】原理:Bean的作用域与生命周期
本文将围绕 Spring Bean 的作用域与生命周期展开深度剖析,系统梳理作用域的类型与应用场景、生命周期的关键阶段与扩展点,并结合实际案例揭示其底层实现原理,为开发者提供从理论到实践的完整指导。
559 22
|
4月前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
1572 0
|
4月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
505 0
|
3月前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
3月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
8月前
|
监控 安全 Java
Spring AOP实现原理
本内容主要介绍了Spring AOP的核心概念、实现机制及代理生成流程。涵盖切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)等关键概念,解析了JDK动态代理与CGLIB代理的原理及对比,并深入探讨了通知执行链路和责任链模式的应用。同时,详细分析了AspectJ注解驱动的AOP解析过程,包括切面识别、切点表达式匹配及通知适配为Advice的机制,帮助理解Spring AOP的工作原理与实现细节。
1234 13
|
3月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
474 2
|
5月前
|
Java 关系型数据库 数据库
深度剖析【Spring】事务:万字详解,彻底掌握传播机制与事务原理
在Java开发中,Spring框架通过事务管理机制,帮我们轻松实现了这种“承诺”。它不仅封装了底层复杂的事务控制逻辑(比如手动开启、提交、回滚事务),还提供了灵活的配置方式,让开发者能专注于业务逻辑,而不用纠结于事务细节。
|
5月前
|
人工智能 监控 安全
如何快速上手【Spring AOP】?核心应用实战(上篇)
哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。 AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。