当前位置: 首页 > news >正文

Spring的深入浅出(6)--使用AOP的思想改造转账案例

在Spring中使用AOP(基于注解)


使用AOP的思想改造转账案例

业务类改造:移除事务管理代码

public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}

    // 转账业务逻辑(纯业务代码)
public void transfer(String source, String target, Double money) {
Account sourceAccount = accountDao.findAccountByName(source);
Account targetAccount = accountDao.findAccountByName(target);

if (sourceAccount != null && targetAccount != null) {
sourceAccount.setMoney(sourceAccount.getMoney() - money);
accountDao.updateAccount(sourceAccount);

// 模拟异常(测试事务回滚)
int num = 100 / 0;

targetAccount.setMoney(targetAccount.getMoney() + money);
accountDao.updateAccount(targetAccount);
}
}
}

优点:

  1. 移除了所有事务管理相关的代码(begin/commit/rollback)
  2. 业务逻辑更纯净,只关注核心转账功能
  3. 保留了模拟异常的代码用于测试事务回滚
  4. 符合单一职责原则,职责分离更清晰

事务管理器(切面类)

public class TransactionManager {
private ConnectionUtils connectionUtils;

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}

    // 开启事务
public void beginTransaction() {
try {
Connection connection = connectionUtils.getConnection();
connection.setAutoCommit(false);
} catch (SQLException e) {
throw new RuntimeException("开启事务失败", e);
}
}

    // 提交事务
public void commitTransaction() {
try {
connectionUtils.getConnection().commit();
} catch (SQLException e) {
throw new RuntimeException("提交事务失败", e);
}
}

    // 回滚事务
public void rollBackTransaction() {
try {
connectionUtils.getConnection().rollback();
} catch (SQLException e) {
throw new RuntimeException("回滚事务失败", e);
}
}

    // 关闭连接(带资源清理)
public void closeConnection() {
try {
Connection conn = connectionUtils.getConnection();
conn.setAutoCommit(true); // 恢复自动提交
conn.close(); // 实际应归还连接池
connectionUtils.removeConnection(); // 清除ThreadLocal
} catch (SQLException e) {
throw new RuntimeException("关闭连接失败", e);
}
}
}

优点:

  1. 完整的事务管理功能封装(开启/提交/回滚/关闭)
  2. 使用ConnectionUtils确保线程安全的连接管理
  3. closeConnection()中恢复自动提交状态是良好实践
  4. 异常处理升级为运行时异常,符合Spring事务管理规范

Spring AOP配置(XML方式)

        

<!-- 1. 引入AOP命名空间 -->
xmlns:aop="http://www.springframework.org/schema/aop"

<!-- 2. 定义事务管理器Bean -->
<bean id="transactionManager" class="com.xq.utils.TransactionManager">
<property name="connectionUtils" ref="connectionUtils"/>
</bean>

<!-- 3. 配置AOP -->
<aop:config>
<!-- 定义切点:AccountServiceImpl的transfer方法 -->
<aop:pointcut id="txPointcut" 
expression="execution(* com.xq.service.impl.AccountServiceImpl.transfer(..))"/>

<!-- 配置切面 -->
<aop:aspect ref="transactionManager" order="1">
<!-- 前置通知:开启事务 -->
<aop:before method="beginTransaction" pointcut-ref="txPointcut"/>

<!-- 返回通知:提交事务 -->
<aop:after-returning method="commitTransaction" pointcut-ref="txPointcut"/>

<!-- 异常通知:回滚事务 -->
<aop:after-throwing method="rollBackTransaction" 
pointcut-ref="txPointcut" 
throwing="ex"/>

<!-- 最终通知:关闭连接 -->
<aop:after method="closeConnection" pointcut-ref="txPointcut"/>
</aop:aspect>
</aop:config>

分析:

切点定义:精确匹配transfer()方法执行

通知顺序

  • before:方法执行前开启事务

  • after-returning:方法正常返回时提交

  • after-throwing:方法抛出异常时回滚

  • after:最终关闭连接(类似finally)

  • 异常处理:通过throwing="ex"捕获异常对象

