Spring框架爲事務管理提供一套統一的抽象,帶來的好處有:
1. 跨不一樣事務API的統一的編程模型,不管你使用的是jdbc、jta、jpa、hibernate。
2. 支持聲明式事務
3. 簡單的事務管理API
4. 能與Spring的數據訪問抽象層完美集成html
說明:Spring的事物管理是用AOP實現的java
此事務與其餘事務的工做隔離的程度。例如,該事務可否看到來自其餘事務的未提交的寫操做
READ_UNCOMMITTED讀未提交
READ_COMMITTED讀提交
REPEATABLE_READ可重複讀
SERIALIZABLE序列化(串行)git
該事務操做是讀、仍是寫、仍是有讀有寫github
對事務執行的時長設置一個閥值,若是超過閥值還未完成則回滾。spring
當一個方法開啓事務後,在方法中調用了其餘的方法,其餘方法可能也須要事務管理,此時就涉及事務該如何傳播了。
4.1. TransactionDefinition.PROPAGATION_REQUIRED:若是當前存在事務,則加入該事務;若是當前沒有事務,則建立一個新的事務。
4.2. TransactionDefinition.PROPAGATION_REQUIRES_NEW:建立一個新的事務,若是當前存在事務,則把當前事務掛起。
4.3. TransactionDefinition.PROPAGATION_SUPPORTS:若是當前存在事務,則加入該事務;若是當前沒有事務,則以非事務的方式繼續運行。
4.4. TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,若是當前存在事務,則把當前事務掛起。
4.5. TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,若是當前存在事務,則拋出異常。
4.6. TransactionDefinition.PROPAGATION_MANDATORY:若是當前存在事務,則加入該事務;若是當前沒有事務,則拋出異常。
4.7. TransactionDefinition.PROPAGATION_NESTED:若是當前存在事務,則建立一個事務做爲當前事務的嵌套事務來運行;若是當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。express
幾種事物傳播行爲的狀況:編程
事務中能夠設置一些保存點(階段標識),回滾時能夠指定回滾到前面的哪一個保存點。設計模式
提交、回滾事務app
<!-- 配置事務管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
在xml中開啓註解方式支持框架
<!-- 開啓註解方式的事務配置支持--> <tx:annotation-driven transaction-manager="txManager"/>
或在java代碼中以註解的方式(@EnableTransactionManagement)開啓註解方式支持
package com.study.leesmall.spring.sample.tx; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; import org.springframework.transaction.annotation.EnableTransactionManagement; import com.study.leesmall.spring.sample.tx.entity.User; import com.study.leesmall.spring.sample.tx.service.UserService; @Configuration @ComponentScan("com.study.leesmall.spring.sample.tx") @ImportResource("classpath:com/study/leesmall/spring/sample/tx/application.xml") @EnableTransactionManagement public class TxMain { public static void main(String[] args) { try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TxMain.class);) { User user = new User(); user.setId("1234564"); user.setUserName("leesmall-666666666"); UserService userService = context.getBean(UserService.class); userService.insertUser(user); } } }
package com.study.leesmall.spring.sample.jta.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.study.leesmall.spring.sample.jta.dao.LogDao; import com.study.leesmall.spring.sample.jta.entity.Log; @Service public class LogService { @Autowired private LogDao logDao; @Transactional public void insertLog(Log log) { this.logDao.insert(log); } }
說明:rollbackFor默認狀況下是對RuntimeException進行回滾。
Spring提供基於AOP的聲明式事務管理,讓咱們的事務管理變得簡單、易用!
<!-- 數據源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <!-- 配置初始化大小、最小、最大鏈接數 --> <property name="initialSize" value="1" /> <property name="minIdle" value="1" /> <property name="maxActive" value="10" /> </bean> <!-- 配置事務管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- ********************** 聲明式事務配置 begin ************** --> <!-- 配置事務加強的advice --> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <!-- all methods starting with 'get' are read-only --> <tx:method name="get*" read-only="true" /> <!-- other methods use the default transaction settings (see below) --> <tx:method name="*" /> </tx:attributes> </tx:advice> <!-- 配置事務的AOP切面 --> <aop:config> <aop:pointcut id="allService" expression="execution(* com.study.leesmall.spring.sample.tx.service.*Service.*(..)))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="allService"/> </aop:config>
<!-- ********************** 聲明式事務配置 end ************** -->
@Autowired
private PlatformTransactionManager txManager;
public User insertUser(User u) { // 一、建立事務定義 TransactionDefinition definition = new DefaultTransactionDefinition(); // 二、根據定義開啓事務 TransactionStatus status = txManager.getTransaction(definition); try { this.userDao.insert(u); // 三、提交事務 txManager.commit(status); return this.userDao.find(u.getId()); } catch (Exception e) { // 四、異常了,回滾事務 txManager.rollback(status); throw e; } }
@Override @Nullable public <T> T execute(TransactionCallback<T> action) throws TransactionException { Assert.state(this.transactionManager != null, "No PlatformTransactionManager set"); if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) { return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action); } else { TransactionStatus status = this.transactionManager.getTransaction(this); T result; try { result = action.doInTransaction(status); } catch (RuntimeException | Error ex) { // Transactional code threw application exception -> rollback rollbackOnException(status, ex); throw ex; } catch (Throwable ex) { // Transactional code threw unexpected exception -> rollback rollbackOnException(status, ex); throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception"); } this.transactionManager.commit(status); return result; } }
Spring爲事務管理提供了統一的抽象建模,這樣咱們使用Spring來進行事務管理時,就只須要學會這套API便可,不管底下使用的是何種事務管理方式,jdbc也好,jpa也好,hibernate也好,jta也好。咱們的業務代碼中都是面向Spring的事務API。大大下降了咱們的學習、使用成本
TransactionDefinition:事務定義。Spring事務管理框架將爲咱們管理事務,但不清楚該如何替咱們管理,咱們就經過事務定義來定義咱們須要的事務管理信息,把這些信給事務管理器,它就知道咱們的意圖了。
PlatformTransactionManager平臺級的事務管理器,它抽象定義了事務管理行爲,不一樣的事務管理實現實現該接口。咱們編程面向該接口。
請仔細看源碼中每一個方法的的註釋。
TransactionStatus 事務狀態,持有事務的狀態信息。事務管理代碼可經過它獲取事務狀態、以及顯式地設置回滾(代替異常的方式)。它繼承了SavePoint接口。在它的實現中會持有事務的不少對象:如事務對象、被掛起的事務資源等等。
從TransactionManager中獲取事務獲得它,提交/回滾事務時要給入它:
看 TransactionTemplate 中的使用示例:
AbstractPlatformTransactionManager是PlatformTransactionManager的實現類
PlatformTransactionManager對應的源代碼:
PlatformTransactionManager的子類:
下面來看一下AbstractPlatformTransactionManager中的三個方法的實現邏輯
getTransaction()
commit()
rollback()
org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(TransactionStatus)
org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(DefaultTransactionStatus, boolean)
總結這裏的設計模式、設計原則:
面向接口編程
抽象類:固定不變的實現,提供模板方法,具體子類實現模板方法
一句話:接口、抽象類、模板方法、具體子類。Spring中不少地方用了。
DataSourceTransactionManager是基於jdbc connection的本地事務管理實現。多個方法調用參與到同一個事務,是經過共用connection來完成的
方法一:UserService.insertUser調用了方法二:logService.insertLog(log),兩個都加事務定義,驗證如下幾種傳播:
方法一required---方法二required
方法一required---方法二requires_new
方法一required---方法二nested
Xml配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 加載配置參數 --> <context:property-placeholder location="classpath:com/study/leesmall/spring/sample/tx/application.properties"/> <!-- 數據源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <!-- 配置初始化大小、最小、最大鏈接數 --> <property name="initialSize" value="1" /> <property name="minIdle" value="1" /> <property name="maxActive" value="10" /> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" scope="prototype"> <property name="dataSource" ref="dataSource" /> </bean> <!-- ******************* 事務管理配置 begin ********************************** --> <!-- 配置事務管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- ******************* 事務管理配置 end ********************************** --> </beans>
UserService:
@Service public class UserService { @Autowired private UserDao userDao; @Autowired private LogService logService; @Transactional public User insertUser(User u) { this.userDao.insert(u); Log log = new Log(System.currentTimeMillis() + "", System.currentTimeMillis() + "-" + u.getUserName()); this.logService.insertLog(log); return this.userDao.find(u.getId()); } }
LogService:
@Service public class LogService { @Autowired private LogDao logDao; @Transactional // @Transactional(propagation = Propagation.REQUIRES_NEW) // @Transactional(propagation = Propagation.NESTED) public void insertLog(Log log) { this.logDao.insert(log); } }
TxMain:
package com.study.leesmall.spring.sample.tx; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; import org.springframework.transaction.annotation.EnableTransactionManagement; import com.study.leesmall.spring.sample.tx.entity.User; import com.study.leesmall.spring.sample.tx.service.UserService; @Configuration @ComponentScan("com.study.leesmall.spring.sample.tx") @ImportResource("classpath:com/study/leesmall/spring/sample/tx/application.xml") @EnableTransactionManagement public class TxMain { public static void main(String[] args) { try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TxMain.class);) { User user = new User(); user.setId("1234564"); user.setUserName("leesmall-666666666"); UserService userService = context.getBean(UserService.class); userService.insertUser(user); } } }
在AbstractPlatformTransactionManager.getTransaction方法裏面打斷點拿到調用棧去分析
接下來跟代碼,重點是要找到 Connection 綁定到線程,綁定到了哪裏
調用棧以下:
看完getTransaction便可。如今已有connection了,接下來業務代碼中使用connection地方
找到JdbcTemplate的update操做的獲取connection的代碼,加斷點,而後F8,讓程序執行到這裏,看下調用棧。
F5進入DataSourceUtils.getConnection(obtainDataSource()),看它如何獲取Connection
調用棧以下:
第二次進到 getTransaction()
先看一下聲明式事務配置的示例:
<!-- ********************** 聲明式事務配置 ************** --> <!-- 配置事務加強的advice --> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <!-- all methods starting with 'get' are read-only --> <tx:method name="get*" read-only="true" /> <!-- other methods use the default transaction settings (see below) --> <tx:method name="*" /> </tx:attributes> </tx:advice> <!-- 配置事務的AOP切面 --> <aop:config> <aop:pointcut id="allService" expression="execution(* com.study.leesmall.spring.sample.tx.service.*Service.*(..)))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="allService"/> </aop:config>
1)入口
E:\lib\spring-tx-5.1.3.RELEASE.jar
/META-INF/spring.handlers
http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler
a)思考:<tx:advice>advice註冊的會是個什麼advice?
b)瀏覽TxAdviceBeanDefinitionParser的代碼,瞭解<tx:advice><tx:attributes>
<tx:method>的解析過程,產出了什麼。
c)瀏覽TransactionInterceptor的代碼,重點:invoke(MethodInvocationinvocation)方法
invokeWithinTransaction方法
d)瀏覽TransactionInterceptor的繼承體系,事務處理的邏輯都在TransactionAspectSupport中
e)瀏覽TransactionAspectSupport
它裏面有以下屬性:
它的重點方法是invokeWithinTransaction
f)瀏覽TransactionAttributeSource接口定義
g)瀏覽TransactionAttributeSource的繼承體系
h)再來看下TransactionAttribute,可能已不記得它是什麼了
1)解析<tx:advice>標籤
org.springframework.transaction.config.TxAdviceBeanDefinitionParser.doParse(Element, ParserContext, BeanDefinitionBuilder)
org.springframework.transaction.config.TxAdviceBeanDefinitionParser.parseAttributeSource(Element, ParserContext)
解析<tx:advice id="txAdvice" />標籤的子標籤<tx:attributes>
2)分析TransactionInterceptor
TransactionInterceptor的繼承體系:
父類:
子類沒有:
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(MethodInvocation)
事務處理的邏輯都在TransactionAspectSupport中
瀏覽TransactionAspectSupport
它裏面有以下屬性:
它的重點方法是invokeWithinTransaction
org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(Method, Class<?>, InvocationCallback)
org.springframework.transaction.interceptor.TransactionAspectSupport.getTransactionAttributeSource()
org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(PlatformTransactionManager, TransactionAttribute, String)
org.springframework.transaction.interceptor.TransactionAspectSupport.completeTransactionAfterThrowing(TransactionInfo, Throwable)
3)瀏覽TransactionAttributeSource接口定義
TransactionAttributeSource的繼承體系:
事務處理結果的監聽:Spring裏面能夠對事物處理的結果進行監聽
https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/data-access.html#transaction-event
開啓註解支持:
<tx:annotation-driven transaction-manager="txManager"/>
1)入口
E:\lib\spring-tx-5.1.3.RELEASE.jar
/META-INF/spring.handlers
http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler
org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser
org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser.parse(Element, ParserContext)
org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser.AopAutoProxyConfigurer.configureAutoProxyCreator(Element, ParserContext)
org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor
org.springframework.transaction.interceptor.TransactionAttributeSourcePointcut
org.springframework.aop.support.StaticMethodMatcherPointcut
org.springframework.transaction.annotation.AnnotationTransactionAttributeSource
何時建立代理?
建立代理:getBean()建立Bean實例的時候
判斷是否要建立代理?
怎麼判斷?Advisor的Pointcut是否匹配。事務是否是一個advisor
怎麼使用調用它的方法?
切面加強、事務
@EnableTransactionManagement
2.1)來看@EnableTransactionManagement這個註解的定義
@EnableTransactionManagement起做用靠:
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
@Import等價於在TxMain上面導入了一個包
2.2)看TransactionManagementConfigurationSelector
和<tx:annotation-driven transaction-manager="txManager"/>的實現是同樣的
/** * 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[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()}; case ASPECTJ: return new String[] {determineTransactionAspectClass()}; default: return null; } }
2.3)看org.springframework.context.annotation.AutoProxyRegistrar實現的接口方法:
2.4)看org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration類
2.5)@import在哪裏被解析的
ContextNamespaceHandler
AnnotationConfigBeanDefinitionParser
ConfigurationClassPostProcessor
ConfigurationClassParser
org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass()
org.springframework.context.annotation.ConfigurationClassParser
啓動的時候,解析@EnableTransactionManagement,完成事務管理相關的AOP bean註冊
剩下的事情都交給AOP
Xml:
啓動的時候,完成事務管理相關的AOP bean註冊
完整代碼獲取地址:https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/spring-source-study
官網學習連接:https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/data-access.html#transaction