HarmonyOS 鸿蒙Next 深入理解Spring AOP中的@EnableAspectJAutoProxy

发布于 1周前 作者 itying888 来自 鸿蒙OS

HarmonyOS 鸿蒙Next 深入理解Spring AOP中的@EnableAspectJAutoProxy

摘要:本文详细探讨了Spring框架中的面向切面编程(AOP),特别是通过@EnableAspectJAutoProxy注解来启用和配置AOP的详细过程。

本文分享自华为云社区《Spring高手之路20——深入理解@EnableAspectJAutoProxy的力量》,作者: 砖业洋__。

1. 初始调试代码

面向切面编程(AOP)是一种编程范式,用于增强软件模块化,通过将横切关注点(如事务管理、安全等)分离出业务逻辑。Spring AOP是Spring框架中实现AOP的一种方式,它通过代理机制在运行时向对象动态地添加增强。AspectJ是一种更强大的AOP实现,它通过编译时和加载时织入,提供了比Spring AOP更丰富的增强选项。本文将探索如何通过Spring AOP进行简单的AOP配置和实现。

后续源码分析就用这个前置通知的代码调试

package com.example.demo.aspect;

import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component;

@Aspect @Component public class MyAspect { @Before(“execution(* com.example.demo.service.MyService.performAction(…))”) public void beforeAdvice(JoinPoint joinPoint) { System.out.println("Before method: " + joinPoint.getSignature().getName()); } } <button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

package com.example.demo.configuration;

import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration @EnableAspectJAutoProxy public class AppConfig { } <button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

package com.example.demo.service;

import org.springframework.stereotype.Service;

// 一个简单的服务类 @Service public class MyService { public void performAction() { System.out.println(“Performing an action”); } } <button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

package com.example.demo;

import com.example.demo.service.MyService; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan;

//主应用类 @ComponentScan(basePackages = “com.example.demo”) public class DemoApplication {

<span class="hljs-keyword"><span class="hljs-keyword">public</span></span> <span class="hljs-keyword"><span class="hljs-keyword">static</span></span> <span class="hljs-keyword"><span class="hljs-keyword">void</span></span> <span class="hljs-title"><span class="hljs-title">main</span></span>(String[] args) {
    AnnotationConfigApplicationContext context = <span class="hljs-keyword"><span class="hljs-keyword">new</span></span> AnnotationConfigApplicationContext(DemoApplication.class);
    MyService myService = context.getBean(MyService.class);
    myService.performAction();  <span class="hljs-comment"><span class="hljs-comment">// 调用方法,触发AOP增强</span></span>
}

}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

2. 源码跟踪分析

2.1 初探@EnableAspectJAutoProxy

上面代码中,AppConfig配置类里有个@EnableAspectJAutoProxy注解,前面说过,@EnableAspectJAutoProxy注解告诉Spring框架去寻找带有@Aspect注解的类,Spring AOP通过读取@EnableAspectJAutoProxy注解的属性来配置代理的行为。

下面用时序图来展示通过@EnableAspectJAutoProxy注解启用面向切面编程(AOP)的过程。

cke_119.png

解读:

1.启动ApplicationContext

  • 应用 (App) 向 ApplicationContext 发送消息以启动Spring的应用上下文。这是Spring应用的初始化阶段,负责设置Spring的核心功能,包括Bean的加载和管理。

2.加载配置类

  • ApplicationContext 接着加载 配置类 (ConfigClass)。这个配置类包含了应用的配置信息,如Bean定义和AOP支持的相关注解等。

3.检测@EnableAspectJAutoProxy

  • 配置类完成加载后,检查是否包含 @EnableAspectJAutoProxy 注解。此注解是启用Spring AOP代理的关键,它指示Spring框架自动为符合条件的Bean创建AOP代理。

4.注册AspectJAutoProxyCreator

  • 一旦检测到@EnableAspectJAutoProxy注解,ApplicationContext 会注册 AspectJAutoProxyCreator (APC)。这个组件是一个BeanPostProcessor,它在Spring容器的bean初始化阶段介入,自动检测容器中所有带有@Aspect注解的类,并为这些类创建代理。这个代理创建过程不仅包括实现通知逻辑的织入,还涉及对被代理对象的调用进行拦截,确保在执行目标方法前后能够执行相应的通知(advice)。

