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


目录
相关文章
|
15天前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
1天前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
15 2
|
2天前
|
设计模式 Java Spring
Spring Boot监听器的底层实现原理
Spring Boot监听器的底层实现原理主要基于观察者模式(也称为发布-订阅模式),这是设计模式中用于实现对象之间一对多依赖的一种常见方式。在Spring Boot中,监听器的实现依赖于Spring框架提供的事件监听机制。
9 1
|
18天前
|
设计模式 Java Spring
spring源码设计模式分析(五)-策略模式
spring源码设计模式分析(五)-策略模式
|
15天前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
28 5
|
18天前
|
消息中间件 设计模式 缓存
spring源码设计模式分析(四)-观察者模式
spring源码设计模式分析(四)-观察者模式
|
18天前
|
设计模式 Java Spring
spring源码设计模式分析(六)-模板方法模式
spring源码设计模式分析(六)-模板方法模式
|
16天前
|
XML Java 开发者
经典面试---spring IOC容器的核心实现原理
作为一名拥有十年研发经验的工程师,对Spring框架尤其是其IOC(Inversion of Control,控制反转)容器的核心实现原理有着深入的理解。
57 3
|
18天前
|
设计模式 Java Spring
spring源码设计模式分析(七)-委派模式
spring源码设计模式分析(七)-委派模式
|
18天前
|
设计模式 Java 数据库
spring源码设计模式分析(八)-访问者模式
spring源码设计模式分析(八)-访问者模式