使用aspectj重构转账项目 事务的例子,通过spring的aspectj编程实现对service的转账操作增加事务处理。并测试是否生效。
使用提供的原先的TransactionUtil的形式进行startTransaction、rollback、commit。
1.maven依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.2.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<version>4.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.6</version>
</dependency>
</dependencies>
2.dao层接口及实现类
public interface AccountDao {
int queryMoney(int id);
int update(int id,int money);
}
import com.cskaoyan.util.TransactionUtils;
import org.apache.commons.dbutils.BaseResultSetHandler;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.springframework.stereotype.Repository;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
@Repository
public class AccountDaoImpl implements AccountDao{
@Override
public int queryMoney(int id) {
try {
QueryRunner queryRunner = new QueryRunner();
String sql = "select money from account where id = ?";
int query = queryRunner.query(TransactionUtils.getConnection(), sql,new ScalarHandler<>(),id);
return query;
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
@Override
public int update(int id, int money) {
String sql = "update account set money = ? where id = ?";
try {
int update = new QueryRunner().update(TransactionUtils.getConnection(), sql, money, id);
return update;
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
}
3.druid.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/j23_db?useSSL=false
username=root
password=root
initialSize=5
maxActive=10
maxWait=3000
maxIdle=8
minIdle=3
4.业务层接口及实现
import java.sql.SQLException;
public interface AccountService {
//from 给 destination 转money
void transfer(int fromId,int destId,int money) throws SQLException;
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.sql.SQLException;
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
AccountDao accountDao;
@Override
public void transfer(int fromId, int destId, int money) throws SQLException {
//将开启事务和关闭事务的代码放到aop中
//TransactionUtils.startTransaction();
int fromMoney = accountDao.queryMoney(fromId) - money;
int destMoney = accountDao.queryMoney(destId) + money;
accountDao.update(fromId,fromMoney);
//制造异常
//int i = 1/0;
accountDao.update(destId,destMoney);
//TransactionUtils.commitTransaction();
}
}
5.工具类
DruidUtils
import com.alibaba.druid.pool.DruidDataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* Druid连接池工具类
* 使用Druid连接池步骤:
* 1、导入Druid-maven
* 2、在项目中新建druid-config.xml配置文件,放在src下
* 3、使用DruidDataSource类来获取DruidDataSource对象(连接池对象,此处无需手动读取配置文件)
* 4、使用DataSource对象调用getConnection()方法即可获取到连接池对象
* @author stone
*
*/
public class DruidUtils {
private static Properties properties;
private static DruidUtils druid;
private static DruidDataSource ds;
static {
properties=new Properties();
try {
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("druid.properties"));
} catch (IOException e) {
e.printStackTrace();
}
ds=new DruidDataSource();
ds.setDriverClassName(properties.getProperty("driverClassName"));
ds.setUrl(properties.getProperty("url"));
ds.setUsername(properties.getProperty("username"));
ds.setPassword(properties.getProperty("password"));
ds.setMaxActive(Integer.parseInt(properties.getProperty("maxActive")));
}
/**
* 创建单列模式
* @return DruidUtils实例
*/
public static synchronized DruidUtils getInstance() {
if(druid==null) {
druid=new DruidUtils();
return druid;
}
return druid;
}
private DruidUtils() {
}
public static Connection getConnection() throws SQLException {
Connection connection = ds.getConnection();
return connection;
}
}
TransactionUtils
import java.sql.Connection;
import java.sql.SQLException;
public class TransactionUtils {
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
/**
* 获取连接
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
Connection con = tl.get();
if(con==null) {//说明当前线程尚未加入连接对象,需要获取连接对象赋值
tl.set(DruidUtils.getConnection());
return tl.get();
}else {
return con;
}
}
/**
* 开启事务
* @throws SQLException
*/
public static void startTransaction() throws SQLException {
getConnection().setAutoCommit(false);;
}
/**
* 提交事务
* @throws SQLException
*/
public static void commitTransaction() throws SQLException {
getConnection().commit();
}
/**
* 回滚事务
* @throws SQLException
*/
public static void rollBackTransaction() throws SQLException {
getConnection().rollback();
}
/**
* 关闭连接
* @throws SQLException
*/
public static void close() throws SQLException{
getConnection().close();
}
}
6.配置文件
application.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 https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- bean definitions here -->
<context:component-scan base-package="com.cskaoyan"/>
<aop:aspectj-autoproxy/>
</beans>
7.切面类及around方法
ServiceAspect
import com.cskaoyan.util.TransactionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.sql.SQLException;
@Component
@Aspect
public class ServiceAspect {
@Pointcut("execution(* com.cskaoyan.service..*(..))")
public void pointcut(){}
@Around("pointcut()")
public Object txAround(ProceedingJoinPoint joinPoint) {
Object proceed = null;
try {
//开启事务
TransactionUtils.startTransaction();
proceed = joinPoint.proceed();
//提交事务
TransactionUtils.commitTransaction();
TransactionUtils.close();
} catch (Throwable throwable) {
try {
//发生异常回滚
TransactionUtils.rollBackTransaction();
} catch (SQLException e) {
e.printStackTrace();
}
throwable.printStackTrace();
}
return proceed;
}
}
8.单元测试
import com.cskaoyan.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.sql.SQLException;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class Test1 {
@Autowired
AccountService accountService;
@Test
public void mytest() throws SQLException {
accountService.transfer(1,2,100);
}
}