根据之前一篇文章的例子,我发现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事务管理器。
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()方法中被执行。
`TransactionInterceptor#invoke`调用的时序图
在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就会根据事务的状态,判断是否要提交当前事务,如果事务完成还需要释放当前事务占用的相关资源。














网友评论