5.扫描和注册Beans

  • ApplicationContext 继续扫描应用中的其他 Bean,并将它们注册到Spring容器中。这包括普通的Bean和那些可能成为AOP代理目标的Bean。

6.识别@Aspect注解

  • 在Bean的扫描过程中,识别出带有 @Aspect 注解的Bean(AspectBean)。这些Bean定义了AOP的切面,如通知方法(advice),指定在某些方法执行前后或抛出异常时执行。

7.请求创建代理

  • 当识别到@Aspect注解的Bean时,这些Bean会向 AspectJAutoProxyCreator 发出请求,要求创建相应的代理。

8.调用创建代理

  • AspectJAutoProxyCreator 收到创建代理的请求后,调用代理工厂 (ProxyFactory) 来构建具体的代理实例。

9.构建代理Bean

  • 代理工厂 根据AspectJAutoProxyCreator的指示,为@Aspect注解的Bean创建代理。这些代理将封装原Bean,并在调用原Bean的方法时,按照@Aspect定义执行相应的前置、后置或异常通知。

10.注册代理Bean

  • 创建完成的代理Bean(ProxyBean)被注册回 ApplicationContext,替换或增加到原有的Bean配置中。

11.完成Bean加载和初始化

  • 所有Bean,包括新注册的代理Bean,都被加载和初始化后,ApplicationContext 向应用 (App) 发送消息,表示Bean加载和初始化工作已完成,应用可以开始执行。

来看看源码,这里可以看到@Import 导入了一个注册器AspectJAutoProxyRegistrar。

cke_120.png

@EnableAspectJAutoProxy注解启用Spring的自动代理机制,该注解有两个重要的属性配置:proxyTargetClass和exposeProxy。proxyTargetClass属性默认为false,此时Spring使用JDK动态代理来代理接口。如果设置为true,则Spring将使用CGLIB来代理类,这在目标对象没有实现接口时特别有用。exposeProxy属性默认为false,如果设置为true,允许通过AopContext类访问当前的代理对象,这在需要在目标对象内部方法调用自身被代理的方法时非常有用。

2.2 registerBeanDefinitions方法和时序图分析

本节源码都基于5.3.16分析。

这段代码主要涉及2.1节时序图中的“加载配置类”和“注册AspectJAutoProxyCreator”这两个步骤。

在AspectJAutoProxyRegistrar类的registerBeanDefinitions方法打上断点调试。

cke_121.png

这个方法主要负责根据@EnableAspectJAutoProxy注解的设置来配置Spring AOP的行为,包括是否使用CGLIB进行类代理而不是基于接口的JDK代理,以及是否允许在被代理的对象内部通过AopContext访问代理对象。这两个设置对于控制Spring AOP的行为至关重要,特别是在处理复杂的代理场景和高级AOP功能时。

代码提出来分析:

// 注册Bean定义的方法,通过读取注解元数据和操作Bean定义注册表进行配置
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 检查是否已经注册了AspectJ自动代理创建器,如果没有,则进行注册
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

<span class="hljs-comment"><span class="hljs-comment">// 从导入的类的注解元数据中获取[@EnableAspectJAutoProxy](/user/EnableAspectJAutoProxy)注解的属性</span></span>
AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);

<span class="hljs-comment"><span class="hljs-comment">// 检查是否成功获取[@EnableAspectJAutoProxy](/user/EnableAspectJAutoProxy)注解的属性</span></span>
<span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (enableAspectJAutoProxy != <span class="hljs-keyword"><span class="hljs-keyword">null</span></span>) {
    <span class="hljs-comment"><span class="hljs-comment">// 检查[@EnableAspectJAutoProxy](/user/EnableAspectJAutoProxy)注解的proxyTargetClass属性是否为true</span></span>
    <span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (enableAspectJAutoProxy.getBoolean(<span class="hljs-string"><span class="hljs-string">"proxyTargetClass"</span></span>)) {
        <span class="hljs-comment"><span class="hljs-comment">// 如果proxyTargetClass为true,则强制AOP代理创建器使用CGLIB来进行类代理</span></span>
        AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
    }

    <span class="hljs-comment"><span class="hljs-comment">// 检查[@EnableAspectJAutoProxy](/user/EnableAspectJAutoProxy)注解的exposeProxy属性是否为true</span></span>
    <span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (enableAspectJAutoProxy.getBoolean(<span class="hljs-string"><span class="hljs-string">"exposeProxy"</span></span>)) {
        <span class="hljs-comment"><span class="hljs-comment">// 如果exposeProxy为true,则强制AOP代理创建器暴露代理对象,使其能在被代理的对象内部通过AopContext访问</span></span>
        AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
    }
}

}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

