从 01 开始 从 01 开始
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机相关技巧
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • 最全面的 RSS 教程
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金
  • 职场规划
  • 关于离职
  • 杂谈
  • 自媒体
  • 📖 读书

    • 读书工具
    • 走进科学
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
    • 读书笔记
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档

晓林

程序猿,自由职业者,博主,英语爱好者,健身达人
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机相关技巧
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • 最全面的 RSS 教程
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金
  • 职场规划
  • 关于离职
  • 杂谈
  • 自媒体
  • 📖 读书

    • 读书工具
    • 走进科学
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
    • 读书笔记
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档
  • JavaSE

  • JavaSenior

  • JavaEE

  • JavaWeb

  • Spring

    • Spring 介绍
    • 程序中的耦合
    • IoC 的概念和作用
    • Spring 中的依赖注入
    • 基于注解的 IoC
    • 使用基于 XML 的 IoC 完成单表的 CRUD
    • 使用基于注解的 IoC 完成单表的 CRUD
    • IoC 的纯注解配置
    • Spring 整合 Junit
    • 事务问题
    • 代理模式
    • AOP 的概念和入门
    • 基于注解的 AOP
    • Spring 的 JdbcTemplate
    • JdBCDaoSupport
    • 基于 XML 的 AOP 实现事务控制
    • 基于注解的 AOP 实现事务控制
      • 配置 bean.xml
      • 修改 service 类
      • 配置 dao
      • 配置 ConnectionUtils
      • 配置 TransactionManager
      • 测试
      • 使用环绕通知
      • 源码
    • Spring 的事务控制
    • 基于 XML 的声明式事务控制
    • 基于注解的声明式事务控制
    • 纯注解实现事务控制
    • Spring 编程式事务控制
    • Spring5 新特性
  • 主流框架

  • SpringMVC

  • SpringBoot

  • Java
  • Spring
2023-05-08
目录

基于注解的 AOP 实现事务控制

# 130.基于注解的 AOP 实现事务控制

我们改造上一篇博客的案例,改为使用注解的方式  ‍ ‍

# 配置 bean.xml

由于我们使用了注解,因此得加上注解的约束:添加了第 5,10,11 行

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

1
2
3
4
5
6
7
8
9
10
11
12

我们首先配置容器创建时要扫描的包:

<context:component-scan base-package="com.peterjxl"/>
1

# 修改 service 类

在 service 实现类中,加上@Service 的注解,并给 dao 加上 @Autowired 注解,去掉 set 方法

@Service("accountService")
public class AccountServiceImpl implements IAccountService {

    @Autowired
    private IAccountDao accountDao;
1
2
3
4
5

然后我们删除 bean.xml 中的配置 ‍

# 配置 dao

dao 实现类同理,加上@Repository 注解,并给成员变量加上 @Autowired 注解,去掉 set 方法

@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {

    @Autowired
    private QueryRunner runner;

    @Autowired
    private ConnectionUtils connectionUtils;
1
2
3
4
5
6
7
8

然后我们删除 bean.xml 中的配置

# 配置 ConnectionUtils

加上@Compoment 注解,并给 dataSource 加上注解,去掉 set 方法

@Component("connectionUtils")
public class ConnectionUtils {

    private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

    @Autowired
    private DataSource dataSource;
1
2
3
4
5
6
7

然后我们删除 bean.xml 中的配置 ‍

# 配置 TransactionManager

我们做如下事情

  1. 给类加上 Component 的注解,和@Aspect 的注解
  2. 配置切入点表达式
  3. 给各个方法配置通知

完整代码:

package com.peterjxl.utils;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


@Component("transactionManager")
@Aspect
public class TransactionManager {

    @Autowired
    private ConnectionUtils connectionUtils;

    @Pointcut("execution(* com.peterjxl.service.impl.*.*(..))")
    private void pt1() {}


    @Before("pt1()")
    public void beginTransaction() {
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    @AfterReturning("pt1()")
    public void commit() {
        try {
            connectionUtils.getThreadConnection().commit();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @AfterThrowing("pt1()")
    public void rollback() {
        try {
            connectionUtils.getThreadConnection().rollback();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @After("pt1()")
    public void release() {
        try {
            connectionUtils.getThreadConnection().close(); // 还回连接池中
            connectionUtils.removeConnection();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

配置 bean.xml,我们删除其 bean 配置,和 AOP 的配置,然后加上

 <aop:aspectj-autoproxy/>
1

# 测试

此时我们就已经用注解改造完了,完整的配置如下:

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
  
    <context:component-scan base-package="com.peterjxl"/>
    <aop:aspectj-autoproxy/>

    <!--配置QueryRunner-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"/>

    <!-- 配置数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--连接数据库的必备信息-->
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/learnSpring"/>
        <property name="user" value="learnSpringUser"/>
        <property name="password" value="learnSpringPassword"/>
    </bean>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

‍ 然后我们测试下,有异常的情况下能否正常运行,运行结果:

java.lang.RuntimeException: java.sql.SQLNonTransientConnectionException: Can't call rollback when autocommit=true
1

‍ 这是因为使用注解的时候,执行顺序是有问题的,有异常时先执行了最终通知,也就是执行了 release 方法释放了连接,然后再回滚,是会出错的

即使没有异常,提交也是不行的,因为提交的时候先执行了最终通知,释放了连接,然后 commit,当然也是不行的

综上,不管转账时有无异常,数据库数据都没有变化,因为既没有提交,也没有回滚。只能用环绕通知 ‍

# 使用环绕通知

我们将其他方法上面的注解注释掉,然后在 TransactionManager 新加一个方法:

@Around("pt1()")
public Object aroundAdvice(ProceedingJoinPoint pjp) {
    Object rtValue = null;
    try {
        // 1. 获取参数
        Object[] args = pjp.getArgs();
        // 2. 开启事务
        this.beginTransaction();
        // 3. 执行方法
        rtValue = pjp.proceed(args);
        // 4. 提交事务
        this.commit();
        // 5. 返回结果
        return rtValue;
    } catch (Throwable e) {
        // 6. 回滚事务
        this.rollback();
        throw new RuntimeException(e);
    } finally {
        // 7. 释放资源
        this.release();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

‍ 此时我们再进行测试,能看到正常控制事务

# 源码

本项目已将源码上传到 GitHub (opens new window) 和 Gitee (opens new window) 上。并且创建了分支 demo16,读者可以通过切换分支来查看本文的示例代码

上次更新: 2025/6/3 09:31:54
基于 XML 的 AOP 实现事务控制
Spring 的事务控制

← 基于 XML 的 AOP 实现事务控制 Spring 的事务控制→

最近更新
01
学点统计学:轻松识破一本正经的胡说八道
06-05
02
2025 年 5 月记
05-31
03
《贫穷的本质》很棒,但可能不适合你
05-27
更多文章>
Theme by Vdoing | Copyright © 2022-2025 | 粤 ICP 备 2022067627 号 -1 | 粤公网安备 44011302003646 号 | 点击查看十年之约
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式