news 2026/2/15 4:30:54

Spring框架:AOP

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring框架:AOP

在软件开发的世界里,我们不断追求着代码的高内聚、低耦合和可维护性。当系统变得复杂时,一些与核心业务逻辑无关但又必不可少的功能,如日志记录、事务管理、权限校验等,会不可避免地散落在各个业务模块中,形成大量重复代码,这给维护带来了巨大挑战。

Spring AOP(面向切面编程)正是为解决这一问题而生的利器。它如同一位技艺高超的裁缝,能够在不改动衣服(业务代码)本身的情况下,为其巧妙地缝上精美的补丁(横切逻辑)。本文将带你深入理解 AOP 的概念、原理,并通过 XML 和注解两种方式,手把手教你如何在 Spring 中应用 AOP。

一、AOP概念的引入

首先我们来看一下登录的原理

如上图所示这是一个基本的登录原理图,但是如果我们想要在这个登录之上添加一些新的功能,比如权限校验

那么我们能想到的就有两种方法:

①:通过对源代码的修改实现。

②:不通过修改源代码方式添加新的功能 (AOP)

AOP 的解决方案(横向抽取):AOP 提供了一种全新的思路:在不修改 源代码的前提下,动态地为其添加功能。我们将日志和权限校验逻辑抽离出来,形成独立的 “切面”,然后在程序运行时,由框架将这些切面 “织入” 到目标方法的执行流程中。

这就是 AOP 的核心思想:将横切关注点(Cross-cutting Concerns)与业务逻辑分离

AOP的优势:

运行期间,不修改源代码的情况下对已有的方法进行增强

1. 减少重复的代码

2. 提供开发的效率

3. 维护方便

二、AOP的核心概念

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程

AOP是一种编程范式,隶属于软工范畴,指导开发者如何组织程序结构

AOP最早由AOP联盟的组织提出的,制定了一套规范.Spring将AOP思想引入到框架中,必须遵守AOP联盟的规范

通过预编译方式或者运行期动态代理实现程序功能的统一维护的一种技术

AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型

利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率

AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(事务管理、安全检查、缓存)

为什么要学习AOP,可以在不修改源代码的前提下,对程序进行增强!!

三、AOP的底层逻辑

3.1 JDK的动态代理技术

1、为接口创建代理类的字节码文件

2、使用ClassLoader将字节码文件加载到JVM

3、创建代理类实例对象,执行对象的目标方法

3.2CGLIB 代理技术

为类生成代理对象,被代理类有没有接口都无所谓,底层是生成子类,继承被代理类

四、Spring AOP 的两种实现方式

4.1 基于 XML 的配置

4.1.1 AOP相关的术语

Joinpoint(连接点) 类里面有哪些方法可以增强这些方法称为连接点

Pointcut(切入点) -- 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义

Advice(通知/增强)-- 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)

Aspect(切面)-- 是 切入点+通知 的结合,以后咱们自己来编写和配置的

4.1.2 基本准备工作

AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,AspectJ实际上是对AOP编程思想的一个实践。

4.1.3 AOP配置文件方式的入门

创建maven项目,坐标依赖、

<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!--AOP联盟--> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> <!--Spring Aspects--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.0.2.RELEASE</version> </dependency> <!--aspectj--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.3</version> </dependency> </dependencies>

创建被增强的类

// 被增强的类 public class User { //连接点/切入点 public void add(){ System.out.println("add......"); } public void update(){ System.out.println("update......"); } }

将目标类配置到Spring中

<bean id="user" class="com.aopImpl.User"></bean>

定义切面类

public class UserProxy { //增强/通知 ---》前置通知 public void before(){ System.out.println("before............."); } }

在配置文件中定义切面类

<bean id="userProxy" class="com.aopImpl.UserProxy"></bean>

在配置文件中完成AOP的配置

<!--配置切面--> <aop:config> <!--配置切面 = 切入点 + 通知组成--> <aop:aspect ref="userProxy"> <!--前置通知:UserServiceImpl的save方法执行前,会增强--> <!--pointcut:后边是切入点表达式,作用是知道对对面的那个方法进行增强--> <aop:before method="before" pointcut="execution(public void com.aopImpl.User.add())"/> </aop:aspect> </aop:config>

完成测试

public class DemoTest { @Test public void aopTest1(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); User user = (User) applicationContext.getBean("user"); user.add(); } }

