SpringBoot AOP 和 事务
SpringBoot 整合 AOP
动态代理技术
JDK 动态代理
- JDK 动态代理是 Java 自带的一种代理方式。它要求目标类必须有接口,基于这个接口,JDK 在运行时会动态生成一个代理对象。这个代理对象和目标对象就像 “拜把子” 的兄弟,因为它们都实现了相同的接口,能以相同的方式被调用,只是代理对象在调用真正的方法前后可以添加额外的逻辑。
.CGLIB 动态代理
- CGLIB 动态代理是另一种代理技术。它的厉害之处在于不需要目标类有接口,通过让代理类继承目标类来实现代理,就好比代理类 “认” 目标类为 “干爹”。这样,代理类就能在目标类方法调用的前后插入自己的逻辑,从而对目标类的行为进行增强。
Spring AoP
AoP 概述

AOP 核心概念
- 切入点:实际被 AOP 控制的方法,需要被增强的方法
- 通知:封装共享功能的方法就是通知

AoP 通知类型
环绕就是一部分在目标方法之前,一部分在之后
- 它的返回值代表的是原始方法执行完毕的返回值

try {前置通知 @Before目标方法执行返回后通知 @AfterReturning
} catch() {异常后通知 @AfterThrowing
} finally {后置通知 @After
}
AoP 通知顺序

AoP 切点表达式 @execution



