Spring AOP原理简析

AspectJ用法 里介绍了 spring AOP 的用法。这里以切面LogAspect的生效流程,简单分析下SpringAOP原理。

切面LogAspect 定义如下:

java 复制代码
@Aspect
@Component
public class LogAspect {
    @Before(value = "execution(String org.example.topic.aop.demo.StudentServiceImpl.*(String))")
    public void logBeforeProceed(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
 
        String output = String.format("LogAspect.logBefore: methodName=%s, args=%s", methodName, args);
        System.out.println(output);
    }

LogAspect 类是 Spring AOP(面向切面编程)的一个典型应用,其工作原理涉及 Spring 容器的 Bean 生命周期、代理机制以及 AOP 的织入过程。


1. @Aspect 和 @Component 的作用

java 复制代码
@Aspect
@Component
public class LogAspect { ... }
  • @Component:告诉 Spring 容器这是一个普通的 Spring Bean,Spring 会在启动时通过组件扫描(如 @ComponentScan)将其注册为一个 Bean。
  • @Aspect:表明这个类是一个切面(Aspect),即包含通知(Advice)和切点(Pointcut)的逻辑单元。Spring AOP 会特别处理带有 @Aspect 注解的 Bean。

✅ 所以 LogAspect 本身就是一个 Spring 管理的 Bean,并且被识别为切面。


2. 切点(Pointcut)定义

java 复制代码
@Before(value = "execution(String org.example.topic.aop.demo.StudentServiceImpl.*(String))")

这是一个 前置通知(Before Advice),其切点表达式含义如下:

  • execution(...):匹配方法执行连接点。
  • String:返回类型必须是 String
  • org.example.topic.aop.demo.StudentServiceImpl.*:目标类是 StudentServiceImpl,任意方法名(*)。
  • (String):方法参数必须是单个 String 类型。

✅ 这个切点会匹配 StudentServiceImpl 中所有返回 String、接受一个 String 参数的方法。


3. Spring 如何创建代理(Proxy)

Spring AOP 是基于 代理模式 实现的。当容器发现某个 Bean 需要被 AOP 切面增强时,会为其创建一个 代理对象,而不是直接返回原始 Bean。

3.1 代理类型选择:
  • 如果目标类(如 StudentServiceImpl实现了接口 ,Spring 默认使用 JDK 动态代理(基于接口)。
  • 如果目标类 没有实现接口 ,Spring 会使用 CGLIB 代理(通过继承目标类生成子类)。

📌 假设 StudentServiceImpl 没有实现接口,Spring 会用 CGLIB 生成一个 StudentServiceImpl$$EnhancerBySpringCGLIB 子类作为代理。

3.2 代理的作用:
  • 当你从 Spring 容器获取 StudentServiceImpl 的 Bean 时,实际拿到的是 代理对象
  • 调用代理对象的方法时,会先经过 AOP 拦截器链(包括你的 @Before 通知),再调用原始方法。

4. AOP 织入(Weaving)过程

在 Spring 容器启动过程中,会执行以下关键步骤:

  1. Bean 扫描与注册

    • LogAspect@Component 注册为 Bean。
    • StudentServiceImpl 也被注册为 Bean(假设它也有 @Service@Component)。
  2. BeanPostProcessor 处理

    Spring 使用 AnnotationAwareAspectJAutoProxyCreator(一个特殊的 BeanPostProcessor)来处理 AOP:

    • 它会扫描所有 @Aspect Bean(如 LogAspect)。
    • 分析其中的切点表达式。
    • 检查其他普通 Bean(如 StudentServiceImpl)是否匹配这些切点。
  3. 创建代理

    • 如果 StudentServiceImpl 匹配 LogAspect 中的切点,AnnotationAwareAspectJAutoProxyCreator 会在 postProcessAfterInitialization 阶段替换原始 Bean 为代理对象
  4. 方法调用时的拦截

    • 当你调用 studentService.someMethod("hello") 时:
      • 实际调用的是代理对象的方法。
      • 代理对象内部会构建一个 拦截器链(Interceptor Chain)
      • 首先执行 logBeforeProceed(因为是 @Before)。
      • 然后调用原始 StudentServiceImpl 的目标方法。

5. JoinPoint 的作用

java 复制代码
public void logBeforeProceed(JoinPoint joinPoint) {
    String methodName = joinPoint.getSignature().getName();
    List<Object> args = Arrays.asList(joinPoint.getArgs());
    ...
}
  • JoinPoint 是 AOP 中的"连接点",代表程序执行过程中的一个点(如方法调用)。
  • Spring AOP 会自动将当前方法调用的上下文(方法名、参数等)封装到 JoinPoint 对象中,并传入通知方法。

6. 流程总结

java 复制代码
Spring 启动
   ↓
扫描 @Component → 注册 LogAspect 和 StudentServiceImpl 为 Bean
   ↓
AnnotationAwareAspectJAutoProxyCreator 发现 @Aspect
   ↓
解析切点表达式:execution(String StudentServiceImpl.*(String))
   ↓
检查 StudentServiceImpl 是否匹配 → 是!
   ↓
为 StudentServiceImpl 创建代理(JDK/CGLIB)
   ↓
容器中注入的是代理对象
   ↓
调用 studentService.xxx("arg")
   ↓
代理拦截 → 执行 LogAspect.logBeforeProceed(JoinPoint)
   ↓
输出日志
   ↓
调用原始 StudentServiceImpl.xxx("arg")

补充说明

  • 仅对 Spring Bean 有效 :AOP 只对 Spring 容器管理的 Bean 生效。如果 new StudentServiceImpl() 手动创建对象,AOP 不会生效。
  • 代理限制 :CGLIB 无法代理 final 方法;JDK 代理只能代理接口方法。
  • 性能:AOP 通过动态代理实现,有一定开销,但通常可忽略。

通过以上机制,LogAspect 就能在不修改 StudentServiceImpl 代码的情况下,透明地在方法调用前插入日志逻辑,体现了 AOP 的"横切关注点"分离思想。

7. 相关文档

  1. AspectJ用法: https://blog.csdn.net/taotiezhengfeng/article/details/149515395

  2. .Spring AOP切点表达式的关键词梳理: https://blog.csdn.net/taotiezhengfeng/article/details/155744545

  3. HandlerInterceptor 与 AOP 对比:https://blog.csdn.net/taotiezhengfeng/article/details/155452970

相关推荐
李慕婉学姐6 小时前
【开题答辩过程】以《基于JAVA的校园即时配送系统的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·开发语言·数据库
奋进的芋圆7 小时前
Java 延时任务实现方案详解(适用于 Spring Boot 3)
java·spring boot·redis·rabbitmq
sxlishaobin8 小时前
设计模式之桥接模式
java·设计模式·桥接模式
model20058 小时前
alibaba linux3 系统盘网站迁移数据盘
java·服务器·前端
荒诞硬汉8 小时前
JavaBean相关补充
java·开发语言
提笔忘字的帝国8 小时前
【教程】macOS 如何完全卸载 Java 开发环境
java·开发语言·macos
2501_941882489 小时前
从灰度发布到流量切分的互联网工程语法控制与多语言实现实践思路随笔分享
java·开发语言
華勳全栈9 小时前
两天开发完成智能体平台
java·spring·go
alonewolf_999 小时前
Spring MVC重点功能底层源码深度解析
java·spring·mvc
沛沛老爹9 小时前
Java泛型擦除:原理、实践与应对策略
java·开发语言·人工智能·企业开发·发展趋势·技术原理