事务完整性:四种通知组合确保事务的ACID特性

账户实体类(Account.java)

public class Account {
private String name;
private Double money;

    // 构造器/getter/setter
public Account() {}

    public Account(String name, Double money) {
this.name = name;
this.money = money;
}

    // 省略getter/setter
}

连接工具类(ConnectionUtils.java)

使用ThreadLocal管理连接

public class ConnectionUtils {
// 使用ThreadLocal保证线程安全
private static final ThreadLocal<Connection> tl = new ThreadLocal<>();

// 数据源(通过Spring注入)
private DataSource dataSource;

public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}

// 获取当前线程的连接
public Connection getConnection() throws SQLException {
Connection conn = tl.get();
if (conn == null) {
conn = dataSource.getConnection();
tl.set(conn);
}
return conn;
}

// 解除当前线程的连接绑定
public void removeConnection() {
tl.remove();
}
}

DAO接口以及DAO实现

public interface AccountDao {
Account findAccountByName(String name) throws SQLException;
void updateAccount(Account account) throws SQLException;
}

public class AccountDaoImpl implements AccountDao {
private QueryRunner runner;
private ConnectionUtils connectionUtils;

public void setRunner(QueryRunner runner) {
this.runner = runner;
}

public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}

    @Override
public Account findAccountByName(String name) throws SQLException {
return runner.query(
connectionUtils.getConnection(),
"SELECT * FROM account WHERE name = ?",
new BeanHandler<>(Account.class),
name
);
}

    @Override
public void updateAccount(Account account) throws SQLException {
runner.update(
connectionUtils.getConnection(),
"UPDATE account SET money = ? WHERE name = ?",
account.getMoney(),
account.getName()
);
}
}

编写测试类

public class TransferTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService service = context.getBean("accountService", AccountService.class);

try {
// 执行转账:Tom给Jerry转500元
service.transfer("Tom", "Jerry", 500.0);
System.out.println("转账成功!");
} catch (Exception e) {
System.out.println("转账失败:" + e.getMessage());
e.printStackTrace();
}
}
}

http://www.dtcms.com/a/299485.html

相关文章:

  • 人形机器人指南(八)操作
  • 手动开发一个串口调试工具(二):Qt 串口类基本认识与使用
  • 基于 ThinkPHP 开发的垂直化网址导航
  • Linux进程地址空间:深入探索其结构与机制
  • 元宇宙新基建:重塑数字市场的“超大陆”边界
  • 【Android】内容提供器
  • 7️⃣ 递归函数
  • 【AcWing 835题解】滑动窗口
  • 数据结构 双向链表
  • greenhills编译出错问题
  • C++学习之深入学习模板(进阶)
  • SAPUI5 树形表格TreeTable示例
  • Spring AI(14)——文本分块优化
  • java之23种设计模式
  • 设计模式:Memento 模式详解
  • 简单实现支付密码的页面及输入效果
  • 面条式代码(Spaghetti Code)
  • Java高级之基于Java Attach与Byte-Buddy实现SQL语句增强
  • JWT安全机制与最佳实践详解
  • Linux 系统调用详解:操作文件的常用系统调用
  • Vulnhub jangow-01-1.0.1靶机渗透攻略详解
  • 自定义定时任务功能详解
  • MySQL 表的约束
  • 【面板数据】中国A股上市公司制造业智能制造数据集(1992-2024年)
  • 基于图神经网络的星间路由与计算卸载强化学习算法设计与实现
  • java实现一个方法,isTure则程序继续往下,为false则return的链式写法
  • 零基础学习性能测试第三章:jmeter线程组组合
  • LeetCode|Day26|191. 位 1 的个数|Python刷题笔记
  • Java学习|黑马笔记|Day23】网络编程、反射、动态代理
  • AI+预测3D新模型百十个定位预测+胆码预测+去和尾2025年7月26日第150弹