当前位置: 首页 > news >正文

如何使用Spring框架实现AOP?

一、先明确核心概念(快速回顾)

在动手前,先理清 Spring AOP 的核心术语,避免后续代码理解混乱:

  • 切面(Aspect):封装 “横切逻辑” 的类(比如日志、权限校验、事务),是 AOP 的核心载体。
  • 切点(Pointcut):定义 “哪些方法需要被增强”(比如指定包下的所有 Service 方法)。
  • 通知(Advice):定义 “增强的时机和逻辑”,包括:
    • @Before:目标方法执行前执行
    • @After:目标方法执行后执行(无论是否异常)
    • @AfterReturning:目标方法正常返回后执行
    • @AfterThrowing:目标方法抛出异常后执行
    • @Around:环绕通知(最灵活,可控制目标方法的执行时机、参数、返回值)
  • 连接点(JoinPoint):程序执行过程中可被增强的 “点”(比如方法调用、异常抛出),Spring 只支持方法级连接点。

二、Spring AOP 实现步骤(基于注解,最常用)

Spring AOP 有两种实现方式:注解式(推荐)XML 配置式,这里重点讲注解式,步骤如下:

步骤 1:引入依赖(Maven/Gradle)

核心依赖是spring-context(已包含 AOP 基础),如果是 Spring Boot 项目,只需引入spring-boot-starter即可;非 Spring Boot 项目需手动引入:

xml

<!-- Spring核心+AOP依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.1.4</version> <!-- 适配Java 17+,按需调整版本 --> </dependency> <!-- Spring AOP增强(可选,简化切点表达式) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>6.1.4</version> </dependency>
步骤 2:开启 AOP 注解支持

在 Spring 配置类上添加@EnableAspectJAutoProxy注解,启用 AOP 自动代理:

java

运行

import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; // 配置类,开启AOP注解支持 @Configuration @ComponentScan("com.example") // 扫描指定包下的Bean @EnableAspectJAutoProxy public class SpringConfig { }
步骤 3:定义目标类(被增强的业务类)

创建普通的 Spring Bean,作为 AOP 的 “目标对象”:

java

运行

import org.springframework.stereotype.Service; // 业务层Bean,作为AOP的目标类 @Service public class UserService { // 待增强的方法1 public void addUser(String username) { System.out.println("执行添加用户逻辑:" + username); // 可手动抛出异常测试@AfterThrowing:throw new RuntimeException("添加用户失败"); } // 待增强的方法2 public String getUserById(Integer id) { System.out.println("执行查询用户逻辑:id=" + id); return "用户-" + id; } }
步骤 4:定义切面类(封装横切逻辑)

创建切面类,用@Aspect标记,同时标注为 Spring Bean(@Component),并定义切点和通知:

java

运行

import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; // 1. 标记为切面类 + Spring Bean @Aspect @Component public class LogAspect { // 2. 定义切点(复用性高,可被多个通知引用) // 切点表达式:匹配com.example.service包下所有类的所有方法 @Pointcut("execution(* com.example.service.*.*(..))") public void servicePointcut() {} // 3. 定义通知(增强逻辑) // 前置通知:目标方法执行前执行 @Before("servicePointcut()") public void beforeAdvice(JoinPoint joinPoint) { // JoinPoint可获取目标方法名、参数等信息 String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); System.out.println("[前置通知] 方法:" + methodName + ",参数:" + java.util.Arrays.toString(args)); } // 后置返回通知:目标方法正常返回后执行 @AfterReturning(value = "servicePointcut()", returning = "result") public void afterReturningAdvice(JoinPoint joinPoint, Object result) { String methodName = joinPoint.getSignature().getName(); System.out.println("[返回通知] 方法:" + methodName + ",返回值:" + result); } // 异常通知:目标方法抛出异常后执行 @AfterThrowing(value = "servicePointcut()", throwing = "e") public void afterThrowingAdvice(JoinPoint joinPoint, Exception e) { String methodName = joinPoint.getSignature().getName(); System.out.println("[异常通知] 方法:" + methodName + ",异常:" + e.getMessage()); } // 环绕通知(最灵活,可控制目标方法执行) @Around("servicePointcut()") public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable { String methodName = joinPoint.getSignature().getName(); System.out.println("[环绕前置] 开始执行方法:" + methodName); // 执行目标方法(必须调用,否则目标方法不会执行) Object result = joinPoint.proceed(); System.out.println("[环绕后置] 方法:" + methodName + "执行完成"); return result; // 可修改返回值 } }
步骤 5:测试 AOP 效果

编写测试类,启动 Spring 容器并调用目标方法:

java

运行