4.1.4 切入点的表达式

再配置切入点的时候,需要定义表达式,具体展开如下:

切入点表达式的格式如下:

execution([修饰符] [返回值类型] [类全路径] [方法名 ( [参数] )])

修饰符可以省略不写,不是必须要出现的。

返回值类型是不能省略不写的,根据你的方法来编写返回值,可以使用 * 代替。

包名,类名,方法名,参数的规则如下:

例如:com.qcby.demo3.BookDaoImpl.save()

首先包名,类名,方法名是不能省略不写的,但是可以使用 * 代替

中间的包名可以使用 * 号代替

类名也可以使用 * 号代替,也有类似的写法:*DaoImpl

方法也可以使用 * 号代替

参数如果是一个参数可以使用 * 号代替,如果想代表任意参数使用 ..

比较通用的表达式:execution(* com.qcby.*.ServiceImpl.save(..))

举例2:com.qcby.demo3.BookDaoImpl当中所有的方法进行增强

execution(* com.qcby.*.ServiceImpl.*(..))

举例3:com.qcby.demo3包当中所有的方法进行增强

execution(* com.qcby.*.*.*(..))

<!--配置切面--> <aop:config> <!--配置切面 = 切入点 + 通知组成--> <aop:aspect ref="userProxy"> <!--切入点的表达式 execution() 固定的写法 public 是可以省略不写的 方法的返回值 int String 通用的写法,可以编写 * 不能省略不写的 包名+类名 不能省略不写的,编写 * com.* 方法名称 add() 可以写 * 参数列表 (..) 表示任意类型和个数的参数 比较通用的表达式:execution(* com.*.User.add(..))--> <aop:before method="before" pointcut="execution(* com.*.User.add(..))"/> </aop:aspect> </aop:config>

4.1.5 AOP的通知类型

1. 前置通知:目标方法执行前,进行增强。

如上配置案例就是前置通知

2. 环绕通知:目标方法执行前后,都可以进行增强。目标对象的方法需要手动执行。

// 环绕通知 public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("before............."); // 执行被增强的方法 proceedingJoinPoint.proceed(); System.out.println("after............."); }

xml配置:

<aop:around method="around" pointcut="execution(* com.*.User.add(..))"/>

3. 最终通知:目标方法执行成功或者失败,进行增强。

// 最终通知 public void after() { System.out.println("after............."); }

xml配置:

<aop:after method="after" pointcut="execution(* com.*.User.add(..))"/>

4. 后置通知:目标方法执行成功后,进行增强。

//后置通知 public void afterReturning() { System.out.println("afterReturning............."); }

xml配置:

<aop:after-returning method="afterReturning" pointcut="execution(public void com.aopImpl.User.add())"/>

5. 异常通知:目标方法执行失败后,进行增强。(发生异常的时候才会执行,否则不执行)

//异常通知 public void afterThrowing() { System.out.println("afterThrowing............."); }

需要改动一下切点:

//连接点/切入点 public void add(){ int a = 10 / 0; System.out.println("add......"); }

xml配置:

<aop:after-throwing method="afterThrowing" pointcut="execution(public void com.aopImpl.User.add())"/>

4.2 基于注解的配置

创建maven工程,导入坐标。编写接口,完成IOC的操作。步骤略。

编写切面类

给切面类添加注解 @Aspect,编写增强的方法,使用通知类型注解声明

1.配置xml扫描注解

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--开启注解扫描--> <context:component-scan base-package="com.aopImpl"></context:component-scan> </beans>

2.配置注解