这个方法的两个入参说明一下:

  1. importingClassMetadata是AnnotationMetadata类型的实例,它持有关于当前正在被处理的类的注解信息。这里用来检索有关@EnableAspectJAutoProxy注解的信息,这些信息决定了如何配置AOP代理的行为(是否使用CGLIB代理以及是否暴露代理对象)。
  2. registry是BeanDefinitionRegistry类型的实例,它是一个用于注册Bean定义的接口。通过这个注册表,可以在运行时向Spring应用上下文添加新的Bean定义或修改现有的Bean定义。这里用于实际调整AOP配置,如注册AOP代理创建器,以及设置代理创建器的行为(根据@EnableAspectJAutoProxy的属性值)。这些操作直接影响了Spring AOP如何在运行时创建和管理AOP代理。

如果流程太抽象,那么用时序图补充

cke_122.png

这个时序图展示了Spring AOP配置的完整流程,从检查和注册自动代理创建器,到根据@EnableAspectJAutoProxy注解的设置调整Spring的代理行为。此过程确保了应用的AOP配置能够根据给定的注解属性正确地执行,无论是使用更高性能的CGLIB代理,还是暴露代理以供内部访问。

完整的时序图解释

1. 方法调用开始

  • 调用者 (Caller)触发 registerBeanDefinitions 方法(RBD),这通常发生在应用的配置阶段。

2. 检查并注册自动代理创建器

  • registerBeanDefinitions 向 AopConfigUtils (AopCU)发起调用,检查是否已注册AspectJ自动代理创建器,或者是否需要注册新的或更新现有的代理创建器。

3. 自动代理创建器的注册和更新

  • AopConfigUtils 向 Registry (Reg)执行实际的注册或更新操作。
  • Registry 完成更新后反馈给 AopConfigUtils。
  • AopConfigUtils 然后将结果返回给 registerBeanDefinitions。

4. 获取@EnableAspectJAutoProxy注解的属性

  • registerBeanDefinitions 接着从 AnnotationConfigUtils (ACU)获取@EnableAspectJAutoProxy注解的相关属性,这些属性决定代理的行为。

5. 根据属性设置代理方式

  • 如果注解的proxyTargetClass属性为真,意味着需要使用CGLIB来进行类代理而不是基于接口的代理。
  • registerBeanDefinitions 要求 AopConfigUtils 强制使用CGLIB代理。
  • AopConfigUtils 更新 Registry 中相关Bean定义的设置以使用CGLIB。
  • Registry 确认设置已更新,然后 AopConfigUtils 通知 registerBeanDefinitions 配置完成。

6. 设置是否暴露代理

  • 如果注解的exposeProxy属性为真,意味着需要暴露代理,允许通过AopContext访问当前代理。
  • registerBeanDefinitions 要求 AopConfigUtils 强制暴露代理。
  • AopConfigUtils 在 Registry 中进行相应设置更新。
  • Registry 确认设置已更新,然后 AopConfigUtils 通知 registerBeanDefinitions 配置完成。

7. 配置流程完成

  • 一旦所有设置完成,registerBeanDefinitions 向调用者报告配置流程已完成。

2.3 registerOrEscalateApcAsRequired方法和时序图分析

看到刚刚第一句注册后置处理器,我们来详细看看

cke_123.png

这段代码主要与2.1节时序图中的“注册AspectJAutoProxyCreator”步骤相对应。AspectJAutoProxyCreator是由Spring内部管理的一个自动代理创建器,用于基于AspectJ的注解来创建AOP代理。它与用户定义的切面(使用@Aspect注解的类)相区分,后者指定了具体的通知(如@Before@AfterReturning等)和切点表达式。在Spring的AOP实现中,代理创建器负责实际的代理对象创建工作,而用户定义的切面提供了应用于这些代理对象的通知逻辑。具体而言,它描述了如何在Spring的ApplicationContext中检查并可能更新或注册一个新的自动代理创建器(AspectJAutoProxyCreator)。

