@Transactional原理
在讲解Transactional原理前,先看下spring对事务的管理都有哪些。
事务管理
spring支持编程式事务管理和声明式事务管理两种方式。
编程式事务管理
编程式事务使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。
对于编程式事务管理,spring推荐使用TransactionTemplate。
声明式事务
声明式事务是建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,它的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。
声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。
Transactional原理
再分析源码前,现从理论上大概分析下:
纯JDBC操作数据库的基本步骤:
- 获取连接 Connection conn = DriverManager.getConnection()
- 开启事务conn.setAutoCommit(true/false);
- 执行CRUD
- 提交事务/回滚事务 conn.commit() / conn.rollback();
- 关闭连接 conn.close();
spring开启事务
spring开启事务主要有两种方式
spring方式
开启事务注解标记@Transactional
Spring在jdbc中提供了一个事务管理组件DataSourceTransactionManager
<!-- 配置事务管理组件 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource”>
</bean>
<!-- 开启事务注解标记@Transactional -->
<tx:annotation-driven transaction-manager=“txManager" />
配置上面的信息后,Spring在初始化包含Transactional注解的类时,会自动生成这些类的代理,并放置再容器中,以便备用。
spring boot
spring boot中打开事务的几种方式:
1、自动装载
spring-boot-autoconfigure jar中 spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
@TransactionAutoConfiguration->EnableTransactionManagement
2、手功启动事务管理 @EnableTransactionManagemen
@EnableTransactionManagemen ->TransactionManagementConfigurationSelector->ProxyTransactionManagementConfiguration->TransactionInterceptor
具体实现:
::Transactional 实现事务管理是通过TransactionInterceptor拦截器工作的。::
主要关注两点:
::Spring 会调用TransactionInterceptor在目标方法执行前后进行拦截::
::获取数据库连接是通过当前线程获取的,同一线程获取的连接是同一个::
生成TransactionInterceptor切面类
我们以spring开启事务方式为例,
spring tx的入口就是在TxAdviceBeanDefinitionParser这里将解析tx的配置,生成TransactionInterceptor对象,这个也就是一个普通的切面类,只要符合AOP规则的调用都会进入此切面。
解析<tx:annotation-driven/>
配置
public class TxNamespaceHandler extends NamespaceHandlerSupport {
static final String /TRANSACTION_MANAGER_ATTRIBUTE/= "transaction-manager";
static final String /DEFAULT_TRANSACTION_MANAGER_BEAN_NAME/= "transactionManager";
@Override
public void init() {
registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
//对<tx:annotation-driven/>标签的解析
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
}
}
AnnotationDrivenBeanDefinitionParser类中会创建并注册以下三个类:
- TransactionInterceptor:MethodInterceptor-Advice
- BeanFactoryTransactionAttributeSourceAdvisor: PointcutAdvisor
- AnnotationTransactionAttributeSource: Pointcut
并且组装了这三个类的关系,关系如下:
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(new AnnotationTransactionAttributeSource());
advisor.setAdvice(new TransactionInterceptor());
关于PointcutAdvisor与MethodInterceptor的使用方式,参考:spring aop使用 - 简书
具体实现:
BeanFactoryTransactionAttributeSourceAdvisor:实现了PointcutAdvisor接口的getPointcut()方法
TransactionInterceptor:实现了MethodInterceptor
AnnotationTransactionAttributeSource:通过调用getTransactionAttribute方法判断是否有@Transactional
TransactionAttributeSourcePointcut :实现MethodMatcher接口的matches方法,用于判断方法是否有@Transactional,内部使用了AnnotationTransactionAttributeSource
Spring AOP容器为使用@Transactional注解的类创建代理,在执行代理类的目标方法时,会调用Advisor的getAdvice获取MethodInterceptor并执行其invoke方法,BeanFactoryTransactionAttributeSourceAdvisor的getAdvice方法会返回TransactionInterceptor,它实现了MethodInterceptor。
TransactionInterceptor类中invoke方法为:
@Override
@Nullable
public Object invoke(final MethodInvocation invocation) throws Throwable {
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
最终会调用TransactionAspectSupport中的invokeWithinTransaction方法:
@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 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;
}
}
从上可以看出,在invokeWithinTransaction方法中有完整的事务管理。
获取数据库连接
接着我们看下如何开创建事务,进入createTransactionIfNecessary方法
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable 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);
}
getTransaction方法的实现由DataSourceTransactionManager类提供,
DataSourceTransactionManager调用父类AbstractPlatformTransactionManager的实现,如下:
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction();
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}
doGetTransaction为DataSourceTransactionManager中方法,用于获取数据库连接。
org.springframework.jdbc.datasource.DataSourceTransactionManager
@Override
protected Object doGetTransaction() {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager./getResource/(obtainDataSource());
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
TransactionSynchronizationManager中getResource方法:
resources中存的就是当前线程池与当前线程中的一个连接,以后,这个线程中获取连接时都会从获取同一个连接。
::注意:resources是用ThreadLocal保存的,这也就说明了为什么开户新的线程会获取一个新的连接,也有跟之前事务没有关系了::
public abstract class TransactionSynchronizationManager {
//resources中存的就是当前线程池与当前线程中的一个连接,以后,这个线程中获取连接时都会从获取同一个连接。
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<Map<Object, Object>>("Transactional resources");
/**
* Retrieve a resource for the given key that is bound to the current thread.
* @param key the key to check (usually the resource factory)
* @return a value bound to the current thread (usually the active
* resource object), or {@code null} if none
* @see ResourceTransactionManager#getResourceFactory()
*/
public static Object getResource(Object key) {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Object value = doGetResource(actualKey);
return value;
}
/**
* Actually check the value of the resource that is bound for the given key.
*/
private static Object doGetResource(Object actualKey) {
Map<Object, Object> map = resources.get();
if (map == null) {
return null;
}
Object value = map.get(actualKey);
// Transparently remove ResourceHolder that was marked as void...
if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
map.remove(actualKey);
// Remove entire ThreadLocal if empty...
if (map.isEmpty()) {
resources.remove();
}
value = null;
}
return value;
}
}
DataSourceUtils
别外也可通过DataSourceUtils类直接获取连接:
public abstract class DataSourceUtils {
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
try {
return doGetConnection(dataSource);
}
catch (SQLException ex) {
throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
}
}
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
logger.debug("Fetching resumed JDBC Connection from DataSource");
conHolder.setConnection(dataSource.getConnection());
}
return conHolder.getConnection();
}
Connection con = dataSource.getConnection();
......
return con;
}
}
参考:
Spring事务实现原理详解 - 爱宝贝丶的个人空间 - 开源中国
Spring AOP和事务的相关陷阱 - lanhz - 博客园
Spring事务源码阅读笔记 - 活在夢裡 - 博客园