一、创建代理对象
spring常用的事务有xml和注解两种方式,两种流程大体相同,只是创建代理对象细节略有区别,本文以注解方式进行讲解,即我们工作中最常用的@Transactional,重点讲解执行目标方法,代理对象的创建略过。
代码展示:
- 配置类:TxConfig
- service:AccountService
- dao:AccountDao
- 测试类:TestTx
- 配置文件:db.properties
jdbc.url=jdbc:mysql://127.0.0.1:13306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8
jdbc.username=root
jdbc.password=123456
jdbc.driver-class-name=com.mysql.cj.jdbc.Driver
- maven坐标:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
@EnableTransactionManagement
@Configuration
@PropertySource("classpath:db.properties")
public class TxConfig {
@Value("${jdbc.url}")
private String username;
@Value("${jdbc.username}")
private String password;
@Value("${jdbc.password}")
private String jdbcUrl;
@Value("${jdbc.driver-class-name}")
private String driverClass;
@Bean
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(username);
dataSource.setUsername(password);
dataSource.setPassword(jdbcUrl);
dataSource.setDriverClassName(driverClass);
return dataSource;
}
@Bean
public TransactionManager transactionManager(DataSource dataSource) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Bean
public AccountDao accountDao() {
AccountDao accountDao = new AccountDao();
accountDao.setDataSource(dataSource());
return accountDao;
}
@Bean
public AccountService accountService() {
AccountService accountService = new AccountService();
accountService.setAccountDao(accountDao());
return accountService;
}
}
public class AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Transactional()
public void trans(){
accountDao.transOut(1L, 100);
accountDao.transIn(2L, 100);
System.out.println("转账成功");
}
}
public class AccountDao{
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource ds){
this.jdbcTemplate = new JdbcTemplate(ds);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void transOut(Long outId, int money) {
String sql = "UPDATE account SET balance = balance - ? WHERE id = ?";
jdbcTemplate.update(sql, money, outId);
}
@Transactional()
public void transIn(Long inId, int money) {
String sql = "UPDATE account SET balance = balance + ? WHERE id = ?";
jdbcTemplate.update(sql, money, inId);
int i = 10 / 0;
}
}
public class TestTx {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(TxConfig.class);
context.refresh();
AccountService accountService = context.getBean(AccountService.class);
accountService.trans();
}
}
- 数据库:
二、执行目标方法
可以看到accountService为cglib为我们创建的代理对象,可以理解为执行目标方法之前,会被一系列拦截器进行拦截,其中第一个拦截器CglibAopProxy$DynamicAdvisedInterceptor的intercept方法是我们重点关注的。
//这个方法拿到拦截器链,这里拿到了TransactionInterceptor
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
//执行拦截器链
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
//执行目标方法
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
//执行拦截器
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
TransactionInterceptor核心处理逻辑
核心对象
- TransactionInfo
- TransactionAttribute
- TransactionStatus
- DataSourceTransactionObject
这几个类的嵌套属性比较深,如图:
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
final TransactionManager tm = determineTransactionManager(txAttr);
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// 创建TransactionInfo对象,如果是第一次创建,newTransaction = true、newSynchronization = true,
// 其中newTransaction关系到了事务的提交和回滚,是比较重要的标识
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// 执行目标方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 发生异常是进行回滚
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
//切换线程的TransactionInfo对象
cleanupTransactionInfo(txInfo);
}
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
// 提交事务、清空一系列的ThreadLocal对象,释放连接等
commitTransactionAfterReturning(txInfo);
return retVal;
}
}
- accountDao.transOut(1L, 100);
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void transOut(Long outId, int money) {
String sql = "UPDATE account SET balance = balance - ? WHERE id = ?";
jdbcTemplate.update(sql, money, outId);
}
因为accountDao的transOut方法被注解修饰,且传播特性为REQUIRES_NEW,所以它不会继承AccountService.trans()的事务,而是创建一个新的,并挂起当前线程的ConnectionHolder,把自己创建的ConnectionHolder存入当前线程,等自己提交事务后,再把之前挂起的ConnectionHolder进行恢复。
可以看到newTransaction = true、newSynchronization = true,也就是后续的提交和回滚都是独立的,后续的transIn方法不会影响到它,因为它自己会进行提交。
这里会进行事务提交:
数据库已经提交:
- accountDao.transIn(2L, 100);
事务连接拿到的是AccountService.trans()的事务 - 虽然发生了异常,但是transOut事务已经提交,所以这里不会回滚transOut的数据
@Transactional()
public void transIn(Long inId, int money) {
String sql = "UPDATE account SET balance = balance + ? WHERE id = ?";
jdbcTemplate.update(sql, money, inId);
int i = 10 / 0;
}