spring-reading./spring-aop/spring-aop-enableAspectJAut.../README.md

14 KiB
Raw Permalink Blame History

@EnableAspectJAutoProxy

一、基本信息

✒️ 作者 - Lex 📝 博客 - 掘金 📚 源码地址 - github

二、基本描述

EnableAspectJAutoProxy注解是Spring框架中的一个注解用于启用AspectJ自动代理功能它能够自动将AspectJ切面与Spring的IoC容器集成无需显式配置大量AOP相关的内容从而简化AOP的使用和配置。

三、主要功能

  1. 启用AspectJ自动代理功能

    • 通过在配置类上添加该注解Spring会自动启用AspectJ自动代理功能无需显式配置大量AOP相关内容。
  2. 自动创建代理对象

    • Spring会自动创建代理对象来应用切面将切面逻辑与目标对象进行结合。
  3. 集成AspectJ切面与Spring容器

    • 能够方便地将AspectJ切面与Spring的IoC容器集成实现横切关注点的模块化管理。
  4. 简化AOP配置

    • 减少了手动配置AOP所需的繁琐步骤提高了开发效率。
  5. 自动扫描切面

  • Spring会自动扫描应用中的AspectJ切面并将其应用到相应的目标对象中无需手动配置切面。

四、注解源码

EnableAspectJAutoProxy注解用于启用支持处理使用AspectJ的@Aspect注解标记的组件在Spring中类似于XML配置中的<aop:aspectj-autoproxy>功能。它允许通过配置类轻松集成AspectJ切面并控制代理类型和代理的可见性。

/**
 * 启用支持处理使用AspectJ的{@code @Aspect}注解标记的组件,
 * 类似于Spring的{@code <aop:aspectj-autoproxy>} XML元素中的功能。
 * 应用在如下的@{@link Configuration}类上:
 *
 * <pre class="code">
 * &#064;Configuration
 * &#064;EnableAspectJAutoProxy
 * public class AppConfig {
 *
 *     &#064;Bean
 *     public FooService fooService() {
 *         return new FooService();
 *     }
 *
 *     &#064;Bean
 *     public MyAspect myAspect() {
 *         return new MyAspect();
 *     }
 * }</pre>
 *
 * 这里的{@code FooService}是一个典型的POJO组件{@code MyAspect}是一个
 * {@code @Aspect}-风格的切面:
 *
 * <pre class="code">
 * public class FooService {
 *
 *     // 各种方法
 * }</pre>
 *
 * <pre class="code">
 * &#064;Aspect
 * public class MyAspect {
 *
 *     &#064;Before("execution(* FooService+.*(..))")
 *     public void advice() {
 *         // 适当地提供FooService方法的建议
 *     }
 * }</pre>
 *
 * 在上述场景中,{@code @EnableAspectJAutoProxy}确保{@code MyAspect}
 * 将被正确处理,并且{@code FooService}将被代理,混合其中的建议。
 *
 * <p>用户可以通过{@link #proxyTargetClass()}属性控制为{@code FooService}创建的代理类型。
 * 以下示例启用了CGLIB风格的“子类”代理而不是默认的基于接口的JDK代理方式。
 *
 * <pre class="code">
 * &#064;Configuration
 * &#064;EnableAspectJAutoProxy(proxyTargetClass=true)
 * public class AppConfig {
 *     // ...
 * }</pre>
 *
 * <p>注意,{@code @Aspect} bean可以像任何其他组件一样进行组件扫描。
 * 只需将切面标记为{@code @Aspect}和{@code @Component}
 *
 * <pre class="code">
 * package com.foo;
 *
 * &#064;Component
 * public class FooService { ... }
 *
 * &#064;Aspect
 * &#064;Component
 * public class MyAspect { ... }</pre>
 *
 * 然后使用@{@link ComponentScan}注解来同时选择它们:
 *
 * <pre class="code">
 * &#064;Configuration
 * &#064;ComponentScan("com.foo")
 * &#064;EnableAspectJAutoProxy
 * public class AppConfig {
 *
 *     // 不需要显式的{@code @Bean}定义
 * }</pre>
 *
 * <b>注意:{@code @EnableAspectJAutoProxy}仅适用于其本地应用上下文,
 * 允许在不同级别选择性地对bean进行代理。</b>
 * 如果需要在多个级别应用其行为例如常见的根Web应用程序上下文和任何单独的{@code DispatcherServlet}应用程序上下文中,
 * 请在每个单独的上下文中重新声明{@code @EnableAspectJAutoProxy}。
 *
 * <p>该功能要求类路径上存在{@code aspectjweaver}。
 * 虽然{@code spring-aop}一般情况下对该依赖是可选的,但是对于{@code @EnableAspectJAutoProxy}及其基础设施,它是必需的。
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @since 3.1
 * @see org.aspectj.lang.annotation.Aspect
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

    /**
     * 指示是否创建基于子类CGLIB的代理而不是标准的基于Java接口的代理。默认值为{@code false}。
     */
    boolean proxyTargetClass() default false;

    /**
     * 指示代理是否应该被AOP框架公开为一个{@code ThreadLocal},以便通过{@link org.springframework.aop.framework.AopContext}类进行检索。
     * 默认情况下关闭,即不保证{@code AopContext}访问将起作用。
     * @since 4.3.1
     */
    boolean exposeProxy() default false;

}

五、最佳实践

