Spring事务管理

事务概念

  1. 概念 事务 百度百科中对事务是这样描述的:事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书写的用户程序的执行所引起,并用形如begin transaction和end transaction语句(或函数调用)来界定。事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。

  2. 特性(ACID)

    • 原子性(automicity):一个事务是不可分割的单位,事务包括的所有操作要么都全部成功,要么都全部失败回滚。
    • 一致性(consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。比如银行转账A用户和B用户总金额是10000,无论在两人发生几次转账行为,都必须保证两人总金额是10000,这就是事务的一致性。
    • 隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
    • 持久性(durability):持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
  3. 隔离性

    a) 隔离性的重要性:

    如果忽略隔离性,可能会导致如下问题:

    • 数据脏读:在一个事务中读取到了另一个事务未提交的数据。
    • 不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同。(一个事务读取到另一个事务已经提交的update数据)
    • 虚读(幻读):在一个事务中读取到了别的事务插入的数据,导致前后读取不一致。(一个事务读取到了另一个事务已经提交的insert数据)

      b) 隔离性解决方案:

      设置隔离级别:

    • read uncommitted(未提交读):级别最低。脏读、不可重复读、虚读都有可能发生。

    • read committed(提交读):可避免脏读。不可重复读、虚读都有可能发生。
    • repeatable read(可重复读):可避免脏读、不可重复读。虚读可能发生。
    • serializable(串行化):可避免脏读、不可重复读、虚读的情况。

      安全性:read uncommitted < read commited < repeatable read < serializable

      效率:read uncommitted > read commited > repeatable read > serializable

      MySQL默认隔离级别为:repeatable read. Oracle的默认隔离级别为:read committed.

      注: 数据库设置隔离级别考虑安全性和效率,一般不使用read uncommitted和serializable


Spring事务管理

1.Spring中提供了一组事务管理的API:

PlatformTransactionManager根据TransactionDefinition定义的事务信息管理事务,管理事务过程中产生一些状态,状态由TransactionStatus对象记录。

【PlatformTransactionManager】

a) PlatformTransactionManager作为一个事务基础结构中心的接口,它提供了如下几个实现类,方便我们在实际开发中使用:

  • DataSourceTransactionManager:使用Spring JDBC或MyBatis进行持久化数据时使用
  • HibernateTransactionManager:使用Hibernate进行持久化数据时使用
  • JpaTransactionManager:使用JPA进行持久化时使用
  • JtaTransactionManager:使用JTA进行持久化时使用

b) PlatformTransactionManager方法:

返回值类型 方法及描述
void commit(TransactionStatus status)根据给定事务的状态提交该事务。
TransactionStatus getTransaction(TransactionDefinition definition)根据指定的传播行为,返回当前活动的事务或创建一个新事务。
void rollback(TransactionStatus status)执行给定事务的回滚。

【TransactionDefinition】

a) TransactionDefinition是事务管理的定义接口类,其中包含了一些常量信息:

常量类型 字段名 描述
static int ISOLATION_DEFAULT 使用基础数据存储的默认隔离级别。
static int ISOLATION_READ_COMMITTED 指示防止脏读;可能发生不可重复读取和幻象读取。
static int ISOLATION_READ_UNCOMMITTED 指示可能发生脏读、不可重复读和幻象读。
static int ISOLATION_REPEATABLE_READ 指示防止脏读和不可重复读;可能发生幻象读取。
static int ISOLATION_SERIALIZABLE 指示防止脏读、不可重复读和幻象读。
static int PROPAGATION_MANDATORY 支持当前事务;如果不存在当前事务,则抛出异常。
static int PROPAGATION_NESTED 如果当前事务存在,则在嵌套事务中执行,否则行为类似于PROPAGATION_REQUIRED。
static int PROPAGATION_NEVER 不支持当前事务;如果当前事务存在,则抛出异常。
static int PROPAGATION_NOT_SUPPORTED 不支持当前事务;而是始终以非事务方式执行。
static int PROPAGATION_REQUIRED 支持当前事务;如果不存在,创建一个新的。
static int PROPAGATION_REQUIRES_NEW 创建一个新事务,如果当前事务存在,则挂起当前事务。
static int PROPAGATION_SUPPORTS 支持当前事务;如果不存在,则以非事务方式执行。
static int TIMEOUT_DEFAULT 使用基础事务系统的默认超时,如果不支持超时,则为none。

