Java AOP面向切面编程的概念和实现方式

2025-10-01 01:50

本文主要是介绍Java AOP面向切面编程的概念和实现方式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概...

一、AOP 是什么?

AOP(ASPect-Oriented Programming),即面向切面编程。它是一种编程范式,与我们所熟悉的OOP(面向对象编程)是互补的关系,而不是替代。

  • OOP 的局限:OOP的核心是对象,它擅长将功能进行纵向的模块化划分(例如,UserService类负责用户相关所有逻辑)。但对于一些需要横向地散布 across 多个模块的功能,OOP就显得力不从心。
    • 例如:日志记录、性能监控、事务管理、安全校验。这些逻辑几乎需要出现在每一个业务方法中,但它们本质上又不属于核心业务逻辑。
  • AOP 的解决方案:AOP将这些散布在各处的横切关注点(Cross-Cutting Concerns)从核心业务逻辑中分离出来,封装成一个独立的可重用的模块,称为切面(Aspect)。然后,在程序运行的合适时机,AOP会自动地将这些切面代码“织入”到需要它们的方法中。
http://www.chinasem.cn

看下面的例子:

Java 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面向切面编程的概念和实现方式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1156125

相关文章

C#借助Spire.XLS for .NET实现在Excel中添加文档属性

《C#借助Spire.XLSfor.NET实现在Excel中添加文档属性》在日常的数据处理和项目管理中,Excel文档扮演着举足轻重的角色,本文将深入探讨如何在C#中借助强大的第三方库Spire.... 目录为什么需要程序化添加Excel文档属性使用Spire.XLS for .NET库实现文档属性管理Sp

Python+FFmpeg实现视频自动化处理的完整指南

《Python+FFmpeg实现视频自动化处理的完整指南》本文总结了一套在Python中使用subprocess.run调用FFmpeg进行视频自动化处理的解决方案,涵盖了跨平台硬件加速、中间素材处理... 目录一、 跨平台硬件加速:统一接口设计1. 核心映射逻辑2. python 实现代码二、 中间素材处

Java方法重载与重写之同名方法的双面魔法(最新整理)

《Java方法重载与重写之同名方法的双面魔法(最新整理)》文章介绍了Java中的方法重载Overloading和方法重写Overriding的区别联系,方法重载是指在同一个类中,允许存在多个方法名相同... 目录Java方法重载与重写:同名方法的双面魔法方法重载(Overloading):同门师兄弟的不同绝

idea设置快捷键风格方式

《idea设置快捷键风格方式》在IntelliJIDEA中设置快捷键风格,打开IDEA,进入设置页面,选择Keymap,从Keymaps下拉列表中选择或复制想要的快捷键风格,点击Apply和OK即可使... 目录idea设www.chinasem.cn置快捷键风格按照以下步骤进行总结idea设置快捷键pyth

Linux镜像文件制作方式

《Linux镜像文件制作方式》本文介绍了Linux镜像文件制作的过程,包括确定磁盘空间布局、制作空白镜像文件、分区与格式化、复制引导分区和其他分区... 目录1.确定磁盘空间布局2.制作空白镜像文件3.分区与格式化1) 分区2) 格式化4.复制引导分区5.复制其它分区1) 挂载2) 复制bootfs分区3)

Spring配置扩展之JavaConfig的使用小结

《Spring配置扩展之JavaConfig的使用小结》JavaConfig是Spring框架中基于纯Java代码的配置方式,用于替代传统的XML配置,通过注解(如@Bean)定义Spring容器的组... 目录JavaConfig 的概念什么是JavaConfig?为什么使用 JavaConfig?Jav

Java数组动态扩容的实现示例

《Java数组动态扩容的实现示例》本文主要介绍了Java数组动态扩容的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1 问题2 方法3 结语1 问题实现动态的给数组添加元素效果,实现对数组扩容,原始数组使用静态分配

Java中ArrayList与顺序表示例详解

《Java中ArrayList与顺序表示例详解》顺序表是在计算机内存中以数组的形式保存的线性表,是指用一组地址连续的存储单元依次存储数据元素的线性结构,:本文主要介绍Java中ArrayList与... 目录前言一、Java集合框架核心接口与分类ArrayList二、顺序表数据结构中的顺序表三、常用代码手动

JAVA项目swing转javafx语法规则以及示例代码

《JAVA项目swing转javafx语法规则以及示例代码》:本文主要介绍JAVA项目swing转javafx语法规则以及示例代码的相关资料,文中详细讲解了主类继承、窗口创建、布局管理、控件替换、... 目录最常用的“一行换一行”速查表(直接全局替换)实际转换示例(JFramejs → JavaFX)迁移建

Spring Boot Interceptor的原理、配置、顺序控制及与Filter的关键区别对比分析

《SpringBootInterceptor的原理、配置、顺序控制及与Filter的关键区别对比分析》本文主要介绍了SpringBoot中的拦截器(Interceptor)及其与过滤器(Filt... 目录前言一、核心功能二、拦截器的实现2.1 定义自定义拦截器2.2 注册拦截器三、多拦截器的执行顺序四、过