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);
}
}
}
优点:
- 移除了所有事务管理相关的代码(begin/commit/rollback)
- 业务逻辑更纯净,只关注核心转账功能
- 保留了模拟异常的代码用于测试事务回滚
- 符合单一职责原则,职责分离更清晰
事务管理器(切面类)
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);
}
}
}
优点:
- 完整的事务管理功能封装(开启/提交/回滚/关闭)
- 使用
ConnectionUtils
确保线程安全的连接管理 closeConnection()
中恢复自动提交状态是良好实践- 异常处理升级为运行时异常,符合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();
}
}
}