前言
在Spring中,使用事物的方式基本上都是通过声明@Transactional来完成的。
xml方式
在xml的IOC容器中<tx:annotation-driven/>
开启Spring的声明式事物支持。
Spring事物xml标签的解析由TxNamespaceHandler来处理。
<tx:annotation-driven/>
该标签的解析由AnnotationDrivenBeanDefinitionParser来解析处理。
Spring声明式事物涉及三个核心类:
-
BeanFactoryTransactionAttributeSourceAdvisor:
在bean的初始化中,有一步populateBean
,为该bean设置属性对象。
属性是从哪里赋值的呢?
在AnnotationDrivenBeanDefinitionParser解析该xml标签的时候,通过代码添加的。
/**
* Parses the {@code <tx:annotation-driven/>} tag. Will
* {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator}
* with the container as necessary.
*/
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
registerTransactionalEventListenerFactory(parserContext);
String mode = element.getAttribute("mode");
if ("aspectj".equals(mode)) {
// mode="aspectj"
registerTransactionAspect(element, parserContext);
}
else {
// mode="proxy"
// 默认走这里,为该beanDefinition添加属性值,以便创建该bean的时候
// 其field域的属性能被赋值。
AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
}
return null;
}
在populateBean()
的该对象的属性值的时候,触发AnnotationTransactionAttributeSource对象和TransactionInterceptor的创建。即transactionAttributeSource属性
和adviceBeanName属性
。
-
AnnotationTransactionAttributeSource:
AnnotationTransactionAttributeSource(true)
决定了@Transactional
只能应用在public方法
中。
值为true,代表@Transanctional
只能应用在public方法
中,且是基于AOP代理的
值为false,代表@Transactional
可以应用在protected or private方法
中,且是基于AspectJ代理的
-
TransactionInterceptor:
-
DataSourceTransactionManager:
注解的方式
通过@EnableTransactionManagement
来开启Spring事物的声明式管理,这个注解引入TransactionManagementConfigurationSelector。
/**
* Returns {@link ProxyTransactionManagementConfiguration} or
* {@code AspectJ(Jta)TransactionManagementConfiguration} 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[] {
// 引入SpringAOP处理,即AnnotationAwareAspectJAutoProxyCreator
AutoProxyRegistrar.class.getName(),
// 事物配置类,即开启声明式事物所需的三个核心对象
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {determineTransactionAspectClass()};
default:
return null;
}
}
该注解的解析是由ConfigurationClassPostProcessor来处理成可供IOC容器生成bean的BeanDefinition。【processImports
阶段来处理该ImportSelector】
由AnnotationAwareAspectJAutoProxyCreator这个BeanPostProcessor通过postProcessBeforeInstantiation
来触发Spring声明式事物的三个核心类的Bean对象创建(findAdvisorBeans()
)。在这个过程中,触发对配置类ProxyTransactionManagementConfiguration的创建。
通过ProxyTransactionManagementConfiguration的创建来看ImportAware的作用?
在initializationBean
阶段中交由ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor处理ImportAware接口。有什么作用呢?获取该bean的注解元数据。
// ImportAwareBeanPostProcessor 获取一个bean的注解元数据
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof ImportAware) {
ImportRegistry ir = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);
AnnotationMetadata importingClass = ir.getImportingClassFor(ClassUtils.getUserClass(bean).getName());
if (importingClass != null) {
// 设置注解元数据
((ImportAware) bean).setImportMetadata(importingClass);
}
}
return bean;
}
// 为ProxyTransactionManagementConfiguration设置注解元数据
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableTx = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));
if (this.enableTx == null) {
throw new IllegalArgumentException(
"@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName());
}
}
代理对象
代理对象的生成是在bean初始化的initializationBean阶段
,即advisedBeans缓存,防止重复代理bean对象(用@Aspect
注解的类不会被代理)。
// 在doCreateBean之前,接口代理处理,会给advisedBeans赋值
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
// 这里,在AbstractAutoProxyCreator中,以下情况不能代理
// 情况一:该类角色是基础设施类或者用切面注解,isInfrastructureClass(beanClass)
// 情况二:shouldSkip,是否应该跳过代理
// 是这两种情况,不能AOP代理,用advisedBeans存放判断结果
// this.advisedBeans.put(cacheKey, Boolean.FALSE); return null;
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
生成的代理对象只有一个,对于自定义AOP跟@Transactional同切一个方法,也只对该方法所在类生成一个代理对象,所不同的是自定义的AOP切面对应的是InstantiationModelAwarePointcutAdvisor和BeanFactoryTransactionAttributeSourceAdvisor这两个Advisor进行针对性切面匹配处理。
用@Aspect
进行自定义切面的,防止对方法的重复切逻辑匹配判断处理, 用shadowMatchCache
进行缓存。
用@Transactional
进行事物切面的,防止对方法的重复切逻辑匹配判断处理, 用attributeCache
进行缓存。【因为,匹配的过程,有关类下的所有方法进行匹配,且当执行该事物方法时,会重新再通过attributeCache
获取TransactionAttribute。】
为什么要有这两个缓存?
因为,只要开启Aop和事物,那么在IOC容器初始化的bean的创建中,总会有Bean后置处理器AnnotationAwareAspectJAutoProxyCreator对该bean进行处理。那么,如果该bean需要代理,就会每次都走该处理器的父类AbstractAutoProxyCreator的wrapIfNecessary()
方法。
/**
* Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
* @param bean the raw bean instance
* @param beanName the name of the bean
* @param cacheKey the cache key for metadata access
* @return a proxy wrapping the bean, or the raw bean instance as-is
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 通过缓存的advisedBean先判断是否代理
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 基础设施类或者Aop类以及shouldSkip不代理
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
// 是否有针对该bean代理处理的Advisor
// 这个获取,就是防止重复处理,用缓存进行
// 如果是AOP,用shadowCache;如果是Transaction,用attributeCache缓存
// 该方法对应的TransactionAttribute
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;
}
这一过程发生在findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName)
阶段中。
@Aspect
和@Transactional
都是被AnnotationAwareAspectJAutoProxyCreator来处理的。而@Async
是被AsyncAnnotationBeanPostProcessor所来处理的。
完整分析可以参考IOC容器初始化之AOP内部机制
事物执行
在执行事物方法(被切方法)时,防止重复进入创建方法执行器并处理,用methodCache
方法缓存,缓存该方法的拦截器或者Advise链(是List<Object>
),因为该方法会被不同切面处理,比如这个场景:自定义的AOP切这个方法和用 @Transactional切这个方法。同时,这个场景需要注意,自定义的切面如果跟声明式事物同切一个方法时,一定要注意,自定义切面的通知方法,显示抛出异常,否则,异常被吃掉,导致声明式事物不起作用。
会进入CglibAopProxy的intercept方法
【方法拦截】。
获取到该方法的所有的拦截器Interceptor及Advice。
// 1. 获取AOP中所有的Advisor
// 2. 从所有的Advisor中,筛选出它们的匹配器能匹配到该方法的MethodInterceptor
// 在这个matches的过程中会用到TransactionAttribute,因此是从缓存中获取的。
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
创建CglibMethodInvocation方法执行器进行proceed()
处理。
进入TransactionInterceptor的invoke处理,即在事物的情形下执行:
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
。
1.获取事务属性源TransactionAttributeSource
怎么来的?即是TransactionInterceptor中的属性值。
在创建TransactionInterceptor对象的时候赋值。
=> ProxyTransactionManagementConfiguration
2.从TransactionAttributeSource中获取事务属性TransactionAttribute。
这里是从缓存中获取的,第一次创建是在创建事物方法所在的类的代理对象的初始化bean阶段,
用AnnotationAwareAspectJAutoProxyCreator创建代理对象的时候,将@Transactional
转换成TransactionAttribute。
3.从IOC容器中获取事务管理器TransactionManager【DataSourceTransactionManager】。
4.将事物管理器对象强转及获取事物方法信息(methodJoinPointInfo,以此作为事物名【默认】)。
5.根据事物属性,事务管理器,事物方法信息创建事物【createTransactionIfNecessary】
(这里,注意一下命名,获取的是事物信息TransactionInfo,但是方法命名却是Transaction,
简化方法的命名,不是根据返回值命名,而是根据语义命名。)
5.1 根据事物属性从事务管理器中获取事物【TransactionStatus】
(这里的方法名同上,也是没有命名为getTransactionStatus,而是getTransaction)
5.1.1 真正获取事物(方法命名,doGetTransaction,且这是一个模板方法,实际实现,
交由具体的事务管理器实现,比如DataSourceTransactionManager)
创建一个事物对象,DataSourceTransactionObject
给对象设定相应属性值,比如,嵌套事物,Jdbc连接
Jdbc连接是怎么获取的?
从事物同步管理器中获取:
TransactionSynchronizationManager.getResource(obtainDataSource())
事物同步管理器,通过NameThreadLocal将线程与事物相关信息进行了绑定,通过
ThreadLocal,我们也不难看出,Spring的事物是本地事物。
5.1.2 已经存在事物【场景:嵌套事物】
判断事物存在的方法:
就是判断新建事物(事物对象)中是否含有Jdbc连接(Holder)或者Jdbc连接
(Holder)中的事物活跃
处理存在的事物:handleExistingTransaction,依照事物的传播机制分场景处理
5.1.3 当前不存在事物,依照事物的传播机制分场景处理
不支持这种传播机制:TransactionDefinition.PROPAGATION_MANDATORY
该事物方法定义的传播行为是以下三种:
TransactionDefinition.PROPAGATION_REQUIRED
TransactionDefinition.PROPAGATION_REQUIRES_NEW
TransactionDefinition.PROPAGATION_NESTED
挂起事物:suspend(null)
开始事务:startTransaction
创建事物状态TransactionStatus
实际开始事务:doBegin(方法命名,没有起名doBeginTransaction,
因为语境已是开始事物阶段)
事物对象中不存在连接Holder或者连接没有到事物同步阶段
从数据源中获取连接对象,为事物对象设置连接对象
为事物对象设置事物同步标志true,到事物同步阶段
为事物对象设置隔离级别,只读标识
Jdbc连接自动提交:
设置为非自动提交(因此,Spring事物都是非自动提交方式)
准备事物连接:prepareTransactionalConnection
事物是否只读
为事物对象的连接Holder设置事物活跃标志,true
事物对象是新创建的,绑定连接到当前线程ThreadLocal:
TransactionSynchronizationManager.bindResource(
obtainDataSource(),
txObject.getConnectionHolder());
在整个doBegin阶段中抛异常的话:
事物对象是新创建的:
释放Jdbc连接
为该事物对象的connectionHolder置空
准备同步事物,绑定到线程:prepareSynchronization
开始事物这个阶段抛异常:
恢复挂起的事物资源:
resume(null, suspendedResources);
5.2 准备事物信息:prepareTransactionInfo
将事物信息绑定到当前线程:
原理的事物信息,赋值给oldTransactionInfo
6. 继续处理,多个切点,切的是同一个方法:
invocation.proceedWithInvocation()
最终,会再次进入ReflectiveMethodInvocation的proceed方法
判断,事物方法的拦截器链执行完,执行实际方法invokeJoinPoint(被声明式事物注解的方法)
7. 执行被声明式事物注解的方法
执行jdbc的方法的时候都没去提交。
8. 当执行完清除事物信息的时候cleanupTransactionInfo,提交事物:
commitTransactionAfterReturning(txInfo);
通过事务管理器提交事物状态:commit
是否回滚
处理提交:processCommit
提交前的准备:prepareForCommit
提交前触发回调:triggerBeforeCommit
事物完成前触发回调:triggerBeforeCompletion
根据事物状态属性执行不同动作:
有保存点savePoint,释放
事物状态是一个新事物的状态,实际提交事物状态:doCommit(status)
提交事物之后触发回调:triggerAfterCommit
事物完成后触发回调:triggerAfterCompletion
清除事物同步器
事物完成后清除:cleanupAfterCompletion
事物状态的完成属性置为true
事物同步器清除:TransactionSynchronizationManager.clear();
完成之后实际清除事物:doCleanupAfterCompletion
将数据源与当前线程解绑:
TransactionSynchronizationManager.unbindResource(obtainDataSource());
重置Jdbc连接为自动提交
重置事物之后的连接:
DataSourceUtils.resetConnectionAfterTransaction(
con, txObject.getPreviousIsolationLevel(),
txObject.isReadOnly());
释放连接
清除事物对象中的连接Holder
9.一次事物方法的执行已经完成