使用EnableAspectJAutoProxy 注解和Spring的基于注解的应用上下文来启用AspectJ自动代理功能。在程序中首先创建了一个基于注解的应用上下文然后通过该上下文获取了MyService bean并调用了其方法。

public class EnableAspectJAutoProxyDemo {

    public static void main(String[] args) {
        // 创建基于注解的应用上下文
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        // 从应用上下文中获取MyService bean
        MyService myService = context.getBean(MyService.class);
        // 调用MyService的方法
        myService.foo();
    }
}

AppConfig 类是一个使用 @Configuration 注解标记的配置类,通过 @EnableAspectJAutoProxy 开启了 AspectJ 自动代理功能,并通过 @ComponentScan 启用了组件扫描,用于自动发现和注册 Spring 组件。

@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class AppConfig {

}

MyService 类是一个使用 @Service 注解标记的服务类,提供了一个名为 foo() 的方法,该方法在调用时会打印消息 "foo..."。


@Service
public class MyService {

    public void foo() {
        System.out.println("foo...");
    }
}

MyAspect是一个使用了@Aspect注解的Java类表示它是一个切面。在这个类中定义了一个名为advice的方法,并使用了@Before注解来指定在目标方法执行之前执行的通知。

@Aspect
@Component
public class MyAspect {

    @Before("execution(* com.xcs.spring.MyService+.*(..))")
    public void before() {
        System.out.println("Before method execution");
    }
}

运行结果,调用 MyService 类的 foo() 方法之前,成功地执行了一个切面通知,输出了 "Before method execution" 的消息,然后执行了 foo() 方法,输出了 "foo..." 的消息。

Before method
execution
foo...

六、源码分析

org.springframework.context.annotation.AspectJAutoProxyRegistrar#registerBeanDefinitions方法中首先注册了AspectJ注解自动代理创建器然后获取了@EnableAspectJAutoProxy注解的属性。如果@EnableAspectJAutoProxy注解中指定了proxyTargetClass属性为true则强制使用CGLIB代理如果指定了exposeProxy属性为true则强制代理对象暴露为ThreadLocal。

@Override
public void registerBeanDefinitions(
        AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    // 1、注册AspectJ注解自动代理创建器
    AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

    // 获取@EnableAspectJAutoProxy注解的属性
    AnnotationAttributes enableAspectJAutoProxy =
            AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
    if (enableAspectJAutoProxy != null) {
        ///2.如果@EnableAspectJAutoProxy注解指定了proxyTargetClass属性为true则强制使用CGLIB代理
        if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
        }
        // 3.如果@EnableAspectJAutoProxy注解指定了exposeProxy属性为true则强制代理对象暴露为ThreadLocal
        if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
        }
    }
}

org.springframework.aop.config.AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)方法中注册AspectJ注解自动代理创建器。它提供了一个重载方法允许传入一个额外的参数但这里调用的是没有额外参数的版本。在方法中它会根据给定的BeanDefinitionRegistry对象来注册AspectJ注解自动代理创建器并返回相应的BeanDefinition。

@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
    return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
}

org.springframework.aop.config.AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry,source)方法中注册AspectJ注解自动代理创建器并且可以指定源对象。在方法中它调用了一个辅助方法registerOrEscalateApcAsRequired该方法会根据需要注册或升级AspectJ注解自动代理创建器并返回相应的BeanDefinition。

AnnotationAwareAspectJAutoProxyCreator源码分析

@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
       BeanDefinitionRegistry registry, @Nullable Object source) {

    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

org.springframework.aop.config.AopConfigUtils#registerOrEscalateApcAsRequired方法中,首先检查给定的BeanDefinitionRegistry中是否已经存在了自动代理创建器的定义。如果存在,则比较现有自动代理创建器与指定类的优先级,如果指定类的优先级更高,则进行升级操作;如果不存在,则创建一个新的自动代理创建器的定义,并将其注册到给定的BeanDefinitionRegistry中。

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

    // 检查BeanDefinitionRegistry对象是否为null
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

    // 如果已经存在相同名称的自动代理创建器,则进行升级操作
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
       // 获取已存在的自动代理创建器的BeanDefinition
       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;
    }

    // 如果不存在相同名称的自动代理创建器,则进行注册操作
    // 创建新的自动代理创建器的BeanDefinition
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    // 注册新的自动代理创建器的BeanDefinition
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}

org.springframework.aop.config.AopConfigUtils#forceAutoProxyCreatorToUseClassProxying方法中,强制自动代理创建器使用基于类的代理。它首先检查给定的BeanDefinitionRegistry中是否已经存在了自动代理创建器的定义。如果存在,则获取该定义并设置其proxyTargetClass属性为true,表示使用基于类的代理。

public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
    // 检查是否存在自动代理创建器的BeanDefinition
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        // 获取自动代理创建器的BeanDefinition
        BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        // 设置proxyTargetClass属性为true表示使用基于类的代理
        definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
    }
}

org.springframework.aop.config.AopConfigUtils#forceAutoProxyCreatorToExposeProxy方法中,强制自动代理创建器暴露代理对象。它首先检查给定的BeanDefinitionRegistry中是否已经存在了自动代理创建器的定义。如果存在,则获取该定义并设置其exposeProxy属性为true,表示暴露代理对象。

public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
    // 检查是否存在自动代理创建器的BeanDefinition
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        // 获取自动代理创建器的BeanDefinition
        BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        // 设置exposeProxy属性为true表示暴露代理对象
        definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
    }
}