在Spring中,聲明式事務是通過事務屬性(transaction attribute)來定義的。事務屬性描述了事務策略如何應用到方法上。事務屬性包含5個方面:
- 傳播行爲
- 隔離級別
- 是否只讀
- 事務超時
- 回滾規則
<tx:advice id="txAdviceHibernate" transaction-manager="txManagerHibernate">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" rollback-for="Exception" no-rollback-for="" />
<tx:method name="batch*" propagation="REQUIRED" rollback-for="Exception" />
<tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception" />
<tx:method name="execute*" propagation="REQUIRED" rollback-for="Exception" />
<tx:method name="export*" propagation="REQUIRED" rollback-for="Exception" />
<tx:method name="import*" propagation="REQUIRED" rollback-for="Exception" />
<tx:method name="insert*" propagation="REQUIRED" rollback-for="Exception" />
<tx:method name="save*" propagation="REQUIRED" rollback-for="Exception" />
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception" />
<tx:method name="count*" propagation="NOT_SUPPORTED" read-only="true" />
<tx:method name="find*" propagation="NOT_SUPPORTED" read-only="true" />
<tx:method name="get*" propagation="NOT_SUPPORTED" read-only="true" />
<tx:method name="list*" propagation="NOT_SUPPORTED" read-only="true" />
<tx:method name="load*" propagation="NOT_SUPPORTED" read-only="true" />
<tx:method name="page*" propagation="NOT_SUPPORTED" read-only="true" />
<tx:method name="query*" propagation="NOT_SUPPORTED" read-only="true" />
</tx:attributes>
</tx:advice>
<!--此處應想辦法實現動態加載-->
<aop:config>
<aop:advisor pointcut="execution(* com.cetc.datamc.app.collect.bs.*.*(..))
or execution(* com.cetc.datamc.app.mdms.bs.*.*(..))" advice-ref="txAdviceHibernate" />
</aop:config>
<tx:advice id="txAdviceHibernate" transaction-manager="txManager">
...
</tx:advice>
<tx:advice>只是定義了AOP通知,用於把事務邊界通知給方法。但是這只是事務通知,而不是完整的事務性切面。我們在<tx:advice>中沒有聲明哪些Bean應該被通知——我們需要一個切點來做這件事。爲了完整定義事務性切面,我們必須定義一個通知器(advisor)。這就涉及aop命名空間了。通過上面的配置示例可以看到:public interface IDataCollect {
public String addDataItem(String data);
}
public class DataCollectService implements IDataCollect {
private IMetadataService metaService;
public void setMetaService(IMetadataService metaService) {
this.metaService = metaService;
}
public String addDataItem(String data) {
metaService.deleteMeta(data);
return metaService.addMeta(data);
}
}
public interface IMetadataService {
public void deleteMeta(String data);
public String addMeta(String data);
public String queryMetaId(String data);
}
public class MetadataService implements IMetadataService {
public void deleteMeta(String data) {
System.out.println("delete metadata");
}
public String addMeta(String data) {
System.out.println("add metadata");
String id = queryMetaId(data);
return data;
}
public String queryMetaId(String data) {
return "id";
}
}
這樣,在DataCollectService中有一個IMetadataService類型的對象。那麼在實際中該類型則是被Spring替換爲動態代理對象,而不是原來的MetaService對象。所以調用了IMetadataService中的deleteMeta和addMeta方法,這兩個方法都是調用的織入事務切面代碼的函數。而不是原來的deleteMeta和addMeta方法。/**
* General delegate for around-advice-based subclasses, delegating to several other template
* methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager}
* as well as regular {@link PlatformTransactionManager} implementations.
* @param method the Method being invoked
* @param targetClass the target class that we're invoking the method on
* @param invocation the callback to use for proceeding with the target invocation
* @return the return value of the method, if any
* @throws Throwable propagated from the target invocation
*/
protected Object invokeWithinTransaction(Method method, Class targetClass,
final InvocationCallback invocation)
throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
//這裏返回的即是在tx:method中配置的屬性值
//包括propagation, isolation, readonly, timeout, readonly等屬性的配置值。
//如果一個方法沒有在tx:method中配置,那麼txAttr爲null
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
//該方法主要就是根據配置來採取合適的事務策略。
//理解了該方法的調用原理,基本上spring的配置事務的各屬性的涵義也就理解了
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
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.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
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.proceedWithInvocation();
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
return new ThrowableHolder(ex);
}
}
finally {
cleanupTransactionInfo(txInfo);
}
}
});
// Check result: It might indicate a Throwable to rethrow.
if (result instanceof ThrowableHolder) {
throw ((ThrowableHolder) result).getThrowable();
}
else {
return result;
}
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
}
}
/**
* Create a transaction if necessary based on the given TransactionAttribute.
* <p>Allows callers to perform custom TransactionAttribute lookups through
* the TransactionAttributeSource.
* @param txAttr the TransactionAttribute (may be {@code null})
* @param joinpointIdentification the fully qualified method name
* (used for monitoring and logging purposes)
* @return a TransactionInfo object, whether or not a transaction was created.
* The {@code hasTransaction()} method on TransactionInfo can be used to
* tell if there was a transaction created.
* @see #getTransactionAttributeSource()
*/
@SuppressWarnings("serial")
protected TransactionInfo createTransactionIfNecessary(
PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {
// If no name specified, apply method identification as transaction name.
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
//重點看此函數
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
//如果沒有在tx:method中配置,那麼txAttr=null, 這樣返回的TransactionStatus也爲null
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
/**
* This implementation handles propagation behavior. Delegates to
* {@code doGetTransaction}, {@code isExistingTransaction}
* and {@code doBegin}.
* @see #doGetTransaction
* @see #isExistingTransaction
* @see #doBegin
*/
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
//doGetTransaction在各繼承該類的子類中實現。
//這裏假設使用的是Hibernate的事務,那麼實現類是HibernateTransactionManager類
//這裏的object即表示我們通常所說的Transaction,即事務的類的定義
//對於Hibernate Transaction的具體類是HibernateTransactionObject
Object transaction = doGetTransaction();
// Cache debug flag to avoid repeated checks.
boolean debugEnabled = logger.isDebugEnabled();
if (definition == null) {
// Use defaults if no transaction definition given.
definition = new DefaultTransactionDefinition();
}
//根據得到的transaction對象判斷是否當前已經存在事務
//如果存在事務,那麼調用handleExistingTransaction來根據配置的策略處理事務
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(definition, transaction, debugEnabled);
}
// Check definition settings for new transaction.
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
//如果當前不存在事務,那麼根據不同的策略配置來產生具體的事務行爲。
//這裏即是各種不同配置策略行爲的具體代碼實現過程
// No existing transaction found -> check propagation behavior to find out how to proceed.
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);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
}
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
//開啓新事務
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException ex) {
resume(null, suspendedResources);
throw ex;
}
catch (Error err) {
resume(null, suspendedResources);
throw err;
}
}
else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}
下面我們暫時先拋開如何doGetTransaction以及如何判斷isExistingTransaction不談,先來看看如何doBegin一個新事務。@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
throw new IllegalTransactionStateException(
"Pre-bound JDBC Connection found! HibernateTransactionManager does not support " +
"running within DataSourceTransactionManager if told to manage the DataSource itself. " +
"It is recommended to use a single HibernateTransactionManager for all transactions " +
"on a single DataSource, no matter whether Hibernate or JDBC access.");
}
Session session = null;
try {
//如果當前txObject沒有已有的Session,那麼打開一個新的Session.
//txObject也可能已經有了Session,
//例如如果配置了OpenSessionInViewFilter,且當前的線程是一個request線程,
//那麼就會在每個request線程中就先openSession,並且綁定到當前線程中。
//這樣在doGetTransaction就會獲取到該Session。詳見doGetTransaction函數和OpenSessionInViewFilter實現
if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
Interceptor entityInterceptor = getEntityInterceptor();
//這裏是openSession,而不是getCurrentSession
Session newSession = (entityInterceptor != null ?
getSessionFactory().openSession(entityInterceptor) : getSessionFactory().openSession());
if (logger.isDebugEnabled()) {
logger.debug("Opened new Session [" + SessionFactoryUtils.toString(newSession) +
"] for Hibernate transaction");
}
//設置newSessionHolder和newSession狀態爲true
txObject.setSession(newSession);
}
session = txObject.getSessionHolder().getSession();
if (this.prepareConnection && isSameConnectionForEntireSession(session)) {
// We're allowed to change the transaction settings of the JDBC Connection.
if (logger.isDebugEnabled()) {
logger.debug(
"Preparing JDBC Connection of Hibernate Session [" + SessionFactoryUtils.toString(session) + "]");
}
Connection con = session.connection();
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
}
else {
// Not allowed to change the transaction settings of the JDBC Connection.
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
// We should set a specific isolation level but are not allowed to...
throw new InvalidIsolationLevelException(
"HibernateTransactionManager is not allowed to support custom isolation levels: " +
"make sure that its 'prepareConnection' flag is on (the default) and that the " +
"Hibernate connection release mode is set to 'on_close' (SpringTransactionFactory's default). " +
"Make sure that your LocalSessionFactoryBean actually uses SpringTransactionFactory: Your " +
"Hibernate properties should *not* include a 'hibernate.transaction.factory_class' property!");
}
if (logger.isDebugEnabled()) {
logger.debug(
"Not preparing JDBC Connection of Hibernate Session [" + SessionFactoryUtils.toString(session) + "]");
}
}
//如果是一個new Session並且事務是隻讀的,則設置該Session flushMode爲FlushMode.MANUAL
if (definition.isReadOnly() && txObject.isNewSession()) {
// Just set to MANUAL in case of a new Session for this transaction.
session.setFlushMode(FlushMode.MANUAL);
}
//如果事務不是隻讀的,且已有一個Session時,重新設置該Session的flushMode
if (!definition.isReadOnly() && !txObject.isNewSession()) {
// We need AUTO or COMMIT for a non-read-only transaction.
FlushMode flushMode = session.getFlushMode();
if (flushMode.lessThan(FlushMode.COMMIT)) {
session.setFlushMode(FlushMode.AUTO);
txObject.getSessionHolder().setPreviousFlushMode(flushMode);
}
}
Transaction hibTx;
//設置timeout 然後beginTransaction
// Register transaction timeout.
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
// Use Hibernate's own transaction timeout mechanism on Hibernate 3.1+
// Applies to all statements, also to inserts, updates and deletes!
hibTx = session.getTransaction();
hibTx.setTimeout(timeout);
hibTx.begin();
}
else {
// Open a plain Hibernate transaction without specified timeout.
hibTx = session.beginTransaction();
}
// Add the Hibernate transaction to the session holder.
txObject.getSessionHolder().setTransaction(hibTx);
// Register the Hibernate Session's JDBC Connection for the DataSource, if set.
if (getDataSource() != null) {
Connection con = session.connection();
ConnectionHolder conHolder = new ConnectionHolder(con);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
conHolder.setTimeoutInSeconds(timeout);
}
if (logger.isDebugEnabled()) {
logger.debug("Exposing Hibernate transaction as JDBC transaction [" + con + "]");
}
//將當前的conHolder綁定到線程上,可以用來判斷當前線程是否已有事務
TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
txObject.setConnectionHolder(conHolder);
}
// Bind the session holder to the thread.
if (txObject.isNewSessionHolder()) {
//將當前的SessionHolder綁定到當前線程上,可以用來判斷當前線程是否已有事務
TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());
}
txObject.getSessionHolder().setSynchronizedWithTransaction(true);
}
catch (Throwable ex) {
if (txObject.isNewSession()) {
try {
if (session.getTransaction().isActive()) {
session.getTransaction().rollback();
}
}
catch (Throwable ex2) {
logger.debug("Could not rollback Session after failed transaction begin", ex);
}
finally {
SessionFactoryUtils.closeSession(session);
txObject.setSessionHolder(null);
}
}
throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);
}
}
好了,知道了doBegin函數的實現原理,讓我們回過頭來看doGetTransaction和isExistingTransaction函數的實現。這樣就很容易理解了。@Override
protected Object doGetTransaction() {
HibernateTransactionObject txObject = new HibernateTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
//獲取當前線程中是否已經有已綁定的SessionHolder。如果當前線程已經存在事務,那麼就會存在SessionHolder
//具體可見doBegin函數的實現
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
if (sessionHolder != null) {
if (logger.isDebugEnabled()) {
logger.debug("Found thread-bound Session [" +
SessionFactoryUtils.toString(sessionHolder.getSession()) + "] for Hibernate transaction");
}
//如果已經有了SessionHolder,那麼直接就把當前已有的SessionHolder賦給新的transaction Object
txObject.setSessionHolder(sessionHolder);
}
else if (this.hibernateManagedSession) {
try {
//當前線程中已經綁定了session,例如openSessionInViewFilter的情況
Session session = getSessionFactory().getCurrentSession();
if (logger.isDebugEnabled()) {
logger.debug("Found Hibernate-managed Session [" +
SessionFactoryUtils.toString(session) + "] for Spring-managed transaction");
}
txObject.setExistingSession(session);
}
catch (HibernateException ex) {
throw new DataAccessResourceFailureException(
"Could not obtain Hibernate-managed Session for Spring-managed transaction", ex);
}
}
//如果當前線程已經綁定了ConnectionHolder,如果當前線程中已經開啓了事務,就會存在ConnectionHolder
//直接將已有的ConnectionHolder賦值給該transaction
if (getDataSource() != null) {
ConnectionHolder conHolder = (ConnectionHolder)
TransactionSynchronizationManager.getResource(getDataSource());
txObject.setConnectionHolder(conHolder);
}
return txObject;
}
@Override
protected boolean isExistingTransaction(Object transaction) {
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
//spring管理的事務或者是Hibernate自己管理的事務
return (txObject.hasSpringManagedTransaction() ||
(this.hibernateManagedSession && txObject.hasHibernateManagedTransaction()));
}
public boolean hasSpringManagedTransaction() {
//如果當前的transaction Object的sessionHolder不爲null,說明當前存在事務
return (this.sessionHolder != null && this.sessionHolder.getTransaction() != null);
}
hasHibernateManagedTransaction函數實現如下:public boolean hasHibernateManagedTransaction() {
return this.sessionHolder != null && this.sessionHolder.getSession().getTransaction().isActive();
}
這兩個函數都很簡單,結合doGetTransaction函數的實現原理,就很容易理解了。/**
* Create a TransactionStatus for an existing transaction.
*/
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
//配置的策略爲當前方法不應該運行在事務上下文中,如果當前正有一個事務運行,則會拋出異常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
//配置的策略爲當前方法不應該運行在事務中,如果存在當前事務,在該方法運行期間,當前事務將被掛起
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
//配置的策略爲當前方法必須運行在它自己的事務中,一個新的事務將被啓動。
//如果存在當前事務,在該方法運行期間,當前事務將被掛起
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
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;
}
catch (RuntimeException beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
catch (Error beginErr) {
resumeAfterBeginException(transaction, suspendedResources, beginErr);
throw beginErr;
}
}
//配置的策略爲如果當前已經存在一個事務,那麼該方法將會在嵌套事務中運行。
//嵌套的事務可以獨立於當前事務進行單獨地提交或回滾。如果當前事務不存在,
//那麼其行爲與PROPAGATION_REQUIRED一樣
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;
}
}
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
if (isValidateExistingTransaction()) {
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
Constants isoConstants = DefaultTransactionDefinition.constants;
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] specifies isolation level which is incompatible with existing transaction: " +
(currentIsolationLevel != null ?
isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
"(unknown)"));
}
}
if (!definition.isReadOnly()) {
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] is not marked as read-only but existing transaction is");
}
}
}
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}