
Spring 提供了基于注解的事务配置,即对需要事务增强的 Bean 接口 、 实现类或者方法进行标注@Transactional,然后在容器中配置基于注解的事务增强驱动,即可使用基于注解的声明式事务 。
1 配置事务示例
我们使用 @Transactional 来为业务类配置事务:
@Service
@Transactional
public class UserService {
@Autowired
private UserDao userDao;
/**
* 新增
*
* @param user
*/
public int addUser(final User user) {
return userDao.save(user);
}
/**
* 依据 Id,获取账号
*
* @param userId
* @return
*/
public User getUser(Long userId) {
return userDao.get(userId);
}
/**
* 更新账号所对应的密码
* @param userId
* @param pwd
*/
public int update(Long userId, String pwd) {
return userDao.update(userId, pwd);
}
}
接着在 Spring 配置文件中, 告知 Spring 容器对标注了 @Transactional 注解的 Bean,织入事务管理切面:
<!-- 扫描带 @Transactional 注解的 Bean,织入事务管理切面-->
<tx:annotation-driven transaction-manager="transactionManager"/>
在默认情况下, <tx:annotation-driven>
会自动使用名为 transactionManager 的事务管理器, 所以,如果我们的事务管理器就叫做 transactionManager ,那么就可以进一步简化为
<tx:annotation-driven/>
<tx:annotation-driven>
拥有以下属性:
属性 | 默认值 | 说明 |
---|---|---|
transaction-manager | transactionManager | 事务管理器 Bean ID |
proxy-target-class | false | true 表示将通过创建子类来代理业务类(需要在类路径中添加 CGlib.jar 类库); false 表示使用基于接口来代理 。 |
order | - | 如果业务类除了需要事务切面之外,还需要织入其他切面,那么可以通过该属性,来控制事务切面在目标连接点中的织入顺序。 |
单元测试:
public class UserServiceTest {
ApplicationContext context;
@BeforeMethod
public void setUp() throws Exception {
context = new ClassPathXmlApplicationContext("spring_anno.xml");
}
@Test
public void testAddUser() throws Exception {
UserService userService = (UserService) context.getBean("userService");
final User user = new User("deniro");
userService.addUser(user);
}
}
运行日志:

从日志中可以看出,Spring 容器为这个类的所有方法,都织入了事务管理功能。
2 @Transactional 属性
@Transactional 拥有以下这些属性:
属性 | 默认值 | 说明 |
---|---|---|
propagation | PROPAGATION_REQUIRED | 事务传播行为。可通过org.springframework.transaction.annotation.Propagation 枚举类,来提供合法值,例:@Transactional(propagation=Propagation.SUPPORTS) |
isolation | ISOLATION_DEFAULT | 事务隔离级别。可通过 org.springframework.transaction.annotation.Isolation 枚举类,来提供合法值,例:@Transactional(isolation=Isolation.READ_UNCOMMITTED) |
readOnly | false | 是否可读写事务。例:@Transactional(readOnly=true) |
timeout | 使用底层事务系统的默认值 | 超时时间,单位为秒。例: @Transactional(timeout=3) |
rollbackFor | 回滚所有运行期异常。 | 需要回滚的一组异常类,类型为 Class[], 多个异常类使用逗号分隔。例:@Transactional(rollbackFor={SQLException,class}) 。 |
rollbackForClassName | {} | 需要回滚的一组异常类,类型为 String[]。例:@Transactional(rollbackForClassName={“xxxException”}) |
noRollbackFor | {} | 不需要回滚的一组异常类,类型为 Class<? extends Throwable>[] 。 |
noRolbackForClassName | {} | 不需要回滚的一组异常类,类型为 String[]。 |
3 标注位置
@Transactional 注解可以被标注于接口定义、接口方法 、 类定义和类的 Public 方法上 。
但如果 @Transactional 注解被标注在业务接口上,那么如果启用了子类代理:
<tx:annotation-driven proxy-target="true"/>
那么被代理的业务类并不会织入事务增强,仍然工作在非事务环境下。这显然不是我们想看到的。
建议在具体业务类上使用 @Transactional 注解,这样不管是否开启子类代理模式,业务类都会织入事务增强。
也可以在直接在方法上定义注解。
方法上定义的注解会覆盖类定义的注解,比如有些方法需要使用到特殊的事务属性,那么就可以直接在方法上定义注解。
在以下示例中,我们在 getUser() 方法上设置了只读事务属性:
@Service
@Transactional
public class UserService {
@Autowired
private UserDao userDao;
/**
* 依据 Id,获取账号
*
* @param userId
* @return
*/
@Transactional(readOnly = true)
public User getUser(Long userId) {
return userDao.get(userId);
}
...
}
单元测试:
@Test
public void testGetUser() throws Exception {
UserService userService = (UserService) context.getBean("userService");
User user = userService.getUser(1l);
logger.info("user={}", user);
}
控制台输出结果:

从输出结果中我们可以看出,在调用该方法时,事务加入了只读属性。