根据之前一篇文章的例子,我发现Spring Boot
是自动完成事务的配置的,所以周末我特意翻了一段源码,探究了一把Spring Boot
是如何完成这个自动配置的过程的。
首先在Spring Boot
的autoconfigure.jar
依赖中的org.springframework.boot.autoconfigure.jdbc
包下有一个DataSourceTransactionManagerAutoConfiguration
类。Spring启动的时候就会根据是否添加了JDBC相关的包来判断是否要加载这个配置类中的配置。
@Configuration
@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceTransactionManagerAutoConfiguration {
@Configuration
@ConditionalOnSingleCandidate(DataSource.class)
static class DataSourceTransactionManagerConfiguration {
private final DataSource dataSource;
private final TransactionManagerCustomizers transactionManagerCustomizers;
DataSourceTransactionManagerConfiguration(DataSource dataSource,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
this.dataSource = dataSource;
this.transactionManagerCustomizers = transactionManagerCustomizers
.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
public DataSourceTransactionManager transactionManager(
DataSourceProperties properties) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(
this.dataSource);
if (this.transactionManagerCustomizers != null) {
this.transactionManagerCustomizers.customize(transactionManager);
}
return transactionManager;
}
}
@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
protected static class TransactionManagementConfiguration {
}
}
以上这个配置类(DataSourceTransactionManagerAutoConfiguration
)中有做了这样两个动作:
- 添加
DataSourceTransactionManager
事务管理器实例 - 触发
@EnableTransactionManagement
注解让当前项目启用事务。
这里先从第二个动作也就是触发@EnableTransactionManagement
注解这个动作开始分析,看看启动的阶段Spring
到底做了些什么。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
从中可以看出导入了另外一个实际的配置类TransactionManagementConfigurationSelector
,这个类中仅有一个方法selectImports()
,它会根据注解上的配置来选择Spring AOP
中的通知配置,默认情况下是选择PROXY
。
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
return null;
}
}
这里我们以ProxyTransactionManagementConfiguration
为例:
@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource());
advisor.setAdvice(transactionInterceptor());
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
return advisor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor() {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource());
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
}
在ProxyTransactionManagementConfiguration
中的三个配置分别定义:
-
transactionAttributeSource()
方法在Spring
容器中产生一个AnnotationTransactionAttributeSource
实例,它能将我们常用的@Transactional
注解转换成在Spring
框架中使用的TransactionAttribute
,如果我们需要在@Transactional
注解上hack一些功能,也可以在这里重新实现TransactionAttribute
类。 -
transactionAdvisor()
方法根据transactionAttributeSource()
定义了AOP的切点,也就是被@Transactional
注解使用的方法。 -
transactionInterceptor()
则定义了拦截器TransactionInterceptor
,被之前定义的切点(也就是transactionAttributeSource()方法中定义的切面)拦截到的方法都会被送到这个类的实例中执行。
到这里为止,事务拦截的部分已经完成了。再回到文章开头提到的另外一个配置:配置DataSourceTransactionManager
事务管理器。
PlatformTransactionManager
是Spring
事务的核心接口,这个接口只包含了以下三个方法:
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
DataSourceTransactionManager
是PlatformTransactionManager
的最基本实现,应用(Application)操作事务的动作都会在这个类中进行。从配置中也不难发现,如果用户自己配置了自定义的事务管理器(比如JPA,JTA等等),Spring
会忽略这个这个transactionManager()
配置方法:
@ConditionalOnMissingBean(PlatformTransactionManager.class)
当了解Spring
配置事务的过程之后,方法调用时事务的执行过程也就变得清晰起来了。
所有需要使用事务的方法(即标记了@Transactional
的方法),都会在TransactionInterceptor
的invoke()
调用,然后在在TransactionAspectSupport
类的invokeWithinTransaction()
方法中被执行。
在invokeWithinTransaction()
方法中事务处理分成两种:
- 标准事务处理
- 异步事务处理
这里我们就分析下标准事务的部分,其核心代码(TransactionAspectSupport#invokeWithinTransaction
前半段)如下:
// 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);
eturn retVal;
}
这个部分中最重要的两个方法就是标记的两处,他们会分别去判断是否创建事务和提交事务。而invocation.proceedWithInvocation()
则会执行应用中常规的方法,如果使用了JDBC的方法,SQL都会根据数据库的事务来处理。
/**
* 判断是否要创建新的事务
*/
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) {
// 通过事务的属性,去上下文获取事务。如果不存在则会创建新的事务
// 详细代码见 org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction
// 并在这个方法中开启事务
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);
}
在AbstractPlatformTransactionManager#getTransaction
中会根据当前上下文的状态调用抽象方法doBegin()
方法,也就是开启事务。
而在invokeWithinTransaction()
方法标记的②处,会判断本次方法执行完是否要提交事务,这个方法中的核心是AbstractPlatformTransactionManager
类的processCommit()
方法:
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
boolean globalRollbackOnly = false;
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
globalRollbackOnly = status.isGlobalRollbackOnly();
}
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
// ③
doCommit(status);
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (globalRollbackOnly) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
// can only be caused by doCommit
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
// can only be caused by doCommit
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, ex);
throw ex;
}
catch (Error err) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, err);
throw err;
}
// Trigger afterCommit callbacks, with an exception thrown there
// propagated to callers but the transaction still considered as committed.
try {
triggerAfterCommit(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
cleanupAfterCompletion(status);
}
}
以上的整个流程中,由@Transaction
注解转换的TransactionAttribute
类以及由TransactionAttribute
产生的TransactionStatus
管理着Spring
事务的状态和流程。当完成当前上下文的事务之后Spring
就会根据事务的状态,判断是否要提交当前事务,如果事务完成还需要释放当前事务占用的相关资源。
网友评论