直接分析registerOrEscalateApcAsRequired方法

// 定义一个用于注册或升级自动代理创建器的静态方法
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
// 断言,确保传入的registry不为空
Assert.notNull(registry, “BeanDefinitionRegistry must not be null”);

<span class="hljs-comment"><span class="hljs-comment">// 检查容器是否已经包含名为"org.springframework.aop.config.internalAutoProxyCreator"的Bean定义</span></span>
<span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (registry.containsBeanDefinition(<span class="hljs-string"><span class="hljs-string">"org.springframework.aop.config.internalAutoProxyCreator"</span></span>)) {
    <span class="hljs-comment"><span class="hljs-comment">// 获取已存在的自动代理创建器的Bean定义</span></span>
    BeanDefinition apcDefinition = registry.getBeanDefinition(<span class="hljs-string"><span class="hljs-string">"org.springframework.aop.config.internalAutoProxyCreator"</span></span>);
    
    <span class="hljs-comment"><span class="hljs-comment">// 检查当前注册的自动代理创建器类名是否与传入的cls类名不同</span></span>
    <span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (!cls.getName().equals(apcDefinition.getBeanClassName())) {
        <span class="hljs-comment"><span class="hljs-comment">// 找到当前自动代理创建器的优先级</span></span>
        <span class="hljs-keyword"><span class="hljs-keyword">int</span></span> currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
        <span class="hljs-comment"><span class="hljs-comment">// 找到需要注册的自动代理创建器的优先级</span></span>
        <span class="hljs-keyword"><span class="hljs-keyword">int</span></span> requiredPriority = findPriorityForClass(cls);
        
        <span class="hljs-comment"><span class="hljs-comment">// 比较两个优先级,若已注册的优先级低,则更新为新的自动代理创建器类</span></span>
        <span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (currentPriority &lt; requiredPriority) {
            apcDefinition.setBeanClassName(cls.getName());
        }
    }
    
    <span class="hljs-comment"><span class="hljs-comment">// 若已存在自动代理创建器且不需要升级,则返回null</span></span>
    <span class="hljs-keyword"><span class="hljs-keyword">return</span></span> <span class="hljs-keyword"><span class="hljs-keyword">null</span></span>;
} <span class="hljs-keyword"><span class="hljs-keyword">else</span></span> {
    <span class="hljs-comment"><span class="hljs-comment">// 若未注册自动代理创建器,则创建一个新的RootBeanDefinition实例</span></span>
    RootBeanDefinition beanDefinition = <span class="hljs-keyword"><span class="hljs-keyword">new</span></span> RootBeanDefinition(cls);
    
    <span class="hljs-comment"><span class="hljs-comment">// 设置bean定义的来源</span></span>
    beanDefinition.setSource(source);
    
    <span class="hljs-comment"><span class="hljs-comment">// 设置bean定义的属性,这里设置"order"属性为最小整数值,表示最高优先级</span></span>
    beanDefinition.getPropertyValues().add(<span class="hljs-string"><span class="hljs-string">"order"</span></span>, Integer.MIN_VALUE);
    
    <span class="hljs-comment"><span class="hljs-comment">// 设置bean定义的角色,通常ROLE_INFRASTRUCTURE表示框架内部使用的组件</span></span>
    beanDefinition.setRole(<span class="hljs-number"><span class="hljs-number">2</span></span>);
    
    <span class="hljs-comment"><span class="hljs-comment">// 在注册表中注册名为"org.springframework.aop.config.internalAutoProxyCreator"的新自动代理创建器Bean定义</span></span>
    registry.registerBeanDefinition(<span class="hljs-string"><span class="hljs-string">"org.springframework.aop.config.internalAutoProxyCreator"</span></span>, beanDefinition);
    
    <span class="hljs-comment"><span class="hljs-comment">// 返回新创建的Bean定义</span></span>
    <span class="hljs-keyword"><span class="hljs-keyword">return</span></span> beanDefinition;
}

}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

