例子
@Repository
public class TestDao {
public TestDao() {
System.out.println("aaa");
}
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional
public void query() {
jdbcTemplate.execute("insert into test (id) values (1)");
int i = 1 / 0;
jdbcTemplate.execute("delete from test");
}
}
<tx:annotation-driven/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true%26characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
调用TestDao.query()方法,可以发现数据库中并没有插入数据,这是因为我们开启了事务,当1/0抛出异常的时候,事务直接被rollback了。
原理
生成代理对象
xml中通过
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
registerTransactionalEventListenerFactory(parserContext);
String mode = element.getAttribute("mode");
if ("aspectj".equals(mode)) {
// mode="aspectj"
registerTransactionAspect(element, parserContext);
}
else {
// mode="proxy"
AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
}
return null;
}
由于默认情况下mode是proxy,所以会调用AopAutoProxyConfigurer的configureAutoProxyCreator方法进行处理。从名字可以看出,底层的实现是基于aop的。
private static class AopAutoProxyConfigurer {
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;
if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {
Object eleSource = parserContext.extractSource(element);
// Create the TransactionAttributeSource definition.
RootBeanDefinition sourceDef = new RootBeanDefinition(
"org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
sourceDef.setSource(eleSource);
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
// Create the TransactionInterceptor definition.
RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
interceptorDef.setSource(eleSource);
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registerTransactionManager(element, interceptorDef);
interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
// Create the TransactionAttributeSourceAdvisor definition.
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
advisorDef.setSource(eleSource);
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
if (element.hasAttribute("order")) {
advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
}
parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName));
parserContext.registerComponent(compositeDef);
}
}
}
AopAutoProxyConfigurer中定义了aop中需要使用到的Interceptor和Advisor。
**BeanFactoryTransactionAttributeSourceAdvisor**实现了PointcutAdvisor,用于在bean加载的时候对对象做代理操作。看一下该类的定义,
public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
private TransactionAttributeSource transactionAttributeSource;
private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
@Override
protected TransactionAttributeSource getTransactionAttributeSource() {
return transactionAttributeSource;
}
};
会调用AnnotationTransactionAttributeSource的getTransactionAttribute方法获取TransactionAttribute,然后判断获取的TransactionAttribute是否为空,不为空则代表该bean可以被aop代理。而AnnotationTransactionAttributeSource最终是调用SpringTransactionAnnotationParser获取带有@Transactional的方法。
如果bean可以使用事务,接下来会使用**TransactionInterceptor**来生成代理类。
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
});
}
事务的运行
当执行query方法时,会执行invoke方法,然后进入invokeWithinTransaction。
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
首先获取定义好的PlatformTransactionManager,默认情况下会从factory里面获取名字为transactionManager的类,如果不存在则会抛出错误。下一步会根据实际情况决定是否创建新的事务,
protected TransactionInfo createTransactionIfNecessary(
PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {
// If no name specified, apply method identification as transaction name.
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
上面代码中最关键的是tm.getTransaction(),该方法大概可以分为以下的几个步骤:
- 创建一个DataSourceTransactionObject对象,从threadLocal中获取ConnectionHolder放到该对象中
- 如果ConnectionHolder不为空则表明当前线程已经存在了一个事务,则根据事务的不同传播等级进行相应的处理(这里暂时不展开)。
- 如果ConnectionHolder为空,则表示当前线程没有事务,调用doBegin方法开始当前事务
- 如果没有ConnectionHolder或者ConnectionHolder已经被其他事务使用了,则创建新的connection放到ConnectionHolder中。
- 设置connection只读标记以及隔离级别
- 设置connection的autocommit为false,启动事务。
- 如果是只读的,则执行SET TRANSACTION READ ONLY
- 将ConnectionHolder绑定到当前线程
等createTransactionIfNecessary运行结束后,开始执行真正的query方法,query方法。Spring jdbc内部通过DataSourceUtils.doGetConnection获取连接,而该方法则是从threadLocal中获取缓存的ConnectionHolder,ConnectionHolder中的Connection已经是开启事务的了。
如果query方法运行的时候抛出异常,则会调用completeTransactionAfterThrowing处理异常。
protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.hasTransaction()) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
if (txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by rollback error", ex);
throw err;
}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by commit error", ex);
throw err;
}
}
}
}
该方法内部会根据Transactional的配置来决定采取什么措施,默认情况下出现RuntimeException和Error的时候,就会进行回滚操作。
如果query没有抛出异常,则调用commitTransactionAfterReturning提交事务。
到此事务运行的大致流程就介绍完了,其中还有很多细节没有涉及,比如事务的传播等级,事务的回滚细节等等,有时间后面再详细介绍。