我們看事務(wù)的源碼莱找,不僅是為了更好地使用Spring酬姆,而且能夠從源碼中學(xué)習(xí)到編程思想,設(shè)計(jì)思想奥溺。這篇文章框架如圖所示:
一辞色、相關(guān)知識(shí)
MySQL事務(wù)
- 事務(wù)的啟動(dòng)
- 顯式啟動(dòng)事務(wù)語句, begin 或 start transaction浮定。配套的提交語句是 commit相满,回滾語句是 rollback。
- set autocommit=0桦卒,這個(gè)命令會(huì)將這個(gè)線程的自動(dòng)提交關(guān)掉立美。意味著如果你只執(zhí)行一個(gè) select 語句,這個(gè)事務(wù)就啟動(dòng)了方灾,而且并不會(huì)自動(dòng)提交悯辙。這個(gè)事務(wù)持續(xù)存在直到你主動(dòng)執(zhí)行 commit 或 rollback 語句,或者斷開連接迎吵。建議設(shè)置set autocommit=1躲撰,避免造成長事務(wù)。
- 事務(wù)的隔離性:未提交击费、提交讀拢蛋、可重復(fù)讀、串行化
- MVCC
MySQL的默認(rèn)隔離級(jí)別是可重復(fù)讀蔫巩,下面以可重復(fù)度隔離級(jí)別談?wù)凪VCC是怎么實(shí)現(xiàn)可重復(fù)讀的谆棱。
MySQL45講
begin/start transaction 命令并不是一個(gè)事務(wù)的起點(diǎn),在執(zhí)行到它們之后的第一個(gè)操作 InnoDB 表的語句圆仔,事務(wù)才真正啟動(dòng)垃瞧。如果你想要馬上啟動(dòng)一個(gè)事務(wù),可以使用 start transaction with consistent snapshot 這個(gè)命令坪郭。
第一種啟動(dòng)方式个从,一致性視圖是在執(zhí)行第一個(gè)快照讀語句時(shí)創(chuàng)建的;第二種啟動(dòng)方式歪沃,一致性視圖是在執(zhí)行 start transaction with consistent snapshot 時(shí)創(chuàng)建的嗦锐。
對(duì)于MySQL的每一行上,有一個(gè)row trx_id沪曙,表示當(dāng)前的版本號(hào)奕污,每次事務(wù)提交之后,會(huì)新增一個(gè)版本行液走,新的版本行的row trx_id更新為事務(wù)的id
每個(gè)事務(wù)所能看到的一致性視圖碳默,就是事務(wù)啟動(dòng)瞬間已提交的行贾陷。如果事務(wù)的行還未提交,那么需要回滾到上一個(gè)版本嘱根。
- undo log回滾
我們看到的上圖的箭頭昵宇,就是 undo log;而 V1儿子、V2瓦哎、V3 并不是物理上真實(shí)存在的,而是每次需要的時(shí)候根據(jù)當(dāng)前版本和 undo log 計(jì)算出來的柔逼。比如蒋譬,需要 V2 的時(shí)候,就是通過 V4 依次執(zhí)行 兩次undo log U3愉适、U2 算出來犯助。
ThreadLocal
ThreadLocal是Java線程封閉的一種方式,將變量局限在線程中维咸,每個(gè)線程都有一份數(shù)據(jù)剂买,避免了變量的共享,也就避免了線程安全的問題癌蓖。
具體的實(shí)現(xiàn)可以看:http://www.reibang.com/p/22ca4db0a616
Spring AOP
- AOP的一些概念
1.1 Join point和pointcut區(qū)別
Join point 就是菜單上的選項(xiàng)瞬哼,Pointcut就是你選的菜。Join point 你只是你切面中可以切的那些方法租副,一旦你選擇了要切哪些方法坐慰,那就是Pointcut。
也就是說用僧,所有在你程序中可以被調(diào)用的方法都是Join point. 使用Pointcut 表達(dá)式结胀,那些匹配的方法,才叫Pointcut责循。所以你根本不用關(guān)心Join point糟港。比如你有10個(gè)方法,你只想切2個(gè)方法院仿,那么那10個(gè)方法就是Join point秸抚, 2個(gè)方法就是Pointcut
1.2 Advice
advice就是你作用到pointcut上的方式,你可以使用befor, after 或者around
1.3 advisor
advisor就是作用在具體對(duì)象上的ponitcut和advice意蛀,把ponitcut和advice合起來就是advisor耸别。切到哪個(gè)具體方法上面健芭,是使用的befor县钥、after 還是around,就這個(gè)就叫advisor. - 說到Spring AOP慈迈,就得談一下Bean的生命周期若贮。我們知道Bean的生命周期有實(shí)例化省有、屬性注入、初始化谴麦、銷毀蠢沿。Spring的AOP是在Bean初始化的過程中,生成代理類并返回的匾效。
Spring AOP的源碼可以總結(jié)為以下內(nèi)容:
1)通過AutoProxyRegistrar類將AbstractAutoProxyCreator注冊(cè)到Spring容器中
2)AbstractAutoProxyCreator類的postProcessBeforeInstantiation()只有在自定義 TargetSource的時(shí)候才會(huì)返回代理后的Bean舷蟀,否則會(huì)繼續(xù)實(shí)例化Bean
3)AbstractAutoProxyCreator類的postProcessAfterInitializatio會(huì)在Bean實(shí)例化之后調(diào)用,里面有個(gè)wrapIfNecessary方法獲取Advisors面哼,然后創(chuàng)建代理
Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
} else {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
4)調(diào)用bean方法時(shí)通過proxy來調(diào)用野宜,proxy依次調(diào)用增強(qiáng)器的相關(guān)方法,來實(shí)現(xiàn)方法切入getBean可以獲取到代理后的Bean
Spring 攔截器
Spring事務(wù)的切面實(shí)現(xiàn)是TransactionInterceptor魔策,里面的Invoke方法是實(shí)現(xiàn)了MethodInterceptor的invoke接口匈子,至于Spring的攔截器底層原理是怎么樣的呢,還需要再研究研究闯袒。
二虎敦、源碼實(shí)現(xiàn)
整體代碼流程分析
Spring事務(wù)是怎么通過AOP調(diào)用到TransactionInterceptor的invoke方法的,感興趣的可以看下這篇文章:https://my.oschina.net/u/3748038/blog/1602438政敢∑溽悖總體來說就是SpringBoot的有一個(gè)TransactionAutoConfiguration自動(dòng)配置類,自動(dòng)配置了@EnableTransactionManagement注解喷户,這個(gè)注解作用就是開啟事務(wù)擂橘。具體就是有一個(gè)AutoProxyRegistrar注冊(cè)了,初始化AbstractAutoProxyCreator,AbstractAutoProxyCreator實(shí)現(xiàn)了BeanProcessors,所以在Bean實(shí)例化前后時(shí)候分別調(diào)用
postProcessBeforeInitialization(Object bean, String beanName) -- 實(shí)例化前執(zhí)行
Object postProcessAfterInitialization(Object bean, String beanName) --實(shí)例化后執(zhí)行
取具。然后可以發(fā)現(xiàn)在postProcessAfterInitialization方法中調(diào)用了wrapIfNecessary席里,一直往下追蹤,可以看到最終會(huì)調(diào)用ransactionAttributeSource的getTransactionAttribute()方法疲憋,這里就用到了那些parser類去真正解析當(dāng)前bean是否包含那些注解,如果包含注解,那么就把當(dāng)前類包裝成代理類茎用。還有就是事務(wù)代理類在執(zhí)行方法之前調(diào)用的切面是BeanFactoryTransactionAttributeSourceAdvisor的advice,亦即TransactionInterceptor,這樣大概就能串起來了睬罗。
-
TransactionInterceptor的invoke轨功,
然后調(diào)用子類TransactionAspectSupport的invokeWithinTransaction方法,這里又是模板方法的意思容达。
- 關(guān)鍵流程代碼都在invokeWithinTransaction方法里古涧,下面只保留了關(guān)鍵代碼
@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);
//獲取事務(wù)管理器,這里默認(rèn)獲取到的是DataSourceTransactionManager花盐,
//具體原因不明羡滑。菇爪。
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// 通過事務(wù)傳播隔離級(jí)別判斷是否需要開啟新事務(wù)
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal;
try {
//調(diào)用目標(biāo)方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 發(fā)生unchecked異常,或者error的話柒昏,需要回滾凳宙。
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
//提交事務(wù)
commitTransactionAfterReturning(txInfo);
return retVal;
}
}
-
createTransactionIfNecessary方法,判斷是否 需要開啟新事務(wù)职祷,是根據(jù)事務(wù)傳播條件判斷的氏涩,具體的判斷,在下面講有梆。這個(gè)方法主要關(guān)注getTransaction方法
事務(wù)管理器
getTransaction中的doGetTransaction削葱、isExistingTransaction、doBegin等調(diào)用的都是DataSourceTransactionManager中的方法淳梦,事務(wù)管理器保存了dataSource析砸,dataSource中有數(shù)據(jù)庫的連接,在事務(wù)中的每一個(gè)語句的執(zhí)行爆袍,都使用這個(gè)連接執(zhí)行首繁。保證事務(wù)的執(zhí)行,總是復(fù)用同一個(gè)連接陨囊。
doBegin的代碼弦疮,忽略無關(guān)代碼
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();
//保存之前的隔離級(jí)別,因?yàn)楫?dāng)前事務(wù)可能是子事務(wù)蜘醋,等當(dāng)前執(zhí)行完了胁塞,還要再執(zhí)行
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
// 設(shè)置是否自動(dòng)提交,這里的自動(dòng)提交的含義在上面說過了
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
con.setAutoCommit(false);
}
prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);
//設(shè)置超時(shí)時(shí)間
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
if (txObject.isNewConnectionHolder()) {
//連接綁定到線程压语,這里也是使用的ThreadLocal
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
}
doCommit和doRollback源碼比較簡單啸罢,這里就不開展說了。
事務(wù)傳播機(jī)制
Spring事務(wù)是在getTransaction里面判斷胎食,是否要開啟新事務(wù)扰才,判斷的主要依據(jù)是@Transactional的propagation屬性。
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
然后我們可以看下源碼厕怜,是有以下幾種枚舉值:
public enum Propagation {
//支持當(dāng)前事務(wù)衩匣,如果當(dāng)前沒有事務(wù),就新建一個(gè)粥航,是默認(rèn)的傳播機(jī)制
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
//支持當(dāng)前事務(wù)琅捏,當(dāng)前沒有事務(wù)就非事務(wù)地執(zhí)行
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
//支持當(dāng)前事務(wù),如果沒有的話柄延,就拋出異常S
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
//創(chuàng)建新事務(wù)杠输,并且掛起當(dāng)前事務(wù)
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
// 非事務(wù)地執(zhí)行鹦牛,如果當(dāng)前有事務(wù)就掛起
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
//非事務(wù)地執(zhí)行礼殊,如果當(dāng)前存在事務(wù)就拋出異常
NEVER(TransactionDefinition.PROPAGATION_NEVER),
// 如果當(dāng)前事務(wù)存在婚陪,就執(zhí)行嵌套事務(wù)
NESTED(TransactionDefinition.PROPAGATION_NESTED);
}
上面的注釋都是基于源碼注解的理解沽一,接下來我們看下實(shí)際的執(zhí)行情況拙友,看看對(duì)于不同的傳播方式漾根,Spring會(huì)做哪些處理鲫竞。依然省略無關(guān)代碼
@Override
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
//這里調(diào)用的是事務(wù)管理器的方法辐怕,獲取到當(dāng)前的事務(wù)
Object transaction = doGetTransaction();
//省略無關(guān)代碼........
//調(diào)用事務(wù)管理器的isExistingTransaction,判斷是否存在事務(wù)从绘,如果存在事務(wù)的話寄疏,調(diào)用handleExistingTransaction
if (isExistingTransaction(transaction)) {
return handleExistingTransaction(definition, transaction, debugEnabled);
}
//省略無關(guān)代碼.....
// 如果是MANDATORY,而且當(dāng)前不存在事務(wù)僵井,那么拋出異常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
//如果是REQUIRED陕截,或者是REQUIRES_NEW,或者是NESTED批什,那么開啟新事務(wù)
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
//省略無關(guān)代碼
}
else {
// 創(chuàng)建一個(gè)空事務(wù)农曲,沒有實(shí)際的事務(wù)(創(chuàng)建這個(gè)事務(wù)的意圖不是很理解。驻债。乳规。)
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}
接下來,我們看看對(duì)于已存在事務(wù)的情況却汉,Spring是怎么處理的驯妄。
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
//如果已存在事務(wù),NEVER傳播級(jí)別合砂,拋出異常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
//NOT_SUPPORTED的話青扔,中止當(dāng)前事務(wù),然后準(zhǔn)備事務(wù)翩伪,這里并沒有開啟事務(wù)
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
// REQUIRES_NEW微猖,掛起當(dāng)前事務(wù),新建事務(wù)缘屹,并開啟事務(wù)
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}
//NESTED隔離級(jí)別
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
//設(shè)置了安全點(diǎn)凛剥,給已存在的事務(wù)創(chuàng)建安全點(diǎn),應(yīng)該是為了子事務(wù)失敗之后轻姿,父事務(wù)不回滾犁珠。
if (useSavepointForNestedTransaction()) {
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
return status;
}
else {
// 否則開啟新事務(wù)
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, null);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}
// SUPPORTS or REQUIRED.
if (isValidateExistingTransaction()) {
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
Constants isoConstants = DefaultTransactionDefinition.constants;
isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
"(unknown)"));
}
}
}
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
看了那么多源碼,我們可以總結(jié)出以下規(guī)律
回滾和提交的條件
protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.hasTransaction()) {
if (txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
//省略異常代碼
}
else {
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
//省略異常代碼
}
}
}
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
判斷rollbackOn方法互亮,成立的時(shí)候才會(huì)回滾犁享,而對(duì)于rollbackOn,判斷的是RuntimeException和error兩種情況豹休,所以說炊昆,其他的異常都會(huì)提交事務(wù)。
三、使用最佳實(shí)踐
private修飾方法不生效
Spring事務(wù)基于AOP實(shí)現(xiàn)凤巨,而AOP是通過JDK代理视乐,或者Cglib代理,如果是代理接口敢茁,使用的是JDK代理佑淀,否則使用CGLIB代理。對(duì)于private修飾的方法卷要,會(huì)使用Cglib渣聚,但是Cglib的原理是對(duì)指定的目標(biāo)類動(dòng)態(tài)生成一個(gè)子類独榴,并覆蓋其中方法實(shí)現(xiàn)增強(qiáng)僧叉,但因?yàn)椴捎玫氖抢^承,所以不能對(duì)final修飾的類和final方法和private方法進(jìn)行代理棺榔。
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 1.config.isOptimize()是否使用優(yōu)化的代理策略瓶堕,目前使用與CGLIB
// config.isProxyTargetClass() 是否目標(biāo)類本身被代理而不是目標(biāo)類的接口
// hasNoUserSuppliedProxyInterfaces()是否存在代理接口
if (config.isOptimize() || config.isProxyTargetClass() ||hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation.");
}
// 2.如果目標(biāo)類是接口或者是代理類,則直接使用JDKproxy
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 3.其他情況則使用CGLIBproxy
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
方法嵌套不生效
這里直接引用極客時(shí)間的內(nèi)容~
Spring 通過 AOP 技術(shù)對(duì)方法進(jìn)行增強(qiáng)症歇,要調(diào)用增強(qiáng)過的方法必然是調(diào)用代理后的對(duì)象郎笆。我們嘗試修改下 UserService 的代碼,注入一個(gè) self忘晤,然后再通過 self 實(shí)例調(diào)用標(biāo)記有 @Transactional 注解的 createUserPublic 方法宛蚓。設(shè)置斷點(diǎn)可以看到,self 是由 Spring 通過 CGLIB 方式增強(qiáng)過的類:CGLIB 通過繼承方式實(shí)現(xiàn)代理類设塔,private 方法在子類不可見凄吏,自然也就無法進(jìn)行事務(wù)增強(qiáng);this 指針代表對(duì)象自己闰蛔,Spring 不可能注入 this痕钢,所以通過 this 訪問方法必然不是代理。
public int createUserWrong2(String name) {
try {
this.createUserPublic(new UserEntity(name));
} catch (Exception ex) {
log.error("create user failed because {}", ex.getMessage());
}
return userRepository.findByName(name).size();
}
//標(biāo)記了@Transactional的public方法
@Transactional
public void createUserPublic(UserEntity entity) {
userRepository.save(entity);
if (entity.getName().contains("test"))
throw new RuntimeException("invalid username!");
}
傳播方式使用
異承蛄回滾的設(shè)置
通過上面源碼分析可知任连,Spring事務(wù),默認(rèn)只會(huì)對(duì)RuntimeException和error的異常進(jìn)行回滾例诀,如果想讓未受檢異常也回滾随抠,需要在注解上加上
rollbackFor = Exception.class