AoP 切点表达式重用
第一种:创建存储切点的类维护
创建一个存储切点的类
单独维护切点表达式
execution 使用:类全限定符.方法名()
- 切点维护类
@Component
public class MyPointCut {@Pointcut("execution(* com.atguigu.service.impl.*.*(..))")public void pc(){}
- 重用类演示类
@Before("com.atguigu.pointcut.MyPointCut.pc()")public void start() {System.out.println("方法开始了");}@After("com.atguigu.pointcut.MyPointCut.pc()")public void after() {System.out.println("方法结束了");}@AfterThrowing("com.atguigu.pointcut.MyPointCut.pc()")public void error() {System.out.println("方法报错了");}
第二种:当前类中提取表达式
定义一个空方法
注解 @Pointcut()
增强注解中引用切点 直接调用方法名
@Aspect
@Component
public class UserServiceAspect {// 定义一个空方法,使用@Pointcut注解来定义切点表达式,这里表示匹配UserService接口下的所有方法@Pointcut("execution(* com.example.demo.service.UserService.*(..))")public void userServicePointcut() {}// 在前置通知中复用上面定义的切点表达式,直接写切点方法名即可@Before("userServicePointcut()")public void beforeAddUser() {System.out.println("在执行UserService的方法前执行的逻辑");}
AoP 基本使用
底层技术组成

动态代理(InvocationHandler):
JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口(兄弟两个拜把子模式)。cglib:通过继承被代理的目标类(认干爹模式)实现代理,所以不需要目标类实现接口。AspectJ:SpringAOP借用了AspectJ中的AOP注解。也就是说@Before等注解都来自 AspectJ
默认代理方式演示(JDK原生)

第一步:导入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
第二步:准备接口和实现类
public interface Calculator {int add(int i, int j);
}
@Component
public class CalculatorPureImpl implements Calculator {@Overridepublic int add(int i, int j) {int result = i + j;return result;}
}
第三步:声明切面类 @Aspect
切面类要加入 IoC 容器
并且要使用@Aspect声明为切面类
@Aspect
@Component
public class LogAdvice {@Pointcut("execution(* com.mangfu.Calculator.*(..))")public void Mypointcut(){}@Before("Mypointcut()")public void testBefore() {System.out.println("before");}@AfterReturning("Mypointcut()")public void testAfterReturning() {System.out.println("afterReturning");}@AfterThrowing("Mypointcut()")public void testAfterThrowing() {System.out.println("afterThrowing");}@After("Mypointcut()")public void testAfter() {System.out.println("after");}
}
环绕通知
就相当于直接进入切面类执行
- 接口和实现类
public interface Calculator {int add(int i, int j);
}
@Component
public class CalculatorPureImpl implements Calculator {@Overridepublic int add(int i, int j) {int result = i + j;return result;}
}
- 配置类开启 AspectJ
@Configuration
@ComponentScan("com.mangfu")
@EnableAspectJAutoProxy
public class MyConfig {
}
- 切面类
ProceedingJoinPoint joinPoint:目标方法对象jointPoint.getArgs(): 获取目标方法运行的参数joinPoint.proceed(args): 执行目标方法
@Aspect
@Component
public class LogAdvice {@Around("execution(* com.mangfu.Calculator.*(..))")public Object around(ProceedingJoinPoint joinPoint) {Object[] args = joinPoint.getArgs(); //获取目标方法运行的参数Object result = null; //用于接收目标参数的返回值try {System.out.println("Before");result = joinPoint.proceed(args); //调用目标方法System.out.println("AfterReturning");} catch (Throwable e) {System.out.println("Afterthrowing");throw new RuntimeException(e);} finally {System.out.println("After");}return result;}
}
- 测试类
@SpringJUnitConfig(MyConfig.class)
public class MyTest {@Autowiredprivate Calculator calculator;@Testpublic void test() {System.out.println(calculator.add(1, 2));}
}
JoinPoint 详解


CGLib 动态代理生效情况
在目标类没有实现任何接口的情况下,Spring会自动使用cglib技术实现代理。
- 如果目标类有接口, 选择使用
jdk动态代理:实现目标接口- 如果目标类没有接口, 选择
cglib动态代理:继承目标对象- 如果有接口, 就用接口接值
- 如果没有接口, 就用类进行接值
- 没实现接口的实体类和切面类
@Component
public class Calculator {public int add(int i, int j) {int result = i + j;return result;}
}
@Aspect
@Component
public class testBefore {@Before("execution(* com.mangfu2.Calculator.add(..))" )public void before(){System.out.println("test sucessful");}
}
- 测试类
@SpringJUnitConfig(MyConfig.class)
public class MyTest {//建议用接口取。防止后期取不到代理类对象//正常:aop - aop底层选择代理 - 选择jdk代理 - 根据接口生成代理类 - 代理对象和目标对象 是拜把子 兄弟关系。不是同一个//这里实现类没有实现接口所以用 CGLib 动态代理//aop - ioc 容器中真正存储的是代理对象不是目标对象@Autowiredprivate Calculator calculator;@Testpublic void test(){calculator.add(1,2);}}
Spring AoP 对获取 Bean 的影响
JDK 原生代理
声明一个接口,其仅有一个实现类,同时创建切面类对该接口的实现类应用通知:
- 按接口类型获取 bean 可正常获取。
- 按类获取 bean 则无法获取,原因在于应用切面后,实际存放在 IOC 容器中的是代理类对象,目标类本身并未放入 IOC 容器,所以依据目标类类型无法从 IOC 容器中找到相应对象


CGLib 代理
声明一个类,创建一个切面类,对上面的类应用通知
- 根据类获取 bean,能获取到


Spring TX 声明式事务
声明式事务概念
定义:通过注解或 XML 配置的方式控制事务的提交和回滚,具体事务实现由第三方框架负责,开发者只需添加相应配置,无需直接进行事务操作。优点:可将事务控制和业务逻辑分离,提升代码的可读性与可维护性。


spring-tx: 包含声明式事务实现的基本规范(事务管理器规范接口和事务增强等等)spring-jdbc: 包含DataSource方式事务管理器实现类DataSourceTransactionManagerspring-orm: 包含其他持久层框架的事务管理器实现类例如:Hibernate/Jpa等
.
spring-tx就是 Spring 官方提供的事务接口,各大持久层框架需要自己实现这个接口,而spring-jdbc就是jdbc,jdbcTemplate,mybatis的实现类DataSourceTranscationManager。spring-orm就是Hibernate/Jpa等持久层框架的实现类
声明式事务基本使用
SpringBoot项目会自动配置一个DataSourceTransactionManager,所以我们只需在方法(或者类)加上@Transactional注解,就自动纳入Spring的事务管理了
第一步:加入依赖
jdbc, jdbcTemplate, mybatis这三个持久层框架情况
<!-- 第三方实现 spring 事务接口的类的依赖-->
<!-- spring-jdbc --><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>6.0.6</version></dependency>
- 如果是
Hibernate等其他持久层框架就用spring-jdbc换成spring-orm依赖
第二步:给需要事务的地方加上 @Transactional 注解
@Treanscational加在类上就是类里所有方法开启事务。加在方法上就单独那个方法有事务
- Dao层
@Repository
public class StudentDao {@Autowiredprivate JdbcTemplate jdbcTemplate;public void updateNameById(String name,Integer id){String sql = "update students set name = ? where id = ? ;";int rows = jdbcTemplate.update(sql, name, id);}public void updateAgeById(Integer age,Integer id){String sql = "update students set age = ? where id = ? ;";jdbcTemplate.update(sql,age,id);}
}
- Service 层
@Service
public class StudentService {@Autowiredprivate StudentDao studentDao;/*** 添加事务:* @Transctional* 位置: 方法 | 类上* 方法: 当前方法有事务* 类上: 泪下所有方法都有事务*///这里有自动事务了。所以可以报错可以自动回滚@Transactionalpublic void changeInfo(){studentDao.updateAgeById(88,1);int i = 1/0;System.out.println("-----------");studentDao.updateNameById("test1",1);}
}
- 测试类
@SpringJUnitConfig(JavaConfig.class)
public class TxTest {@Autowired private StudentService studentService;@Testpublic void testTx(){studentService.changeInfo();}
}
事务的属性
事务属性:只读
只读模式可以提升查询事务的效率!,一般情况都是通过类添加注解添加事务,类下的所有方法都有事务,而查询方法一般不用添加事务。这个时候可以再次添加事务注解,设置为只读,提高效率查询效率
@Transactional(readOnly = ...)
@Transactional
@Service
public class StudentService {@Autowiredprivate StudentDao studentDao;//查询 没有必要添加事务。这里设置为只读提高效率@Transactional(readOnly = true)public void getStudentInfo() {//获取学生信息 查询数据库 不修改}}
事务属性:超时
事务在执行过程中,有可能因为遇到某些问题,导致程序卡住,从而长时间占用数据库资源。而长时间占用资源,大概率是因为程序运行出现了问题(可能是Java程序或MySQL数据库或网络连接等等)。此时这个很可能出问题的程序应该被回滚,撤销它已做的操作,事务结束,把资源让出来,让其他正常程序可以执行。概括来说就是一句话:超时回滚,释放资源。
@Transactional(timeout = ...)
@Transactional(timeout = 3)
@Service
public class StudentService {@Autowiredprivate StudentDao studentDao;public void changeInfo() {studentDao.updateAgeById(88,1);System.out.println("-----------");try {Thread.sleep(4000);} catch (InterruptedException e) {throw new RuntimeException(e);}studentDao.updateNameById("test2",1);}//因为这个注解会覆盖掉类上注解。所以要再设置一遍@Transactional(readOnly = true, timeout = 3)public void getStudentInfo() {}}
事务属性:回滚
默认情况下,发生运行时 (RuntimeException) 异常事务才回滚,所以我们可以指定 Exception 异常来控制所有异常都回滚
roollbackFor = 回滚的异常范围:设置的异常都回滚noRollbackFor = 不回滚的异常范围:控制某个异常不回滚
//所有异常都回滚,除了 FileNotFoundException
@Transactional(rollbackFor = Exception.class, noRollbackFor = FileNotFoundException.class)public void changeInfo() throws FileNotFoundException {studentDao.updateAgeById(99,1);new FileInputStream("xxxx");studentDao.updateNameById("test2",1);}
事务属性:事务隔离级别
事务并发可能引发的问题
脏读:一个事务读取另一个事务未提交的数据不可重复读:一个事务就是读取了另一个事务提交的修改数据幻读: 一个事务读取了另一个事务提交的插入数据
事务隔离级别
读未提交(Read Uncommitted):事务可以读取未被提交的数据,容易产生脏读、不可重复读和幻读等问题。实现简单但不太安全,一般不用。读已提交(Read Committed):事务只能读取已经提交的数据,可以避免脏读问题,但可能引发不可重复读和幻读。可重复读(Repeatable Read):在一个事务中,相同的查询将返回相同的结果集,不管其他事务对数据做了什么修改。可以避免脏读和不可重复读,但仍有幻读的问题。串行化(Serializable):最高的隔离级别,完全禁止了并发,只允许一个事务执行完毕之后才能执行另一个事务。可以避免以上所有问题,但效率较低,不适用于高并发场景。
.不同的隔离级别适用于不同的场景,需要根据实际业务需求进行选择和调整。
建议设置第二个隔离级别
isolation = Isolation.事务的隔离级别
READ_UNCOMMITTED 读未提交READ_COMMITTED 读已提交REPEATABLE_READ 可重复读SERIALIZABLE 串行化
//设置事务隔离级别为可串行化
@Transactional(isolation = Isolation.SERIALIZABLE)public void changeInfo() throws FileNotFoundException {studentDao.updateAgeById(99,1);new FileInputStream("xxxx");studentDao.updateNameById("test2",1);}
事务属性:事务传播行为
propagation = 传播规则【默认是 Propagation.REQUIRED】
我们一般使用默认就行
| 名称【传播规则】 | 含义 |
|---|---|
REQUIRED | 如果父方法有事务,就加入,如果没有就新建自己独立!父方法有事务报错,子方法会回滚 |
REQUIRES_NEW | 不管父方法是否有事务,我都新建事务,都是独立的!即使父方法有有事务报错。两个子方法也不会回滚 |

就是用父方法
topService()调用 子方法changeAge()和changeName()。这两个子方法是否会进行回滚
相关文章:
SpringBoot AOP 和 事务
SpringBoot 整合 AOP 动态代理技术 JDK 动态代理 JDK 动态代理是 Java 自带的一种代理方式。它要求目标类必须有接口,基于这个接口,JDK 在运行时会动态生成一个代理对象。这个代理对象和目标对象就像 “拜把子” 的兄弟,因为它们都实现了相同…...
Ubuntu全面卸载mysql
如果你已经看到whereis mysql输出了与MySQL相关的路径,说明MySQL仍然存在于系统中。要卸载MySQL,可以按照以下步骤操作,确保完全删除所有相关的文件和配置: 1. 停止MySQL服务 首先,停止MySQL服务: sudo …...
android的gradle
资料: GitHub - ChenSWD/CopyGradleInAction: 备份《Gradle IN Action》书中的源码,添加了部分注释 //github上一个开源项目,外加pdf书 Gradle User Manual gradle官网 讲的挺好的博客 Gradle之重新认识Gradle(项目结构、命令行、tas…...
前端开发之jsencrypt加密解密的使用方法和使用示例
目录 RSA密钥生成选项简介 jsencrypt 使用教程 一、安装 jsencrypt 二、使用 jsencrypt 进行加密和解密 1. 创建密钥对 2. 加密数据 3. 解密数据 三、实际应用示例 加密数据并存储到 localStorage 中: 从 localStorage 中读取加密数据并解密: …...
电路研究9.2.5——合宙Air780EP中GPS 相关命令使用方法研究
注:本命令仅适用于合宙 4G CAT1 模块(Air780EG 系列)。 正好,我们使用的Air780EP好像也有4G CAT1模块,好像也属于Air780EG系列吧。 这个例子好像比较少就个。 18.9 使用方法举例 18.1GPS 开关:ATCGNSPWR 这…...
Python标准库 - os (3) 调度策略、系统信息
文章目录 6 调度策略6.1 调度策略常量6.2 访问和设置进程的调度策略 7 系统信息7.1 系统信息7.2 系统配置信息7.3 系统负载7.4 路径相关常量7.5 生成随机字节 os模块提供了各种操作系统接口。包括环境变量、进程管理、进程调度、文件操作等方面。 这里整理了进程调度和查看系统…...
【NLP251】NLP RNN 系列网络
NLP251 系列主要记录从NLP基础网络结构到知识图谱的学习 1.原理及网络结构 1.1RNN 在Yoshua Bengio论文中( http://proceedings.mlr.press/v28/pascanu13.pdf )证明了梯度求导的一部分环节是一个指数模型…...
【漫话机器学习系列】067.希腊字母(greek letters)-写法、名称、读法和常见用途
希腊字母(Greek Letters) 希腊字母在数学、科学、工程学和编程中广泛使用,常用于表示变量、常量、参数、角度等。以下是希腊字母的完整列表及其常见用途。 大写与小写希腊字母表 大写小写名称(英文)名称(…...
2.文件IO
2.文件IO **1. 文件I/O概述****2. 文件I/O函数接口****3. 文件定位****4. 文件描述符与文件流指针的转换****5. 文件I/O与标准I/O的比较****6. 练习与作业****7. 文件I/O与标准I/O的对应关系****8. 其他注意事项****9. 总结** 1. 文件I/O概述 文件I/O:操作系统为了…...
毕业设计--具有车流量检测功能的智能交通灯设计
摘要: 随着21世纪机动车保有量的持续增加,城市交通拥堵已成为一个日益严重的问题。传统的固定绿灯时长方案导致了大量的时间浪费和交通拥堵。为解决这一问题,本文设计了一款智能交通灯系统,利用车流量检测功能和先进的算法实现了…...
【SpringBoot教程】Spring Boot + MySQL + HikariCP 连接池整合教程
🙋大家好!我是毛毛张! 🌈个人首页: 神马都会亿点点的毛毛张 在前面一篇文章中毛毛张介绍了SpringBoot中数据源与数据库连接池相关概念,今天毛毛张要分享的是关于SpringBoot整合HicariCP连接池相关知识点以及底层源码…...
设计模式的艺术-策略模式
行为型模式的名称、定义、学习难度和使用频率如下表所示: 1.如何理解策略模式 在策略模式中,可以定义一些独立的类来封装不同的算法,每个类封装一种具体的算法。在这里,每个封装算法的类都可以称之为一种策略(Strategy…...
【memgpt】letta 课程1/2:从头实现一个自我编辑、记忆和多步骤推理的代理
llms-as-operating-systems-agent-memory llms-as-operating-systems-agent-memory内存 操作系统的内存管理...
动态规划DP 最长上升子序列模型 合唱队形(题目分析+C++完整代码)
概览检索 动态规划DP 最长上升子序列模型 合唱队形 原题链接 AcWiing 482. 合唱队形 题目描述 N位同学站成一排,音乐老师要请其中的 (N−K)位同学出列,使得剩下的 K位同学排成合唱队形。 合唱队形是指这样的一种队形:设 K位同学从左到右…...
【踩坑】解决Hugging-face下载问题
解决Hugging-face下载问题 问题1:couldnt connect to https://huggingface.co问题2:HTTPSConnectionPool(hostcdn-lfs-us-1.hf-mirror.com, port443)设置hf_transfer加快速度 问题3:requests.exceptions.ChunkedEncodingError: (Connection b…...
Spring AI 在微服务中的应用:支持分布式 AI 推理
1. 引言 在现代企业中,微服务架构 已成为开发复杂系统的主流方式,而 AI 模型推理 也越来越多地被集成到业务流程中。如何在分布式微服务架构下高效地集成 Spring AI,使多个服务可以协同完成 AI 任务,并支持分布式 AI 推理&#x…...
5.3.2 软件设计原则
文章目录 抽象模块化信息隐蔽与独立性衡量 软件设计原则:抽象、模块化、信息隐蔽。 抽象 抽象是抽出事物本质的共同特性。过程抽象是指将一个明确定义功能的操作当作单个实体看待。数据抽象是对数据的类型、操作、取值范围进行定义,然后通过这些操作对数…...
java求职学习day20
1 在线考试系统 1.1 软件开发的流程 需求分析文档、概要设计文档、详细设计文档、编码和测试、安装和调试、维护和升级 1.2 软件的需求分析 在线考试系统的主要功能分析如下: ( 1 )学员系统 (1.1)用户模块&…...
Python NumPy(8):NumPy 位运算、NumPy 字符串函数
1 NumPy 位运算 位运算是一种在二进制数字的位级别上进行操作的一类运算,它们直接操作二进制数字的各个位,而不考虑数字的整体值。NumPy 提供了一系列位运算函数,允许对数组中的元素进行逐位操作,这些操作与 Python 的位运算符类似…...
日志2025.1.30
日志2025.1.30 1.简略地做了一下交互系统 public class Interactable : MonoBehaviour { private MeshRenderer renderer; private Material defaultMaterial; public Material highlightMaterial; private void Awake() { renderer GetComponentInChildren<Me…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向
在人工智能技术呈指数级发展的当下,大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性,吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型,成为释放其巨大潜力的关键所在&…...
【WebSocket】SpringBoot项目中使用WebSocket
1. 导入坐标 如果springboot父工程没有加入websocket的起步依赖,添加它的坐标的时候需要带上版本号。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dep…...
英国云服务器上安装宝塔面板(BT Panel)
在英国云服务器上安装宝塔面板(BT Panel) 是完全可行的,尤其适合需要远程管理Linux服务器、快速部署网站、数据库、FTP、SSL证书等服务的用户。宝塔面板以其可视化操作界面和强大的功能广受国内用户欢迎,虽然官方主要面向中国大陆…...
react更新页面数据,操作页面,双向数据绑定
// 路由不是组件的直接跳转use client,useEffect,useRouter,需3个结合, use client表示客户端 use client; import { Button,Card, Space,Tag,Table,message,Input } from antd; import { useEffect,useState } from react; impor…...
python数据结构和算法(1)
数据结构和算法简介 数据结构:存储和组织数据的方式,决定了数据的存储方式和访问方式。 算法:解决问题的思维、步骤和方法。 程序 数据结构 算法 算法 算法的独立性 算法是独立存在的一种解决问题的方法和思想,对于算法而言&a…...