重点传播行为:PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED

b) TransactionDefinition常用的方法:

返回值类型 方法名 描述
int getIsolationLevel() 返回事务隔离级别
java.lang.String getName() 返回此事务的名称。
int getPropagationBehavior() 返回传播行为。
int getTimeout() 返回事务超时。
boolean isReadOnly() 返回是否优化为只读事务。

【TransactionStatus】

TransactionStatus记录着事务进行操作时的状态。

常用方法:

返回值类型 方法名 描述
void flush() 如果适用,将基础会话刷新到数据存储区:例如,所有受影响的Hibernate / JPA会话。
boolean hasSavepoint() 返回此事务是否在内部携带保存点,即基于保存点创建为嵌套事务。
boolean isCompleted() 返回此事务是否已完成,即是否已提交或回滚。
boolean isNewTransaction() 返回当前交易是否是新的; 否则参与现有交易,或者可能不会首先在实际交易中运行。
boolean isRollbackOnly() 返回事务是否已标记为仅回滚(由应用程序或事务基础结构)。
void setRollbackOnly() 设置仅事务回滚。

2.转账环境搭建

a) 创建数据库

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
/*
Navicat Premium Data Transfer
Source Server : localhost
Source Server Type : MySQL
Source Server Version : 50717
Source Host : localhost:3306
Source Schema : bank
Target Server Type : MySQL
Target Server Version : 50717
File Encoding : 65001
Date: 13/02/2019 11:12:46
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for account
-- ----------------------------
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`money` double(255,0) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of account
-- ----------------------------
BEGIN;
INSERT INTO `account` VALUES (1, 'aaa', 1000);
INSERT INTO `account` VALUES (2, 'bbb', 1000);
INSERT INTO `account` VALUES (3, 'ccc', 1000);
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;

b) 创建spring maven工程

目录结构

Account.java

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
package com.danyuantech.transaction.bean;
public class Account {
private int id;
private String name;
private double money;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}

AccountDao.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.danyuantech.transaction.dao;
import org.springframework.stereotype.Repository;
@Repository
public interface AccountDao {
/**
* 转入
* @param in 转入账户
* @param money 金额
*/
void inMoney(String in, double money);
/**
* 转成
* @param out 转成账户
* @param money 金额
*/
void outMoney(String out, double money);
}

AccountDaoImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.danyuantech.transaction.dao.impl;
import com.danyuantech.transaction.dao.AccountDao;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
@Override
public void inMoney(String in, double money) {
String sql = "update account set money = money + ? where name = ?";
this.getJdbcTemplate().update(sql, money, in);
}
@Override
public void outMoney(String out, double money) {
String sql = "update account set money = money - ? where name = ?";
this.getJdbcTemplate().update(sql, money, out);
}
}

AccountService.java

1
2
3
4
5
6
7
8
9
10
11
package com.danyuantech.transaction.service;
public interface AccountService {
/**
* 转账
* @param out 转出账户
* @param in 转入账户
* @param money 金额
*/
void transfer(String out, String in, double money);
}

AccountServiceImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.danyuantech.transaction.service.impl;
import com.danyuantech.transaction.dao.AccountDao;
import com.danyuantech.transaction.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
@Qualifier("accountDao")
private AccountDao accountDao;
@Override
public void transfer(String out, String in, double money) {
accountDao.outMoney(out, money);
// int a = 2 / 0;
accountDao.inMoney(in, money);
}
}

3.Spring事务管理-编程式事务管理

a) 配置事务管理器、模板类、将模板注入Service

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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/bank"/>
<property name="username" value="root"/>
<property name="password" value="1314"/>
</bean>
<!-- 配置DAO -->
<bean id="accountDao" class="com.danyuantech.transaction.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置Service -->
<bean id="accountService" class="com.danyuantech.transaction.service.impl.AccountServiceImpl">
<property name="transactionTemplate" ref="transactionTemplate"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 配置连接池 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务管理模板类 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
</beans>

b) 编程添加事务

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
package com.danyuantech.transaction.service.impl;
import com.danyuantech.transaction.dao.AccountDao;
import com.danyuantech.transaction.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
@Service("accountService")
public class AccountServiceImpl implements AccountService {
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
@Autowired
@Qualifier("accountDao")
private AccountDao accountDao;
@Override
public void transfer(final String out, final String in, final double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
accountDao.outMoney(out, money);
int a = 2 / 0;
accountDao.inMoney(in, money);
}
});
}
}

