一、使用
spring事务开启和使用比较简单,需要有数据源和事务管理器,然后在启动门面类上开启事务,在需要使用事务的地方添加注解就可以了,我们简单做一下回顾。
1.配置数据源
代码语言:javascript复制spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://host:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=true
spring.datasource.username=aaa
spring.datasource.password=bbb
2.开启事务能力
代码语言:javascript复制@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class},scanBasePackages = "xxx")
@EnableTransactionManagement
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3.使用事务
代码语言:javascript复制@Transactional
public void evaluateSatisfaction(Req req) {
//do business
}
这样我们就可以在业务中使用事务了。
那么我们就要思考一个问题,为什么通过简单的配置和两个注解我们就能便捷的使用事务了,spring框架层面做了哪些能力支撑,接下来我们将从原理和源码维度对spring的事务原理进行分析。
二、原理分析
首先,spring事务管理之所以能生效,得有数据库吧,得有数据源吧,得有事务管理器吧,我们先看一下spring对于数据源和事务管理器的处理和配置。
1.数据源与事务管理器
先看一个配置类DataSourceConfiguration:
代码语言:javascript复制abstract class DataSourceConfiguration {
/**
* Tomcat/Hikari/DBCP DataSource configuration.
*/
/**
* Generic DataSource configuration.
*/
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type")
static class Generic {
@Bean
public DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}
}
省略掉的是Tomcat、Hikari、DBCP数据源配置,国内使用率最高的Druid数据源通过spring.datasource.type指定类型并且自定义配置,然后使用DataSourceProperties和DataSourceBuilder构造。
然后我们看另外一个配置类DataSourceAutoConfiguration:
代码语言:javascript复制@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
//...省略
@Configuration
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
DataSourceJmxConfiguration.class })
protected static class PooledDataSourceConfiguration {
}
}
此处PooledDataSourceConfiguration通过@Import注解导入了支持的数据源配置,应用启动时ConfigurationClassPostProcessor会将@Configuration扫描并根据配置数据源类型注册BeanDefinition供后续实例化,这里我们暂且理解为DataSource已经配置好了。
再看另外一个配置类DataSourceTransactionManagerAutoConfiguration:
代码语言:javascript复制@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;
}
}
}
该类是数据源事务管理配置类,根据前边的数据源创建事务管理器DataSourceTransactionManager,从继承关系可以看出它是一个PlatformTransactionManager(后边会用到此概念):
spring的事务管理也就是使用事务管理器通过数据源和连接来实现开启、提交和回滚操作。
2.开启事务能力
通过@EnableTransactionManagement来开启spring事务能力:
代码语言:javascript复制@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;
}
注解有三个属性
- proxyTargetClass:true表示基于CGLIB创建代理,false表示使用jdk动态代理。默认值为false。仅当mode()设置为AdviceMode时适用。
- mode:表示事务通知实现方式,proxy表示通过代理的当时拦截处理事务,同类级别的方法调用将无法拦截;ASPECTJ表示使用aspectj织入的方式拦截处理事务,功能比proxy模式强大。
- order:在调用链中有多个通知,事务通知的执行优先级,默认最低。
@EnableTransactionManagement开启的能力委托给TransactionManagementConfigurationSelector来实现:
代码语言:javascript复制public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {determineTransactionAspectClass()};
default:
return null;
}
}
private String determineTransactionAspectClass() {
return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
}
}
它是一个ImportSelector,ConfigurationClassPostProcessor会将其初始化并将selectImports返回的类列注册BeanDefinition然后实例化(可参考《ConfigurationClassPostProcessor原理详解》),父类AdviceModeImportSelector的selectImports方法解析EnableTransactionManagement注解属性,然后调用子类selectImports方法返回导入类列表,我们的分析针对mode=proxy展开,可以看到TransactionManagementConfigurationSelector导入了两个类,AutoProxyRegistrar和ProxyTransactionManagementConfiguration,逐个分析一下,先看AutoProxyRegistrar。
它是一个ImportBeanDefinitionRegistrar,实例化后被调用其registerBeanDefinitions方法:
代码语言:javascript复制@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) {
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean) proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
}
会调用AopConfigUtils注册一个类InfrastructureAdvisorAutoProxyCreator,并且需要注意的是,如果有多次调用,只要有任一次proxyTargetClass为true,就会把其该属性设置为true,影响是全局,比如应用中同时开启了事务和异步能力,如果@EnableAsync把proxyTargetClass设置为true那么也会影响到事务以及其他aop能力。
代码语言:javascript复制@Nullable
public static BeanDefinition registerAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}
这个类非常重要,是实现代理和AOP能力的核心组件,拥有处理代理逻辑的能力,本质上又是一个BeanPostProcessor,他的实现比较简单,核心逻辑在其父类AbstractAdvisorAutoProxyCreator和AbstractAutoProxyCreator中,父类实现了InstantiationAwareBeanPostProcessor接口,其postProcessBeforeInstantiation方法会在bean初始化之前调用,如果返回不为null,那么就不在执行bean的初始化,框架设计的目的就是给出扩展能力来做一些代理和bean实例化短路的事情。从spring事务场景来说,就是对使用了事务的类做代理实现,加入事务能力。 那么我们就看一下AbstractAutoProxyCreator的postProcessBeforeInstantiation实现:
代码语言:javascript复制@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
Object cacheKey = getCacheKey(beanClass, beanName);
//...省略
// Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
return null;
}
先创建TargetSource,然后获取适用于当前bean的通知,然后创建代理并返回。因为TargetSource是spring框架的复杂代理,针对代理只能单个类代理做出的扩展,我们暂时没有用到该能力,所以此处targetSource为null,这里不会执行创建代理操作。从AbstractAutoProxyCreator中看到重写BeanPostProcessor的一个方法postProcessAfterInitialization,这个方法的作用是目标bean实例化之后,初始化的时候可以对齐进行包装甚至替换掉,对于事务来说,可以用把事务能力包装进去,或者用具有事务能力的bean实例替换掉原来的,看一下实现:
代码语言:javascript复制@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
如果给定的bean有资格被代理,那么调用wrapIfNecessary包装并返回:
代码语言:javascript复制protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//...省略
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
获取bean使用的通知,然后创建代理,并将通知能力织入进去。因为获取适用当前bean的通知涉及到前边我们说的selectImports导入的另外一个配置类ProxyTransactionManagementConfiguration,所以我们这里只分析创建代理,获取通知和通知拦截逻辑后边分析。先看一下创建代理逻辑:
代码语言:javascript复制protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(getProxyClassLoader());
}
先创建代理工厂,然后填充通知器、设置要代理的目标类,然后创建代理。
从代理工厂的继承关系可以看到其实现了Advised接口,并且拥有创建代理的能力,创建代理工厂的时候使用默认无参构造器,会调用父类ProxyCreatorSupport的无参构造器:
代码语言:javascript复制public ProxyCreatorSupport() {
this.aopProxyFactory = new DefaultAopProxyFactory();
}
创建代理的任务会委托给DefaultAopProxyFactory执行,会先调用createAopProxy创建生成代理的工具:
代码语言:javascript复制@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
根据proxyTargetClass和接口特性决定使用jdk的动态代理JdkDynamicAopProxy还是cglib的动态代理ObjenesisCglibAopProxy。 两者都实现了AopProxy接口用于创建代理,jdk动态代理也实现了InvocationHandler接口,将其本身也定义成一个代理执行器,代理目标类逻辑执行的时候对调用其invoke方法。jdk动态代理的生成逻辑如下:
代码语言:javascript复制@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
而cglib则根据目标类生成增强类,并嵌入拦截逻辑,本篇可以理解为把事务能力嵌入:
代码语言:javascript复制@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
try {
Class<?> rootClass = this.advised.getTargetClass();
Class<?> proxySuperClass = rootClass;
if (ClassUtils.isCglibProxyClass(rootClass)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// Configure CGLIB Enhancer...
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x ) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of " this.advised.getTargetClass()
": Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Throwable ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
两种代理方式的逻辑执行此处不做分析,后边执行事务能力的时候分析。 前边有提到生成代理的时候寻找合适的通知会用到另外一个配置类ProxyTransactionManagementConfiguration,我们也分析一下它做了什么事情。
代码语言:javascript复制@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());
if (this.enableTx != null) {
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;
}
}
它定义了事务属性、事务拦截器和事务通知器等三个bean,并且将事务管理器注入了进来(也可能此刻没有注入),这里重要的是
BeanFactoryTransactionAttributeSourceAdvisor和TransactionInterceptor。
它是一个Advisor,持有切入点(TransactionAttributeSourcePointcut)、事务属性和拦截器。拦截逻辑由TransactionInterceptor实现。
结合前边生成代理的逻辑,我们可以初步猜测,根据通知器中的切入点是否匹配拦截类的方法,如果匹配则将拦截器逻辑嵌入到生成的代理类中。 我们看一下前边提到的创建代理之前,有调用getAdvicesAndAdvisorsForBean方法获取目标bean适用的通知,会调用子类AbstractAdvisorAutoProxyCreator的实现:
代码语言:javascript复制@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
调用链比较长,省略中间步骤,最终调用到AopUtils的canApply方法来做过滤:
代码语言:javascript复制public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
//...省略
Set<Class<?>> classes = new LinkedHashSet<>();
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
if (introductionAwareMethodMatcher != null ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
由于我们的通知器不是Introduction类型,然后会使用BeanFactoryTransactionAttributeSourceAdvisor持有的TransactionAttributeSourcePointcut来做过滤:
代码语言:javascript复制@Override
public boolean matches(Method method, Class<?> targetClass) {
if (TransactionalProxy.class.isAssignableFrom(targetClass) ||
PlatformTransactionManager.class.isAssignableFrom(targetClass) ||
PersistenceExceptionTranslator.class.isAssignableFrom(targetClass)) {
return false;
}
TransactionAttributeSource tas = getTransactionAttributeSource();
return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}
然后委托给TransactionAttributeSource来提取目标方法和类是否具有事务标注,如果有@Transactional就认为适用,其实就是用通知器的Pointcut来和方法对比是否匹配适用。上述方法会调用到SpringTransactionAnnotationParser的方法:
代码语言:javascript复制@Override
@Nullable
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
element, Transactional.class, false, false);
if (attributes != null) {
return parseTransactionAnnotation(attributes);
}
else {
return null;
}
}
其实就是为了提取方法上是否有Transactional注解。 先看一下寻找合适通知器的时序图:
InfrastructureAdvisorAutoProxyCreator处理器注册与实例化:
为使用事务的目标类生成代理的时序图如下:
3.执行事务逻辑
根据前边的分析,基于@EnableTransactionManagement和@Transactional注解,应用启动后会将目标类生成代理,并将事务能力织入进去,生成代理的方式有两种,分别是jdk动态代理和cglib动态代理,由于编写事务逻辑的方法所属类大多不实现接口,所以对于事务,生成代理的方式是cglib。
cglib生成代理类的方式是继承目标类,重写目标方法,然后把拦截器逻辑嵌入进去,对于事务能力,会调用到TransactionInterceptor拦截器的invoke方法:
代码语言:javascript复制@Override
@Nullable
public Object invoke(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, invocation::proceed);
}
寻找到目标类,然后执行事务调用invokeWithinTransaction:
代码语言:javascript复制@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;
}
else {
//...省略
}
}
前边有说到,创建的事务管理器DataSourceTransactionManager是PlatformTransactionManager类型,txAttr是从方法上提取的事务注解属性,所以满足if分支,逻辑大概意思是如果需要,则创建事务,然后执行代理类的目标方法逻辑调用,然后如果有异常则做异常回滚并抛出,如果没有异常则提交事务。 先看一下创建事务createTransactionIfNecessary
代码语言:javascript复制protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
//...省略
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
status = tm.getTransaction(txAttr);
}
}
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
从事务管理器中获取事务状态,然后准备事务信息,获取事务状态使用DataSourceTransactionManager的父类重写方法getTransaction:
代码语言:javascript复制@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction();
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(definition, transaction, debugEnabled);
}
// Check definition settings for new transaction.
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
// No existing transaction found -> check propagation behavior to find out how to proceed.
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" definition.getName() "]: " definition);
}
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}
先检查当前线程调用是否处于事务中,如果是则处理线程传播并返回;然后检查事务超时属性,如果事务传播配置是PROPAGATION_MANDATORY且当前无事务,则报错返回;如果事务传播级别是PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW或PROPAGATION_NESTED则开启事务并返回事务状态。补充一下事务传播级别的概念:
PROPAGATION_REQUIRED:支持当前事务;如果不存在,则创建一个新的。默认
PROPAGATION_SUPPORTS:支持当前事务;如果不存在,以非事务方式执行
PROPAGATION_MANDATORY:支持当前事务;如果当前不存在事务,则异常
PROPAGATION_REQUIRES_NEW:创建一个新事务,如果当前事务存在,挂起当前事务 PROPAGATION_NOT_SUPPORTED:不支持当前事务;始终以非事务方式执行
PROPAGATION_NEVER:不支持事务;如果当前事务存在,则异常
PROPAGATION_NESTED:如果存在当前事务,在嵌套事务中执行
我们看一下开启事务的实现:
代码语言:javascript复制@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = obtainDataSource().getConnection();
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
con.setAutoCommit(false);
}
prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
如果没有数据库连接则先获取连接,设置隔离级别,然后将自动提交设置false,配置超时时间,然后把数据库连接绑定到线程上。
回到事务拦截器的invokeWithinTransaction方法,拿到事务信息后,执行业务逻辑,如果发生异常则调用执行回滚和后续逻辑,如果执行成功则提交事务。
看一下异常执行逻辑:
代码语言:javascript复制protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
throw ex2;
}
}
else {
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
throw ex2;
}
}
}
}
如果存在事务,且异常类型符合回滚规则,那么调用事务管理器的回滚逻辑,否则执行提交。回滚最终会调用数据库连接的回滚方法:
代码语言:javascript复制protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
try {
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
然后会调用后续的一些操作,比如之前我们分析过的《TransactionalEventListener使用场景与原理分析》。如果业务执行成功,那么执行事务提交,事务提交由事务管理器最终委托给数据库连接操作,并且也会触发一些依赖事务执行状态的操作,比如TransactionalEventListener依赖的是事务提交。 到这里事务的执行逻辑分析完了,我们看一下执行时序图:
总结
spring事务能力的支撑用到了很多知识,动态代理、AOP、反射、后置处理器等等,总的来说就是应用启动时为需要使用事务的类生成代理类,以及将事务能力(拦截逻辑)织入进去,在实例化的时候调用后置处理器的逻辑,将代理类实例化替代目标类,并放入上下文容器中,在实际调用目标类事务方法的时候,被代理类中ReflectiveMethodInvocation拦截,然后先调用拦截器中的事务逻辑,然后再调用目标类的业务逻辑,最后处理异常回滚和提交,看起来比较简单,但是框架层面提供了非常庞大的基础组件来支撑和实现事务能力,当然这些基础组件大部分都会复用,比如AOP和动态代理,在异步和缓存场景下都会用到,包括我们自己扩展一些能力出来的时候也会用到。