本文主要是介绍Java AOP面向切面编程的概念和实现方式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概...
一、AOP 是什么?
AOP(ASPect-Oriented Programming),即面向切面编程。它是一种编程范式,与我们所熟悉的OOP(面向对象编程)是互补的关系,而不是替代。
- OOP 的局限:OOP的核心是对象和类,它擅长将功能进行纵向的模块化划分(例如,
UserService
类负责用户相关所有逻辑)。但对于一些需要横向地散布 across 多个模块的功能,OOP就显得力不从心。- 例如:日志记录、性能监控、事务管理、安全校验。这些逻辑几乎需要出现在每一个业务方法中,但它们本质上又不属于核心业务逻辑。
- AOP 的解决方案:AOP将这些散布在各处的横切关注点(Cross-Cutting Concerns)从核心业务逻辑中分离出来,封装成一个独立的可重用的模块,称为切面(Aspect)。然后,在程序运行的合适时机,AOP会自动地将这些切面代码“织入”到需要它们的方法中。
看下面的例子:
面向对象编程OPP完成了穿衣、吃饭、洗碗等这些功能,但从另一个维度去控制这些功能,比如“查看穿衣服、吃饭的时间(性能监控)”、“保证吃饭、洗碗连在一起完成(事务管理)”、“记录洗碗、打扫消耗的用水量(日志记录)”这些就是面向切面编程AOP
- 没有AOP,就得在每个功能上添加相应控制,比如在穿衣服写一遍记录时间的代码,然后再再吃饭处写一遍记录时间的代码。这显然不合理。AOP让核心业务(穿衣、吃饭)和通用功能(记录时间)得以解耦。
二、AOP 的核心概念与实现方式
核心概念
- Aspect(切面):封装横切关注点的模块。它是一个类,上面标注了
@Aspect
注解。例如:LoggingAspect
(日志切面)、TransactionAspect
(事务切面)。 - Advice(通知):切面中的具体方法。它定义了“做什么”以及“何时做”。
- 何时做:
@Before
(方法前)、@After
(方法后)、@AfterReturning
(成功返回后)、@AfterThrowing
(抛出异常后)、@Around
(环绕,最强大)。 - Pointcut(切点):一个表达式,定义了“在何处做”,即匹配哪些类的哪些方法需要被增强。它决定了Advice的应用位置。
- Join Point(连接点):程序执行过程中能够插入切面的一个点,例如方法调用、异常抛出等。在Spring AOP中,连接点总是代表一个方法的执行。
- Weaving(织入):将切面代码应用到目标对象,从而创建代理对象的过程。Spring AOP在运行时通过动态代理完成织入。
实现方式
Spring AOP 的底层就是基于我们之前讨论过的动态代理:
- 如果目标对象实现了接口,默认使用 JDK 动态代理。
- 如果目标对象没有实现接口,则使用 CGLIB 库生成子类进行代理。
三、Spring AOP 的关键注解
注解 | 说明 |
---|---|
@Aspect | 声明一个类是切面。 |
@Pointcut | 声明一个切点表达式,可被通知方法引用,避免重复书写。 |
@Before | 前置通知:在目标方法执行之前执行。 |
@AfterReturning | 返回通知:在目标方法成功执行并返回后执行。 |
@AfterThrowing | 异常通知:在目标方法抛出异常后执行。 |
@After | 后置通知:在目标方法执行之后执行(无论成功还是异常,类似于finally )。 |
@Around | 环绕通知:最强大的通知类型,可以手动控制目标方法的执行时机,可以在方法执行前后添加自定义行为。 |
四、实际场景与代码示例
场景一:记录洗碗和打扫的用水量
1. 业务类(核心方法)
WashDish.java - 洗碗类
@Component public class WashDish { private int water; public int fillWater(int amount) { this.water += amount; System.out.println("洗碗接水: " + amount + "升"); return amount; } public void wash() { System.out.println("使用" + water + "升水洗碗"); water = 0; } }
CleanRoom.java - 打扫类
@Component public class CleanRoom { private int water; public int fillWater(int amount) { this.water += amount; System.out.println("打扫接水: " + amount + "升"); return amount; } public void clean() { System.out.println("使用" + water + "升水打扫房间"); water = 0; } }
2. 切面类(AOP实现)
WaterUsageLoggerAspect.java - 用水量记录切面
/** * 切面类 */ @Aspect @Component public class WaterUsageLoggerAspect { private Map<String, Integer> waterUsageRecords = new HashMap<>(); /** * 切入点:规定哪些类被控制 */ @Pointcut("execution(* WashDish.fillWater(..)) || execution(* CleanRoom.fillWater(..))") public void waterUsagePointcut() {} /** * 通知 */ @AfterReturning(pointcut = "waterUsagePointcut()", returning = "waterAmount") public void logWaterUsage(int waterAmount) { String activity = "家务活动"; wphpaterUsageRecords.put(activity, waterUsageRecords.getOrDefault(activity, 0) + waterAmount); System.out.println("记录用水: " + waterAmount + "升,当前总用水量: " + waterUsageRecords.get(activity) + "升"); } public int getTotalWaterUsage() { return waterUsageRecords.values().stream().mapToInt(Integer::intValue).sum(); } }
3. 配置类(Spring配置)
AppConfig.java - 应用配置
@Configuration @EnableAspectJAutoProxy @ComponentScan("com.example.aop") public class AppConfig { }
4. 测试类(演示代码)
HomeChoresTest.java - 家务测试
public class HomeChoresTest { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); WashDish washDish = context.getBean(WashDish.class); CleanRoom cleanRoom = context.getBean(CleanRoom.class); WaterUsageLoggerAspect waterLogger = context.getBean(WaterUsageLoggerAspect.class); System.out.println("=== 开始家务活动 ==="); // 洗碗活动 System.out.println("\n--- 洗碗 ---"); washDish.fillWater(10); washDish.wash(); // 打扫活动 System.out.println("\n--- 打扫 ---"); cleanRoom.fillWater(15); cleanRoom.clean(); System.out.println("\n=== 家务完成 ==="); System.out.println("总用水量: " + waterLogger.getTotalWaterUsage() + "升"); context.close(); } }
5. 预期输出
运行测试类后,预期输出如下:
=== 开始家务活动 ===
--- 洗碗 ---
洗碗接水: 10升
记录用水: 10升,当前总用水量: 10升
使用10升水洗碗
--- 打扫 ---
打扫接水: 15升
记录用水: 15升,当前总用水量: 25升&nbsChina编程p;
使用15升水打扫房间
=== 家务完成 === 总用水量: 25升
代码说明
- 业务类:包含核心的家务逻辑(洗碗和打扫),每个类都有一个
fillWater
方法用于接水。 - 切面类:
- 使用
@Aspect
注解标记为切面 - 使用
@Pointcut
定义切入点,匹配所有接水操作 - 使用
@AfterReturning
后置通知记录用水量 - 维护用水记录并提供查询接口
- 使用
- 配置类:启用Spring AOP自动代理和组件扫描
- 测试类:演示如何使用AOP记录家务活动的用水量
五、AOP 的常见应用场景
- 日志记录:如上例,记录方法入参、出参、执行耗时,用于调试和监控。
- 事务管理:这是最经典的应用! Spring的
@Transactional
注解就是基于AOP实现的。它在方法开始时开启事务,在方法成功执行后提交事务,在抛出异常时回滚事务。 - 权限校验和安全控制:在方法执行前,判断当前用户是否有权限执行此操作。例如使用
@PreAuthorize
注解。 - 性能监控:统计方法的执行时间,上报给监控系统,用于发现性能瓶颈。
- 异常处理与统一返回:捕获服务层抛出的异常,将其转换为友好的错误信息格式返回给前端。
- 缓存:在方法执行前检查缓存中是否有数据,如果有则直接返回,否则执行方法并将结果放入缓存。
总结
- AOP是什么:一种将横切关注点(日志、事务等)与核心业务逻辑分离的技术。
- 如何实现:Spring AOP通过动态代理在运行时将切面“织入”到目标方法中。
- 核心注解:
@Aspect
,@Pointcut
,@Around
,@Before
,@After
等。 - 为何需要:实现解耦、提高代码的可复用性和可维护性,让开发者能更专注于核心业务逻辑。
通过AOP,我们可以以一种javascript非常优雅和非侵入式的方式,为应用程序添加强大的功能。
到此这篇关于java:AOP面向切面编程的文章就介绍到这了,更多相关java aop切面内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.cwww.chinasem.cnppcns.com)!
这篇关于Java AOP面向切面编程的概念和实现方式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!