这个方法主要用于控制Spring AOP框架中的自动代理创建器(AutoProxyCreator)的注册与优先级升级,确保AOP功能按预期工作,特别是在有多个自动代理创建器可能存在时确保正确的配置和行为优先级。

自动代理创建器(AutoProxyCreator)是一个核心组件,根据配置(如注解、XML配置或程序的指定)识别需要增强的Bean,并自动为这些Bean创建代理。这些代理可以在方法调用前后添加额外的行为,而不修改原有代码的基础上,实现如安全检查、事务管理、日志记录等横切关注点。

如果流程太抽象,那么用时序图补充

cke_124.png

这个时序图展示了 registerOrEscalateApcAsRequired 方法如何根据已存在的自动代理创建器Bean定义的情况来决定执行的操作。通过检查、比较和可能的更新或创建操作,它确保了最适合的类被用于自动代理创建器。如果当前注册的自动代理创建器足够适合,不会进行更改;如果不适合,会进行更新或创建新的Bean定义,以保证系统配置的最优化。

1. 开始调用

  • 调用者发起对 registerOrEscalateApcAsRequired 方法的调用。该方法接收三个参数:类(cls),注册表(registry)和源信息(source)。

2. 检查Bean定义是否存在

  • registerOrEscalateApcAsRequired 向 BeanDefinitionRegistry 查询是否已存在名为 “internalAutoProxyCreator” 的Bean定义。

3. 处理已存在的Bean定义

  • 如果 BeanDefinitionRegistry 确认Bean定义已存在(返回true),registerOrEscalateApcAsRequired 从 BeanDefinitionRegistry 请求获取该Bean定义。
  • BeanDefinitionRegistry 将 BeanDefinition 返回给 registerOrEscalateApcAsRequired。
  • registerOrEscalateApcAsRequired 使用返回的 BeanDefinition 检查并比较当前Bean的类与新传入的类 cls 的优先级。

4. 决定是否更新Bean定义

  • 如果新类 cls 的优先级更高,registerOrEscalateApcAsRequired 会在 BeanDefinition 中更新类名为新类 cls.getName()。
  • 更新操作完成后,BeanDefinition 通知 BeanDefinitionRegistry 更新已完成。
  • 如果当前已注册的类的优先级足够高或相同,不需要进行更新,registerOrEscalateApcAsRequired 直接返回null给调用者。

5. 处理不存在的Bean定义

  • 如果 BeanDefinitionRegistry 确认没有找到名为 “internalAutoProxyCreator” 的Bean定义(返回false),registerOrEscalateApcAsRequired 将创建一个新的 BeanDefinition。
  • 新创建的 BeanDefinition 被注册到 BeanDefinitionRegistry。
  • 注册完成后,BeanDefinitionRegistry 确认新的BeanDefinition已注册。
  • registerOrEscalateApcAsRequired 最终将新创建的BeanDefinition返回给调用者。

点击关注,第一时间了解华为云新鲜技术~

1 回复

在HarmonyOS鸿蒙Next的开发环境中,如果你正在深入理解Spring AOP(面向切面编程)中的@EnableAspectJAutoProxy注解,这里有一个简要的说明。

@EnableAspectJAutoProxy是Spring AOP中的一个关键注解,用于启用AspectJ的自动代理功能。这个注解会告诉Spring框架,在创建bean时,如果检测到有切面(aspect)相关的配置,则自动为匹配的bean生成代理对象。这样,切面中定义的通知(advice)就可以在目标方法执行前后、异常时或周围执行,实现横切关注点(cross-cutting concerns)的分离。

在HarmonyOS鸿蒙Next中,虽然具体的开发环境和API可能与传统的Java Spring应用有所不同,但Spring AOP的核心概念,包括@EnableAspectJAutoProxy的使用,是通用的。你需要确保你的项目已经正确配置了Spring AOP相关的依赖,并且你的切面类和方法已经按照AspectJ的规范进行了定义。

如果在使用@EnableAspectJAutoProxy时遇到问题,比如切面没有生效,检查你的配置是否正确,包括切面类是否使用了@Aspect注解,通知方法是否使用了正确的切入点表达式等。

如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部