import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class AopTest { public static void main(String[] args) { // 初始化Spring容器 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); // 获取目标Bean UserService userService = context.getBean(UserService.class); // 调用目标方法,触发AOP增强 userService.addUser("张三"); System.out.println("-----分割线-----"); userService.getUserById(1001); // 关闭容器 context.close(); } }
执行结果(正常情况):

plaintext

[环绕前置] 开始执行方法:addUser [前置通知] 方法:addUser,参数:[张三] 执行添加用户逻辑:张三 [环绕后置] 方法:addUser执行完成 -----分割线----- [环绕前置] 开始执行方法:getUserById [前置通知] 方法:getUserById,参数:[1001] 执行查询用户逻辑:id=1001 [返回通知] 方法:getUserById,返回值:用户-1001 [环绕后置] 方法:getUserById执行完成

三、关键知识点解析

  1. 切点表达式(execution 语法)上面用的execution(* com.example.service.*.*(..))是最常用的切点表达式,拆解如下:

    • *:返回值任意(第一个 *)
    • com.example.service.*:匹配 service 包下的所有类
    • .*:匹配类下的所有方法
    • (..):方法参数任意(数量、类型都不限)其他常用表达式:
    • 匹配指定方法:execution(public String com.example.service.UserService.getUserById(Integer))
    • 匹配注解:@annotation(org.springframework.transaction.annotation.Transactional)(匹配加了 @Transactional 的方法)
  2. 环绕通知的注意事项

    • ProceedingJoinPointJoinPoint的子类,只有环绕通知能使用。
    • 必须调用joinPoint.proceed()才能执行目标方法,否则目标方法不会运行。
    • 可通过proceed(args)修改目标方法的参数,也可修改返回值。
  3. AOP 的实现原理Spring AOP 基于动态代理实现:

    • 如果目标类实现了接口:使用 JDK 动态代理(生成接口的代理类)。
    • 如果目标类未实现接口:使用 CGLIB 动态代理(生成目标类的子类)。
    • @EnableAspectJAutoProxy(proxyTargetClass = true):强制使用 CGLIB 代理。

四、总结

  1. Spring AOP 实现的核心步骤:引入依赖 → 开启 AOP 注解 → 定义目标 Bean → 编写切面类(@Aspect + 切点 + 通知) → 测试。
  2. 注解式 AOP 是主流方式,核心注解包括@EnableAspectJAutoProxy(开启 AOP)、@Aspect(标记切面)、@Pointcut(定义切点)、各类通知注解(@Before/@Around 等)。
  3. 环绕通知(@Around)最灵活,但使用成本稍高;简单场景优先用 @Before/@AfterReturning 等简单通知。
http://www.jsqmd.com/news/258597/

相关文章:

  • 济南的户外广告投放公司哪家便宜? - 工业品牌热点
  • 【顶级EI复现】基于断线解环思想的配电网辐射状拓扑约束建模方法(Matlab代码实现)
  • 2026年评价高的胶粘剂,水性胶粘剂,注塑胶粘剂厂家选购参考指南 - 品牌鉴赏师
  • 学霸同款8个AI论文写作软件,继续教育学生轻松搞定毕业论文!
  • AI人工智能-强化学习-第十三周(小白)
  • 强烈安利8个AI论文软件,专科生搞定毕业论文必备!
  • 【电压风险评估】基于720个样本与360个样本的Copula及蒙特卡罗推断结果比较研究(Matlab代码实现)
  • 高价回收茅台五粮液 同城上门现金结算 京城亚南让老酒变现更安心 - 品牌排行榜单
  • 2026年评价高的压敏胶,医用压敏胶,环保压敏胶厂家采购参考指南 - 品牌鉴赏师
  • 2026年石墨烯电锅炉厂家推荐榜:辽宁德威赫电热设备有限公司,储水式电锅炉/电锅炉/电热水锅炉/电采暖电锅炉/蓄热电锅炉/超导体电锅炉/电磁电锅炉/半导体电锅炉/全导体电锅炉厂家精选 - 品牌推荐官
  • 【电动车】基于削峰填谷的电动汽车多目标优化调度策略研究(Matlab代码实现)
  • 2025年插补Y哪家强?最新口碑排名揭晓,46排刀机/刀塔车床/四轴机/动力刀塔/双主轴双排刀/插补Y/直Y/双主轴双刀塔插补Y品牌选哪家 - 品牌推荐师
  • 沃尔玛购物卡回收指南,正规平台让你放心变现 - 京回收小程序
  • 详细介绍:nginx代理配置详解
  • 边缘智能革命:让YOLO在FPGA上“飞”起来的软硬协同之道
  • 1.17 模拟赛总结
  • 计及阶梯碳交易成本与多元储能的综合能源系统IES联合低碳优化调度——考虑热电联产机组、燃气机组...
  • Oracle 19c入门学习教程,从入门到精通,SQL语言基础详解:语法、使用方法与综合案例(5)
  • 2026管道除铁器厂家权威推荐榜单:旋转式除铁机/电磁粉料除铁机/旋转式除铁器/电磁除铁机/永磁管道除铁机源头厂家精选。 - 品牌推荐官
  • 可解释集成模型如何提升商品搜索效果
  • Java版LeetCode热题100之二叉树的直径:从深度计算到路径优化的全面解析
  • 2025-2026年徐州高品质住宅评选与推荐报告:于深度调整期中寻找价值确定性 - 2026年企业推荐榜
  • 杭州市英语雅思培训辅导机构推荐,2026权威出国雅思课程排行榜 - 苏木2025
  • 2026年金亿重工高臂钻机市场口碑推荐,浙江地区用户评价分析 - 工业品牌热点
  • 泉州市英语雅思培训辅导机构推荐;2026权威出国雅思课程排行榜 - 苏木2025
  • Java版LeetCode热题100之二叉树的层序遍历:从BFS到多维拓展的全面解析
  • 逆向 讀墨Readmoo iOS客户端:Readium LCP加密
  • ACPI!ACPIBuildProcessRunMethodPhaseCheckSta函数对节点BAT1方法_STA的处理在异步线程ACPI!ACPIWorker
  • 学霸同款8个AI论文写作软件,MBA论文高效搞定!
  • 2026年工业滤芯厂家权威推荐榜单:除杂滤芯/活性炭滤芯/精密滤芯/吸气滤芯/聚结分离滤芯源头厂家精选 - 品牌推荐官