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 拦截目标方法的执行,感兴趣的可以自行阅读相关源码。


目录
相关文章
|
1月前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
36 0
|
9天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
53 14
|
24天前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
51 14
|
1月前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
26天前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
53 5
|
1月前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
61 8
|
1月前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
1月前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
43 5
|
1月前
|
Java 开发者 Spring
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
43 4
|
2月前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
53 3
Spring高手之路23——AOP触发机制与代理逻辑的执行