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月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
579 0
|
3月前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
8月前
|
监控 安全 Java
Spring AOP实现原理
本内容主要介绍了Spring AOP的核心概念、实现机制及代理生成流程。涵盖切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)等关键概念,解析了JDK动态代理与CGLIB代理的原理及对比,并深入探讨了通知执行链路和责任链模式的应用。同时,详细分析了AspectJ注解驱动的AOP解析过程,包括切面识别、切点表达式匹配及通知适配为Advice的机制,帮助理解Spring AOP的工作原理与实现细节。
1321 13
|
5月前
|
人工智能 监控 安全
如何快速上手【Spring AOP】?核心应用实战(上篇)
哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。 AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。
|
XML Java 开发者
Spring Boot中的AOP实现
Spring AOP(面向切面编程)允许开发者在不修改原有业务逻辑的情况下增强功能,基于代理模式拦截和增强方法调用。Spring Boot通过集成Spring AOP和AspectJ简化了AOP的使用,只需添加依赖并定义切面类。关键概念包括切面、通知和切点。切面类使用`@Aspect`和`@Component`注解标注,通知定义切面行为,切点定义应用位置。Spring Boot自动检测并创建代理对象,支持JDK动态代理和CGLIB代理。通过源码分析可深入了解其实现细节,优化应用功能。
587 6
|
5月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
7月前
|
负载均衡 Java API
基于 Spring Cloud 的微服务架构分析
Spring Cloud 是一个基于 Spring Boot 的微服务框架,提供全套分布式系统解决方案。它整合了 Netflix、Zookeeper 等成熟技术,通过简化配置和开发流程,支持服务发现(Eureka)、负载均衡(Ribbon)、断路器(Hystrix)、API网关(Zuul)、配置管理(Config)等功能。此外,Spring Cloud 还兼容 Nacos、Consul、Etcd 等注册中心,满足不同场景需求。其核心组件如 Feign 和 Stream,进一步增强了服务调用与消息处理能力,为开发者提供了一站式微服务开发工具包。
720 0
|
11月前
|
XML Java 测试技术
Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)
Spring 第五节 AOP——切入点表达式 万字详解!
862 25
|
11月前
|
XML 安全 Java
Spring AOP—深入动态代理 万字详解(通俗易懂)
Spring 第四节 AOP——动态代理 万字详解!
501 24

热门文章

最新文章