1友存、依賴包
1.1祷膳、 SpringBoot中的依賴包
眾所周知,在SpringBoot中凡是需要跟數(shù)據(jù)庫打交道的屡立,基本上都要顯式或者隱式添加jdbc的依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
1.2直晨、 Spring中的依賴包
在講SpringBoot中的事務(wù)管理之前,先來講下Spring的事務(wù)管理膨俐。在Spring項目中勇皇,加入的是spring-jdbc依賴:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
1.2.1、配置版事務(wù)
在使用配置文件的方式中焚刺,通常會在Spring的配置文件中配置事務(wù)管理器敛摘,并注入數(shù)據(jù)源:
<!-- 注冊數(shù)據(jù)源 -->
<bean id="dataSource" class="...">
<property name="" value=""/>
</bean>
<!-- 注冊事務(wù)管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 開啟事務(wù)注解 -->
<tx:annotation-driven transaction-manager="txManager" />
接下來可以直接在業(yè)務(wù)層Service的方法上或者類上添加@Transactional。
1.2.2乳愉、注解版事務(wù)
首先需要注冊兩個Bean兄淫,分別對應(yīng)上面Spring配置文件中的兩個Bean:
@Configuration
public class TxConfig {
@Bean
public DataSource dataSource() {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("...");
dataSource.setPassword("...");
dataSource.setDriverClass("...");
dataSource.setJdbcUrl("...");
return dataSource;
}
@Bean
public PlatformTransactionManager platformTransactionManager() {
return new DataSourceTransactionManager(dataSource());//放入數(shù)據(jù)源
}
}
PlatformTransactionManager這個Bean非常重要,要使用事務(wù)管理蔓姚,就必須要在IOC容器中注冊一個事務(wù)管理器捕虽,而在使用@Transactional注解的時候,默認會從IOC容器中查找對應(yīng)的事務(wù)管理器坡脐。
一般人都會認為到這已經(jīng)結(jié)束了泄私,可以正常使用@Transactional注解了,正常則commit备闲,異常則rollback晌端。各位可以簡單嘗試在Service層的方法中手動制造異常,看看事務(wù)是否會回滾恬砂?此處留給大家自行嘗試...
其實到這咧纠,事務(wù)在遇到異常后,還是沒有正承褐瑁回滾惧盹,為什么呢乳幸?缺少一個注解@EnableTransactionManagement,該注解的功能是開啟基于注解的事務(wù)管理功能钧椰,需要在配置類上添加該注解即可,這樣@Transactional的事務(wù)提交和回滾就會生效符欠。
@EnableTransactionManagement//重要
@Configuration
public class TxConfig {
@Bean
public DataSource dataSource() {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("...");
dataSource.setPassword("...");
dataSource.setDriverClass("...");
dataSource.setJdbcUrl("...");
return dataSource;
}
//重要
@Bean
public PlatformTransactionManager platformTransactionManager() {
return new DataSourceTransactionManager(dataSource());//放入數(shù)據(jù)源
}
}
2嫡霞、@Transactional原理
這里咱們再回到SpringBoot的事務(wù)管理。在大多數(shù)SpringBoot項目中希柿,簡單地只要在配置類或者主類上添加@EnableTransactionManagement诊沪,并在業(yè)務(wù)層Service上添加@Transactional即可實現(xiàn)事務(wù)的提交和回滾。
因為在依賴jdbc或者jpa之后曾撤,會自動配置TransactionManager端姚。
- 依賴jdbc會自動配置DataSourceTransactionManager:
@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;
}
}
}
- 依賴jpa會自動配置JpaTransactionManager:
@EnableConfigurationProperties(JpaProperties.class)
@Import(DataSourceInitializedPublisher.Registrar.class)
public abstract class JpaBaseConfiguration implements BeanFactoryAware {
//other code...
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
if (this.transactionManagerCustomizers != null) {
this.transactionManagerCustomizers.customize(transactionManager);
}
return transactionManager;
}
}
2.1、@EnableTransactionManagement
具體原理還要從注解@EnableTransactionManagement說起:
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
從@EnableTransactionManagement簽名上可以看到挤悉,它導(dǎo)入了TransactionManagementConfigurationSelector類渐裸,其作用就是利用該類想容器中導(dǎo)入組件。而TransactionManagementConfigurationSelector主要向容器中導(dǎo)入了兩個組件装悲,分別是AutoProxyRegistrar和ProxyTransactionManagementConfiguration:
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
/**
* {@inheritDoc}
* @return {@link ProxyTransactionManagementConfiguration} or
* {@code AspectJTransactionManagementConfiguration} for {@code PROXY} and
* {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()}, respectively
*/
@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;
}
}
}
2.2昏鹃、AutoProxyRegistrar
咱們先來看看AutoProxyRegistrar做了哪些事?
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false;
Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
for (String annoType : annoTypes) {
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
if (candidate == null) {
continue;
}
Object mode = candidate.get("mode");
Object proxyTargetClass = candidate.get("proxyTargetClass");
if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
Boolean.class == proxyTargetClass.getClass()) {
candidateFound = true;
if (mode == AdviceMode.PROXY) {
//注冊自動代理創(chuàng)建器
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean) proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
//other code...
}
}
由于默認情況下mode為AdviceMode.PROXY诀诊,所以會通過AopConfigUtils.registerAutoProxyCreatorIfNecessary方法向容器中注冊自動代理創(chuàng)建器:
//AopConfigUtils工具類
public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
"org.springframework.aop.config.internalAutoProxyCreator";
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAutoProxyCreatorIfNecessary(registry, null);
}
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
可以看到會向容器中注冊了一個名為internalAutoProxyCreator洞渤,類型為InfrastructureAdvisorAutoProxyCreator的組件Bean,而這個類其實同AnnotationAwareAspectJAutoProxyCreator類似属瓣,也是AbstractAdvisorAutoProxyCreator的實現(xiàn)類载迄,而它的頂層接口類其實就是BeanPostProcessor,所以它的邏輯跟AnnotationAwareAspectJAutoProxyCreator大體上一樣:利用后置處理器機制在對象創(chuàng)建之后抡蛙,并包裝成代理對象护昧,在代理對象執(zhí)行目標方法的時候利用攔截器鏈進行攔截,具體過程可以參考AOP原理溜畅。
2.3捏卓、ProxyTransactionManagementConfiguration
另一個組件為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給容器中注入了多個組件Bean,其中:
- transactionAdvisor這個Bean用于創(chuàng)建一個事務(wù)增強器慈格。
- transactionAttributeSource這個Bean主要保存事務(wù)屬性怠晴,其中包括用于解析不同包下的@Transactional注解的解析器:
public AnnotationTransactionAttributeSource() {
this(true);
}
public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
this.publicMethodsOnly = publicMethodsOnly;
this.annotationParsers = new LinkedHashSet<TransactionAnnotationParser>(2);
this.annotationParsers.add(new SpringTransactionAnnotationParser());
if (jta12Present) {
this.annotationParsers.add(new JtaTransactionAnnotationParser());
}
if (ejb3Present) {
this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
}
}
其中包括Spring中的@Transactional注解,同時也包括了Jta和Ejb3中的@Transactional注解解析器浴捆。
- transactionInterceptor這個Bean主要用于創(chuàng)建事務(wù)攔截器蒜田,其中設(shè)置上面所說到的事務(wù)屬性Bean,同時也保存了事務(wù)管理器选泻。TransactionInterceptor其實也是MethodInterceptor的子類冲粤。所以在代理對象執(zhí)行目標方法的時候美莫,方法攔截器就會進行攔截并工作。具體如何攔截工作梯捕,請繼續(xù)往下看invoke方法:
@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();
}
});
}
重點就在invokeWithinTransaction方法:
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
//獲取事務(wù)注解屬性
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
//獲取事務(wù)管理器
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;
}
else {
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, new TransactionCallback<Object>() {
@Override public Object doInTransaction(TransactionStatus status) { TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceedWithInvocation();
} catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
} else {
throw new ThrowableHolderException(ex);
}
}else {
// A normal return value: will lead to a commit.
return new ThrowableHolder(ex);
}
} finally {
cleanupTransactionInfo(txInfo);
} } });
// Check result: It might indicate a Throwable to rethrow.
if (result instanceof ThrowableHolder) {
throw ((ThrowableHolder) result).getThrowable();
}
else {
return result;
}
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
}
}
其實大家看這段源碼就能看到:1厢呵、獲取事務(wù)相關(guān)屬性;2傀顾、獲取事務(wù)管理器襟铭;3、如果需要事務(wù)支持短曾,則創(chuàng)建一個事務(wù)寒砖;4、執(zhí)行目標方法嫉拐;5哩都、如果執(zhí)行正常,則進行事務(wù)提交婉徘;6漠嵌、如果執(zhí)行異常,則進行事務(wù)回滾判哥。