c) 编写测试类测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import com.danyuantech.transaction.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountTest {
@Resource(name = "accountService")
private AccountService accountService;
@Test
public void accountTest(){
accountService.transfer("aaa", "bbb", 200);
}
}

4.Spring事务管理-声明式事务管理

a) 配置事务管理器、为Service生成代理

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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/bank"/>
<property name="username" value="root"/>
<property name="password" value="1314"/>
</bean>
<!-- 配置DAO -->
<bean id="accountDao" class="com.danyuantech.transaction.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置Service -->
<bean id="accountService" class="com.danyuantech.transaction.service.impl.AccountServiceImpl">
<property name="transactionTemplate" ref="transactionTemplate"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 配置连接池 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--为Service生成代理-->
<bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 配置目标对象 -->
<property name="target" ref="accountService"/>
<!-- 配置事务管理器 -->
<property name="transactionManager" ref="transactionManager"/>
<!-- 配置事务属性 -->
<property name="transactionAttributes">
<props>
<!-- prop 格式:propagation,isolation,readOnly,-Excption,+Exception -->
<!-- 至少配置一个传播行为 -->
<prop key="transfer">PROPAGATION_REQUIRED</prop>
<prop key="save*"/>
<prop key="update*"/>
<prop key="delete*"/>
</props>
</property>
</bean>
</beans>

b) 编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import com.danyuantech.transaction.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountTest {
// 需要注入代理对象
@Resource(name = "accountServiceProxy")
private AccountService accountService;
@Test
public void accountTest(){
accountService.transfer("aaa", "bbb", 200);
}
}

5.Spring事务管理-使用tx和aop声明式事务管理

a) .pom文件中引入tx及aop依赖包

1
2
3
4
5
6
7
8
9
10
11
12
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>

b) 配置事务管理器、AOP

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
<?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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/bank"/>
<property name="username" value="root"/>
<property name="password" value="1314"/>
</bean>
<!-- 配置DAO -->
<bean id="accountDao" class="com.danyuantech.transaction.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置Service -->
<bean id="accountService" class="com.danyuantech.transaction.service.impl.AccountServiceImpl">
<property name="transactionTemplate" ref="transactionTemplate"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 配置连接池 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务增强 -->
<tx:advice id="tx" transaction-manager="transactionManager">
<tx:attributes>
<!--
propagation: 传播行为 REQUIRED
isolation: 隔离级别 DEFAULT
read-only: 是否只读 true false
rollback-for: 发生哪些异常回滚
no-rollback-for:发生哪些异常不回滚
timeout: 超时
-->
<tx:method name="transfer" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置切面 -->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.danyuantech.transaction.service.AccountService+.*(..))"/>
<aop:advisor advice-ref="tx" pointcut-ref="pointcut"/>
</aop:config>
</beans>

c) 编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import com.danyuantech.transaction.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountTest {
@Resource(name = "accountService")
private AccountService accountService;
@Test
public void accountTest(){
accountService.transfer("aaa", "bbb", 200);
}
}

5.Spring事务管理-基于注解的声明式事务管理

a) 配置事务管理器、开启事务注解

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
<?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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/bank"/>
<property name="username" value="root"/>
<property name="password" value="1314"/>
</bean>
<!-- 配置DAO -->
<bean id="accountDao" class="com.danyuantech.transaction.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置Service -->
<bean id="accountService" class="com.danyuantech.transaction.service.impl.AccountServiceImpl">
<property name="transactionTemplate" ref="transactionTemplate"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 配置连接池 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

b) 在需要进行事务管理的类上添加注解@Transactional

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
package com.danyuantech.transaction.service.impl;
import com.danyuantech.transaction.dao.AccountDao;
import com.danyuantech.transaction.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
@Transactional
@Service("accountService")
public class AccountServiceImpl implements AccountService {
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
@Autowired
@Qualifier("accountDao")
private AccountDao accountDao;
@Override
public void transfer(final String out, final String in, final double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
accountDao.outMoney(out, money);
int a = 2 / 0;
accountDao.inMoney(in, money);
}
});
}
}

c) 编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import com.danyuantech.transaction.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountTest {
@Resource(name = "accountService")
private AccountService accountService;
@Test
public void accountTest(){
accountService.transfer("aaa", "bbb", 200);
}
}
如果帮到了你,想打赏支持,喏~