@Component public class User { //连接点/切入点 public void add(){ System.out.println("add......"); } }

给切面类添加注解 @Aspect,编写增强的方法,使用通知类型注解声明

@Component @Aspect //生成代理对象 public class UserProxy { }

3.配置文件中开启自动代理

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--开启注解扫描--> <context:component-scan base-package="com.aopImpl"></context:component-scan> <!--开启Aspect生成代理对象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>

4.通知类型注解

@Before -- 前置通知

@AfterReturing -- 后置通知

@Around -- 环绕通知(目标对象方法默认不执行的,需要手动执行)

@After -- 最终通知

@AfterThrowing -- 异常抛出通知

@Component @Aspect //生成代理对象 public class UserProxy { //增强/通知 ---》前置通知 @Before(value = "execution(* com.*.User.add(..))") public void before(){ System.out.println("before............."); } // 环绕通知 @Around(value = "execution(* com.*.User.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("before............."); // 执行被增强的方法 proceedingJoinPoint.proceed(); System.out.println("after............."); } // 最终通知 @After(value = "execution(* com.*.User.add(..))") public void after() { System.out.println("after............."); } //异常通知 @AfterThrowing(value = "execution(* com.*.User.add(..))") public void afterThrowing() { System.out.println("afterThrowing............."); } //后置通知 @AfterReturning(value = "execution(* com.*.User.add(..))") public void afterReturning() { System.out.println("afterReturning............."); } }

5.测试类

@Test public void aopTest1(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); user.add(); }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/11 17:27:54

终极QQ空间回忆拯救计划:一键备份所有珍贵历史记录

终极QQ空间回忆拯救计划&#xff1a;一键备份所有珍贵历史记录 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 还记得那些年我们在QQ空间里写下的青春吗&#xff1f;那些深夜的感慨、节…

作者头像 李华
网站建设 2026/2/3 0:58:20

Nucleus Co-Op终极教程:单机游戏轻松实现分屏多人模式

Nucleus Co-Op终极教程&#xff1a;单机游戏轻松实现分屏多人模式 【免费下载链接】nucleuscoop Starts multiple instances of a game for split-screen multiplayer gaming! 项目地址: https://gitcode.com/gh_mirrors/nu/nucleuscoop 还在为那些精彩的单机游戏无法与…

作者头像 李华
网站建设 2026/2/4 7:50:23

M9A自动化助手:解放《重返未来:1999》玩家的终极解决方案

M9A自动化助手&#xff1a;解放《重返未来&#xff1a;1999》玩家的终极解决方案 【免费下载链接】M9A 重返未来&#xff1a;1999 小助手 项目地址: https://gitcode.com/gh_mirrors/m9a/M9A 还在为《重返未来&#xff1a;1999》中重复的日常任务消耗大量时间而苦恼吗&a…

作者头像 李华
网站建设 2026/2/12 23:35:26

如何快速掌握WarcraftHelper:魔兽争霸III优化的完整指南

如何快速掌握WarcraftHelper&#xff1a;魔兽争霸III优化的完整指南 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 还在为《魔兽争霸III》在现代电脑…

作者头像 李华
网站建设 2026/2/13 6:08:14

番茄小说下载神器:打造个人数字图书馆的终极方案

番茄小说下载神器&#xff1a;打造个人数字图书馆的终极方案 【免费下载链接】Tomato-Novel-Downloader 番茄小说下载器不精简版 项目地址: https://gitcode.com/gh_mirrors/to/Tomato-Novel-Downloader 你是否曾经为了找到心仪的小说而辗转多个平台&#xff1f;是否梦想…

作者头像 李华
网站建设 2026/2/14 16:13:36

Applite终极指南:告别命令行的Mac软件管理神器

还在为复杂的终端命令而头疼吗&#xff1f;Applite这款专为Mac用户设计的图形化Homebrew Cask管理工具&#xff0c;将彻底改变您的软件管理方式。通过直观的界面操作替代繁琐的命令行&#xff0c;让软件安装、更新和卸载变得像点击按钮一样简单。 【免费下载链接】Applite User…

作者头像 李华