對大多數Java開發者來說,Spring事務管理是Spring應用中最常用的功能,使用也比較簡單。本文主要從三個方面(基本概述、基于源碼的原理分析以及需要注意的細節)來逐步介紹Spring事務管理的相關知識點及原理,作為Spring事務管理的學習總結。
Spring事務管理概述
一、幾個重要概念
1.事務隔離級別
ANSI/ISO SQL92標準定義了4個隔離級別:READ UNCOMMITED、READ COMMITED、REPEATABLE READ和SERIALIZABLE,隔離程度由弱到強。不同的事務隔離級別能夠解決數據并發問題的能力不同,它與數據庫并發性是對立的,兩者此消彼長。
2.事務傳播行為
事務傳播主要是為了描述兩個服務接口方法嵌套調用時,被調用者在調用者有無事務時所采取的事務行為。Spring框架在TransactionDefinition接口中固定了7種事務傳播行為:PROPAGATION_REQUIRED、 PROPAGATION_SUPPORTS、 PROPAGATION_MANDATORY、 PROPAGATION_REQUIRES_NEW、 PROPAGATION_NOT_SUPPORTED、 PROPAGATION_NEVER、 PROPAGATION_NESTED。前面的6種是從EJB中引入的,而PROPAGATION_NESTED是Spring特有的。具體可參見深入淺出事務(4):Spring事務的傳播行為,該文結合具體代碼示例,通俗易懂。
3.事務同步管理器
TransactionSynchronizationManager——事務管理的基石,主要是為了解決事務管理在多線程環境下資源(如Connection、Session等)的并發訪問問題:使用ThreadLocal為不同事務線程維護獨立的資源副本,以及事務配置屬性和運行狀態信息,使各個事務線程互不影響。
4.事務管理SPI
SPI(Service Provider Interface)是一個框架開放給第三方的可擴展服務接口,供其具體實現,以支持框架的擴展性和插件式組件。Spring事務管理SPI主要包括3個接口:PlatformTransactionManager(進行事務的創建、提交或回滾)、TransactionDefinition(定義事務屬性,如隔離級別)和TransactionStatus(事務運行時狀態,如是否已完成)。這三者通過PlatformTransactionManager的如下接口進行關聯:
// 根據事務定義創建事務,并由TransactionStatus表示它
TransactionStatus getTransaction(TransactionDefinition definition);
// 根據事務運行時狀態提交或回滾事務
void commit(TransactionStatus status);
void rollback(TransactionStatus status);
二、基本用法
三種方式:編程、XML配置和注解。第一方式對應用代碼侵入性較大,現已較少使用。后面兩種則都屬于聲明式事務管理的方式,兩者的共同點是都提供事務管理信息的元數據,只不過方式不同。前者對代碼的侵入性最小,也最為常用,后者則屬于較為折衷的方案,有一點侵入性,但相對也較少了配置,各有優劣,依場景需求而定。聲明式事務管理是Spring的一大亮點,利用AOP技術將事務管理作為切面動態織入到目標業務方法中,讓事務管理簡單易行。
而不管是使用哪種方式,數據源、事務管理器都是必須的,一般通過XML的Bean配置:
...
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destory-method="close">
<property name="driverClass"><value>${jdbc.driverClass}</value></property>
<property name="jdbcUrl"><value>${jdbc.jdbcUrl}</value></property>
<property name="user"><value>${jdbc.username}</value></property>
<property name="password"><value>${jdbc.password}</value></property>
<property name="maxPoolSize"><value>${jdbc.maxPoolSize}</value></property>
<property name="minPoolSize"><value>${jdbc.minPoolSize}</value></property>
<property name="initialPoolSize"><value>${initialPoolSize}</value></property>
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref local="dataSource" />
</property>
<!-- 指定事務管理器標識,可被@Transactional注解引用 -->
<qualifier value="txManagerA" />
</bean>
...
1.編程式事務管理
采用與DAO模板類一樣的開閉思想,Spring提供了線程安全的TransactionTemplate模板類來處理不變的事務管理邏輯,將變化的部分抽象為回調接口TransactionCallback供用戶自定義數據訪問邏輯。使用示例:
public class ServiceAImpl {
private DaoA daoA;
@autowried
private TransactionTemplate template;
public void addElement(final Element ele) {
template.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status){
daoA.addElement(ele);
}
});
}
}
TransactionTemplate的配置信息:
...
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager">
<ref local="txManager" />
</property>
<property name="isolationLevelName" value="ISOLATION_DEFAULT" />
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED" />
</bean>
...
當然,用戶也可以不使用TransactionTemplate,而是直接基于原始的Spring事務管理SPI進行編程式事務管理,只不過這種方式對代碼侵入性最大,不推薦使用,這里也就不多做介紹了。
2.基于XML配置的事務管理
Spring早期版本,是通過TransactionProxyFactoryBean代理類實施聲明式事務配置,由于這種方式的種種弊端,后來引入AOP切面描述語言后,提出一種更簡潔的基于Schema的配置方式:tx/aop命名空間,使聲明式事務配置更簡潔便利。
2.1基于Bean的配置
...
<bean id="serviceATarget" class="org.sherlocky.book.spring3x.service.ServiceAImpl" />
<bean id="serviceA" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
p:transactionManager-ref="txManager"
p:target-ref="serviceATarget">
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
...
2.1基于Schema的配置(常用)
...
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="create*" propagation="REQUIRED" timeout="300" rollback-for="java.lang.Exception" />
<tx:method name="delete*" propagation="REQUIRED" timeout="300" rollback-for="java.lang.Exception" />
<tx:method name="update*" propagation="REQUIRED" timeout="300" rollback-for="java.lang.Exception" />
<tx:method name="get*" propagation="REQUIRED" read-only="true" timeout="300" />
<tx:method name="*" propagation="REQUIRED" read-only="true" timeout="300" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* org.sherlocky.book.spring3x.service.*ServiceA.*(..))" />
<aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice" />
</aop:config>
3.基于注解的事務管理
通過@Transactional對需要事務增強的Bean接口、實現類或方法進行標注,在容器中配置<tx:annotation-driven>以啟用基于注解的聲明式事務。注解所提供的事務屬性信息與XML配置中的事務信息基本一致,只不過是另一種形式的元數據而已。使用示例:
@Transactional("txManagerA")
public class ServiceAImpl {
private DaoA daoA;
public void addElement(final Element ele) {
...
}
}
Spring事務管理源碼分析-(spring3.1.0)
源碼分析一定要有目的性,至少有一條清晰的主線,比如要搞清楚框架的某一個功能點背后的代碼組織,前因后果,而不是一頭扎進源碼里,無的放矢。本文就從Spring事務管理的三種使用方式入手,逐個分析Spring在背后都為我們做了些什么。
一、編程式
1.TransactionTemplate
TransactionTemplate是編程式事務管理的入口,源碼如下:
public class TransactionTemplate extends DefaultTransactionDefinition
implements TransactionOperations, InitializingBean {
private PlatformTransactionManager transactionManager;
...
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);【1】
}
else {
TransactionStatus status = this.transactionManager.getTransaction(this);【2】
T result;
try {
result = action.doInTransaction(status);
}
catch (RuntimeException ex) {【3】
// Transactional code threw application exception -> rollback
rollbackOnException(status, ex);
throw ex;
}
catch (Error err) {【4】
// Transactional code threw error -> rollback
rollbackOnException(status, err);
throw err;
}
catch (Exception ex) {【5】
// Transactional code threw unexpected exception -> rollback
rollbackOnException(status, ex);
throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
}
this.transactionManager.commit(status);
return result;
}
}
}
1.1整體概述
TransactionTemplate提供了唯一的編程入口execute,它接受用于封裝業務邏輯的TransactionCallback接口的實例,返回用戶自定義的事務操作結果T。具體邏輯:先是判斷transactionManager是否是接口CallbackPreferringPlatformTransactionManager的實例,若是則直接委托給該接口的execute方法進行事務管理;否則交給它的核心成員PlatformTransactionManager進行事務的創建、提交或回滾操作。
CallbackPreferringPlatformTransactionManger接口擴展自PlatformTransactionManger,根據以下的官方源碼注釋可知,該接口相當于是把事務的創建、提交和回滾操作都封裝起來了,用戶只需要傳入TransactionCallback接口實例即可,而不是像使用PlatformTransactionManger接口那樣,還需要用戶自己顯示調用getTransaction、rollback或commit進行事務管理。
// Implementors of this interface automatically express a preference for
// callbacks over programmatic getTransaction, commit and rollback calls.
interface CallbackPreferringPlatformTransactionManager extends PlatformTransactionManager{...}
1.2具體剖析
DefaultTransactionDefinition
可以看到transactionTemplate直接擴展自DefaultTransactionDefinition,讓自身具有默認事務定義功能,【1】和【2】處將this作為execute或getTransaction的實參傳入,說明該事務管理是采用默認的事務配置,可以看下DefaultTransactionDefinition中定義的默認配置:
...
private int propagationBehavior = PROPAGATION_REQUIRED; //常用選擇:當前沒有事務,則新建;否則加入到該事務中
private int isolationLevel = ISOLATION_DEFAULT; //使用數據庫默認的隔離級別
private int timeout = TIMEOUT_DEFAULT; //-1,使用數據庫的超時設置
private boolean readOnly = false; //非只讀事務
TransactionOperations和InitializingBean
而TransactionOperations和InitializingBean接口分別定義了如下單個方法。InitializingBean是Spring在初始化所管理的Bean時常用的接口,以確保某些屬性被正確的設置或做一些初始化時的后處理操作,可參考InitializingBean的作用。
<T> T execute(TransactionCallback<T> action); //TransactionTemplate的編程接口
void afterPropertiesSet(); //Bean初始化時調用:在成員變量裝配之后
TransactionTemplate實現InitializingBean接口,主要是確保其核心成員transactionManager是否已初始化:
public void afterPropertiesSet() {
if (this.transactionManager == null) {
throw new IllegalArgumentException("Property 'transactionManager' is required");
}
}
從【3】【4】【5】可看出,基于TransactionTemplate的事務管理,在發生RuntimeException、Error或Exception時都會回滾,正常時才提交事務。
2. PlatformTransactionManager
該接口在Spring事務管理中扮演著重要角色??聪耮etTransaction的源碼注釋:
// Return a currently active transaction or create a new one, according to
// the specified propagation behavior.
該方法的主要作用就是根據TransactionDefinition返回當前有效事務或新建事務,其中就包含了事務傳播行為的控制邏輯。其唯一實現就是該接口對應的抽象類AbstractPlatformTransactionManager,這是典型的接口->抽象類->具體實現類三層結構,以提高代碼復用性。其中抽象類是負責實現一些共有邏輯,而具體子類則是各自實現差異化功能:
// 聲明為final,確保不能再被子類重寫
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction();
...
if (isExistingTransaction(transaction)) {【1】
return handleExistingTransaction(definition, transaction, debugEnabled);
}
...
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);【2】
...
try {
...
}
catch (RuntimeException ex) {
resume(null, suspendedResources);【3】
throw ex;
}
catch (Error err) {
resume(null, suspendedResources);
throw err;
}
}
else {
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}
可以看到它會根據【1】處的isExistingTransaction方法判斷當前是否有事務而分別作出不同的處理,包括掛起和恢復當前事務等,有興趣的童鞋可以深入【2】處的supend和【3】處的resume方法,會發現對事務的掛起和恢復操作實際是委托于TransactionSynchronizationManager來做的,而該類在前面也提過到,是Spring管理事務資源的,這幾個重要接口和類的關系漸漸清晰了,由于篇幅有限,后面打算單獨另起一篇細講。
2.聲明式
基于XML和注解的方式都是屬于聲明式事務管理,只是提供元數據的形式不用,索性就一起講了。聲明式事務的核心實現就是利用AOP技術,將事務邏輯作為環繞增強MethodInterceptor動態織入目標業務方法中。其中的核心類為TransactionInterceptor。從以下代碼注釋可知,TransactionInterceptor是專用于聲明式事務管理的。
// AOP Alliance MethodInterceptor for declarative transaction
// management using the common Spring transaction infrastructure {PlatformTransactionManager}
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {...}
1.TransactionInterceptor
// AOP Alliance MethodInterceptor for declarative transaction
// management using the common Spring transaction infrastructure
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {...}
從上述注釋中可知該類是專用于聲明式事務管理的,它的核心方法如下invoke:
public Object invoke(final MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr =
getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);【1】
final PlatformTransactionManager tm = determineTransactionManager(txAttr);【2】
final String joinpointIdentification = methodIdentification(invocation.getMethod(), targetClass);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);【3】
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.proceed();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);【4】
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);【5】
return retVal;
}
else {
try {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
new TransactionCallback<Object>() {
public Object doInTransaction(TransactionStatus status) {
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceed();
}
...
}
});
...
}
...
}
}
1.1整體概述
TransactionInterceptor實現了MethodInterceptor接口,將事務管理的邏輯封裝在環繞增強的實現中,而目標業務代碼則抽象為MethodInvocation(該接口擴展自Joinpoint,故實際是AOP中的連接點),使得事務管理代碼與業務邏輯代碼完全分離,可以對任意目標類進行無侵入性的事務織入。具體邏輯:先根據MethodInvocation獲取事務屬性TransactionAttribute,根據TransactionAttribute得到對應的PlatformTransactionManager,再根據其是否是CallbackPreferringPlatformTransactionManager的實例分別做不同的處理,整體上跟TransactionTemplate中大相徑庭,后面主要是介紹幾點不同的地方。
1.2具體剖析
MethodInterceptor
MethodInterceptor是AOP中的環繞增強接口,同一個連接點可以有多個增強,而TransactionInterceptor擴展自該接口,說明事務管理只是眾多橫切邏輯中的一種,還有很多其他的,比如像日志記錄、性能監控等,對于AOP而言并無區別,它會按照增強的順序統一處理。關于AOP,后期會單獨一篇詳細介紹。
TransactionAttribute和TransactionAttributeSource
在代碼【1】處,委托給TransactionAttributeSource根據MethodInvocation獲取對應的事務屬性TransactionAttribute,先來看下TransactionAttribute:
public interface TransactionAttribute extends TransactionDefinition {
/**
* Return a qualifier value associated with this transaction attribute.
* <p>This may be used for choosing a corresponding transaction manager
* to process this specific transaction.
*/
String getQualifier();
/**
* Should we roll back on the given exception?
* @param ex the exception to evaluate
* @return whether to perform a rollback or not
*/
boolean rollbackOn(Throwable ex);
}
就是在TransactionDefinition的基礎上增加了兩個可定制屬性,使用過XML配置和注解方式的童鞋應該都對qualifier和rollback-for再熟悉不過了,那兩個新增屬性就是為了支持這兩個配置項的。再來看下TransactionAttributeSource:
/**
* Interface used by TransactionInterceptor. Implementations know
* how to source transaction attributes, whether from configuration,
* metadata attributes at source level, or anywhere else.
* @see TransactionInterceptor#setTransactionAttributeSource
* @see TransactionProxyFactoryBean#setTransactionAttributeSource
*/
public interface TransactionAttributeSource {
TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass);
}
之所以有這個接口,是因為Spring提供了XML配置、注解等不同的事務元數據形式,即事務屬性的來源多樣,該接口正是將事務配置的來源進行抽象,不同的來源有對應不同的實現類,接口單一職責,巧妙精簡的設計!類圖如下,AnnotationTransactionAttributeSource是注解相關,而NameMatchTransactionAttributeSource、MatchAlwaysTransactionAttributeSource等是XML配置相關。
[圖片上傳失敗...(image-57d403-1705114166941)]
TransactionAspectSupport
該抽象父類是事務切面的基本處理類,實現了一些共有方法,如代碼【2】處determineTransactionManager(..)根據TransactionAttribute得到對應的PlatformTransactionManager,以及【3】處createTransactionIfNecessary創建事務,【4】處completeTransactionAfterThrowing回滾事務,【5】處commitTransactionAfterReturning提交事務等基本操作,底層同樣是委托PlatformTransactionManager進行處理的。這里主要看下事務的回滾操作,跟TransactionTemplate是有區別的:
protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.hasTransaction()) {
...
if (txInfo.transactionAttribute.rollbackOn(ex)) {【1】
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by rollback error", ex);
throw err;
}
}
else {
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
...
}
}
}
從【1】處的transactionAttribute.rollbackon(ex)可看出,事務屬性中的rollbackOn是在這里生效的,在發生指定異常時選擇回滾或提交,是用戶可配置的,而不像TransactionTemplate是固定的全部回滾。
2.TransactionProxyFactoryBean
該類是早期基于Bean的XML配置方式實現聲明式事務的核心類,之所以放在后面講,是因為該方式已不被推薦使用,先來看下定義:
/**
* Proxy factory bean for simplified declarative transaction handling.
* This is a convenient alternative to a standard AOP
* {@link org.springframework.aop.framework.ProxyFactoryBean}
* with a separate {@link TransactionInterceptor} definition.
*
* <p><strong>HISTORICAL NOTE:</strong> This class was originally designed to cover the
* typical case of declarative transaction demarcation: namely, wrapping a singleton
* target object with a transactional proxy, proxying all the interfaces that the target
* implements. However, in Spring versions 2.0 and beyond, the functionality provided here
* is superseded by the more convenient {@code tx:} XML namespace. See the <a
* >declarative transaction management</a> section of the
* Spring reference documentation to understand the modern options for managing
* transactions in Spring applications.
* ...
*/
public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean
implements BeanFactoryAware {
private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
private Pointcut pointcut;
...
protected Object createMainInterceptor() {
this.transactionInterceptor.afterPropertiesSet();
if (this.pointcut != null) {
return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);
}
else {
// Rely on default pointcut.
return new TransactionAttributeSourceAdvisor(this.transactionInterceptor);
}
}
}
源碼的聲明已經相當清晰,大致說明了該類的來龍去脈,忍不住直接貼上來了,感興趣可自行閱讀。這里主要是看下其實現思路:事務處理邏輯是委托給其成員TransactionInterceptor,而將事務邏輯織入目標類的工作則交由AbstractSingletonProxyFactoryBean來處理。FactoryBean是Spring中廣泛使用的用來定制一些較復雜Bean的實例化邏輯,因此從類名上就可看出,AbstractSingletonProxyFactoryBean的主要工作則是實例化并返回一個單例的Proxy對象。有了Proxy對象,織入的工作就輕而易舉了,此時TransactionInterceptor只是Proxy的眾多Advisor中的一個,最后由Proxy創建擁有了事務增強的代理對象即可。
以下是AbstractSingletonProxyFactoryBean中Proxy的實例化過程,全部在afterPropertiesSet中完成。其中的createMainInterceptor()是在其子類TransactionProxyFactoryBean中實現的,對應事務增強邏輯。
public abstract class AbstractSingletonProxyFactoryBean extends ProxyConfig
implements FactoryBean<Object>, BeanClassLoaderAware, InitializingBean {
...
public void afterPropertiesSet() {
if (this.target == null) {
throw new IllegalArgumentException("Property 'target' is required");
}
if (this.target instanceof String) {
throw new IllegalArgumentException("'target' needs to be a bean reference, not a bean name as value");
}
if (this.proxyClassLoader == null) {
this.proxyClassLoader = ClassUtils.getDefaultClassLoader();
}
ProxyFactory proxyFactory = new ProxyFactory();【1】
if (this.preInterceptors != null) {
for (Object interceptor : this.preInterceptors) {
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(interceptor));
}
}
// Add the main interceptor (typically an Advisor).
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor()));
if (this.postInterceptors != null) {
for (Object interceptor : this.postInterceptors) {
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(interceptor));
}
}
proxyFactory.copyFrom(this);
TargetSource targetSource = createTargetSource(this.target);
proxyFactory.setTargetSource(targetSource);
if (this.proxyInterfaces != null) {
proxyFactory.setInterfaces(this.proxyInterfaces);
}
else if (!isProxyTargetClass()) {
// Rely on AOP infrastructure to tell us what interfaces to proxy.
proxyFactory.setInterfaces(
ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass(), this.proxyClassLoader));
}
this.proxy = proxyFactory.getProxy(this.proxyClassLoader);
}
}
從代碼【1】處可以看到,ProxyFactory是創建Proxy對象的關鍵類,感興趣的童鞋可以跟進ProxyFactory的代碼,可發現最終創建Proxy對象的是DefaultAopProxyFactory,細節如下:根據config配置,選擇創建我們所熟知的兩種AopProxy:JDK的JdkDynamicAopProxy和Cglib的Cglib2AopProxy。
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
...
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
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.");
}
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
if (!cglibAvailable) {
throw new AopConfigException(
"Cannot proxy target class because CGLIB2 is not available. " +
"Add CGLIB to the class path or specify proxy interfaces.");
}
return CglibProxyFactory.createCglibProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
}
需要注意的細節
一、PROPAGATION_TESTED(嵌套事務)
當使用PROPAGATION_NESTED時,底層的數據源必須基于JDBC3.0。因為Spring所支持的嵌套事務,是基于事務保存點實現的(JTA除外),而保存點機制是從JDBC3.0才開始出現的。直接看AbstractPlatformTransactionManager中的處理代碼。對于通常的嵌套事務,會在當前所處父事務中創建保存點,然后進行子事務處理;對于JTA事務環境,則是采用嵌套的begin和commit/rollback調用來處理。
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
...
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'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
return status;
}
else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, null);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}
...
}
二、獲取數據連接資源
當脫離模板類,直接操作底層持久技術的原生API時,就需要通過Spring提供的資源工具類獲取線程綁定的資源,而不應該直接從DataSource或SessionFactory中獲取,否則容易造成數據連接泄露的問題。Spring為不同的持久化技術提供了一套從TransactionSynchronizationManager中獲取對應線程綁定資源的工具類:DataSourceUtils(Spring JDBC或iBatis)、SessionFactoryUtils(Hibernate 3.0)等。
三、如何標注@Transactional注解
雖然@Transactional注解可被應用于接口、接口方法、類及類的public方法,但建議在具體實現類上使用@Transactional注解,因為接口上的注解不能被繼承,這樣會有隱患(關于注解的繼承,可參考這里)。當事務配置按如下方式,使用的是子類代理(CGLib)而非接口代理(JDK)時,對應目標類不會添加事務增強!
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true" />
本文同步更新到此處