標籤:

Spring中Transactional註解的實現

最近在不斷的尋找新的技術博客平台,之前一直用簡書寫,但是簡書上貼代碼實在太痛苦,寫技術博客一個是想有個分享的機會,一個算是對自己的技術積累,實在不想費太多時間花在寫本身這個事情上。今天發現知乎專欄貼代碼倒是很方便,而且頁面做的也比cnblogs、csdn等老牌技術博客平台做的高大上些,那就先試寫一篇。

最近不管是review別人的代碼還是自己在做一些東西,接觸Spring事務比較多,對spring的transactional的註解也自然用了不少次,但是有一些自己的疑惑,它到底怎麼實現的。

Spring的事務支持眾所周知,是依賴Spring的AOP實現的,這個一說起來大家可能都知道,舉個簡單的栗子,有一個BizClassInterface的介面,

public interface BizClassInterface { void doBiz();}

很簡單,再做一個它的實現,

public class BizClass implements BizClassInterface { /* (non-Javadoc) * @see com.future.spring.proxy.BizClassInterface#doBiz() */ @Override public void doBiz() { System.out.println("do some biz here!"); }}

做一個簡單的日誌類,來切doBiz的方法,

public class AopLog implements MethodBeforeAdvice{ @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("do before"); }}

然後進行xml配置,

<bean id="log" class="com.future.spring.proxy.AopLog" /><bean id="bizClass" class="com.future.spring.proxy.BizClass"></bean><bean id="bizClassProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>com.future.spring.proxy.BizClassInteface</value> </property> <property name="target"> <ref bean="bizClass" /> </property> <property name="interceptorNames"> <list> <value>log</value> </list> </property></bean>

注意看,這裡有兩個Bean,bizClass是BizClass類本身,bizClassProxy是BizClass類的一個代理。那麼再寫一個測試類,

public class SpringProxyTests extends AbstractSpringTests{ @Qualifier("bizClassProxy") @Autowired private BizClassInterface bizClass111; @Test public void doBiz() { bizClass111.doBiz(); }}

顯然,如果你需要Spring AOP的支持,那麼你不需要將bizClassProxy,即代理類注入到這個test中,否則如果注入的是bizClass,你講無法獲得Spring AOP的支持。

接下來,利用transactional註解做一個簡單的事務配置,其中需要的db和事務管理器的配置略過,

@Servicepublic class BizClass implements BizClassInterface { /* (non-Javadoc) * @see com.future.spring.proxy.BizClassInterface#doBiz() */ @Override @Transactional public void doBiz() { System.out.println("do some biz here!"); }}

重寫測試,並執行,實際表名可以運行通過,並且有事務支持。

public class SpringProxyTests extends AbstractSpringTests{ @Autowired private BizClassInterface bizClass111; @Test public void doBiz() { bizClass111.doBiz(); }}

那麼問題來,通過Service註解會生成一個BizClass類的Bean,通過Transactional註解,由於Spring事務是依賴Spring AOP實現,那麼趕腳會生成一個BizClass的代理類Bean。可是通過autowired怎麼實現自動注入BizClass的代理類bean呢?難道autowired的這麼神奇和自動?另外,關於autowired還有一個坑,就是很多網上可以搜到的文章說autowired僅僅支持類型注入,也就是說如果同一個類的兩個不同名的Bean被聲明出來,啟動Spring上下文的時候就會報錯。事實上,在spring2.5.6或者3.0版本可能是有這個問題(具體版本記不得了),但是其實至少Spring4.0以上(至少我用的4.0.5版本是這樣)就支持如果通過type找到兩個或者以上數量的bean的時候,spring會自動再根據要注入的bean的名字去查找,如果找到唯一一個就會注入不會報錯。而且autowired對集合類的支持也被優化了,也就是說如果有一個欄位是List<SomeClass>,並且被標記了註解autowired,那麼Spring在進行依賴注入的時候,會自動尋找SomeClass類的所有實現類,然後依次注入,非常好用。

回到主題,Spring在初始化IOC容器的時候回load所有的BeanDefinition,通過單步跟蹤可以看到一個名字是bizClass的bean。Spring通過調用BeanFactory的getBean來獲得Bean,實際上會根據情況實例化bean和進行依賴注入,具體細節可以查看Spring源代碼。

重點代碼在AbstractAutowireCapableBeanFactory的doCreateBean方法

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } //.....omit code ....// // Initialize the bean instance. Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { exposedObject = initializeBean(beanName, exposedObject, mbd); } } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } //... omit code ...// return exposedObject; }

其中,createBeanInstance函數是用來創建bean的實例,populateBean函數是用來進行注入,initializeBean是用來初始化這個bean。通過createBeanInstance函數創造出來的確實是bizClass的實例,但是再看initializeBean函數。該函數主要由三部分組成,applyBeanPostProcessorsBeforeInitialization、invokeInitMethods和applyBeanPostProcessorsAfterInitialization。其中applyBeanPostProcessorsBeforeInitialization和applyBeanPostProcessorsAfterInitialization 與BeanPostProcessor機制有關,這裡就不展開了。

這裡會有一個AbstractAdvisorAutoProxyCreator類,他是實現Transaction代理的基礎。具體看這個類的wrapIfNecessary方法,

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { //... omit code...// // 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; }

最終實際是調用ProxyFactory的getProxy方法獲得代理實例。以上可以看到最終bizClass這個bean保存的實例還是代理類的實例,所以最終只是代理類被注入進去,從而實現了事務。

通過查看源代碼也可以發現,其實Transactional的註解是在loadbean的階段,生成了事務相關的advisor等類,所以specificInterceptors != DO_NOT_PROXY這個判斷才會成立,接著才會生成代理類。這也就是Transactional註解如何幫助我們完成事務支持的。

推薦閱讀:

Spring Security(三) -- JWT驗證原理(上)
【spring指南系列】使用Redis進行消息傳遞
Spring Security(一) -- 初識Spring Security
springboot(十七):使用Spring Boot上傳文件
Spring常用的注入方式

TAG:Spring |