分类: Java
2008-01-18 14:28:45
<---- 接3续 ---->
还有一点需要说明的是,AService的方法在执行之前创建的transactionstatus确实是通过这个方法创建的,但是,BService的方法在执行之前创建transactionstatus的方法就与这个不一样了,下面会有详解。
回顾了事务开始时所调用的方法之后,是不是觉得现在对spring如何处理事务越来越清晰了呢。由于这么几个方法的调用,每个方法入栈之前它的事务状态就已经被设置好了。这个事务状态就是为了在方法出栈时被调用而准备的。
让我们再次回到BService中的方法出栈的那个时间段,看看spring都做了些什么,我们知道,后入栈的肯定是先出栈,BService中的方法后入栈,拿它肯定要先出栈了,它出栈的时候是要判断是否要提交事务,释放资源的,让我们来看看TransactionInterceptor的invoke的最后那个方法doCommitTransactionAfterReturning:
代码
1. protected void doCommitTransactionAfterReturning(TransactionInfo txInfo) {
2. if (txInfo != null && txInfo.hasTransaction()) {
3. if (logger.isDebugEnabled()) {
4. logger.debug("Invoking commit for transaction on " + txInfo.joinpointIdentification());
5. }
6. this.transactionManager.commit(txInfo.getTransactionStatus()); //瞧:提交事务时用到了表明事务状态的那个TransactionStatus对象了。
7. }
8. }
看这个方法的名字就知道spring是要在业务方法出栈时提交事务,貌似很简单,但是事实是这样的吗? 我们接着往下看。
代码
1. public final void commit(TransactionStatus status) throws TransactionException {
2. DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
3.
4. if (defStatus.isCompleted()) {
5. throw new IllegalTransactionStateException(
6. "Transaction is already completed - do not call commit or rollback more than once per transaction");
7. }
8. if (defStatus.isLocalRollbackOnly()) {
9. if (defStatus.isDebug()) {
10. logger.debug("Transactional code has requested rollback");
11. }
12. processRollback(defStatus);
13. return;
14. }
15. if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
16. if (defStatus.isDebug()) {
17. logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
18. }
19. processRollback(defStatus);
20. throw new UnexpectedRollbackException(
21. "Transaction has been rolled back because it has been marked as rollback-only");
22. }
23.
24. processCommit(defStatus);
25. }
上面这段代码就是transactionmanager中的commit,但是看上去,它又把自己的职责分配给别人了,从代码里我们看到,如果事务已经结束了就抛异常,如果事务是rollbackonly的,那么就rollback吧,但是按照正常流程,我们还是想来看一下,事务的提交,就是processCommit(status)这个方法吧。
代码
1. private void processCommit(DefaultTransactionStatus status) throws TransactionException {
2. try {
3. boolean beforeCompletionInvoked = false;
4. try {
5. triggerBeforeCommit(status);
6. triggerBeforeCompletion(status);
7. beforeCompletionInvoked = true;
8. if (status.hasSavepoint()) {
9. if (status.isDebug()) {
10. logger.debug("Releasing transaction savepoint");
11. }
12. status.releaseHeldSavepoint();
13. }
14. else if (status.isNewTransaction()) {//这个判断非常重要,下面会详细讲解这个判断的作用
15. if (status.isDebug()) {
16. logger.debug("Initiating transaction commit");
17. }
18. boolean globalRollbackOnly = status.isGlobalRollbackOnly();
19. doCommit(status);
20. // Throw UnexpectedRollbackException if we have a global rollback-only
21. // marker but still didn't get a corresponding exception from commit.
22. `````````````````````
23. }
我们注意到,在判断一个事务是否是新事务之前还有一个status.hasSavepoint()的判断,我认为这个判断事实上就是嵌套事务的判断,即判断这个事务是否是嵌套事务,如果不是嵌套事务,则再判断它是否是一个新事务,下面这段话就非常重要了,BService的中的方法是先出栈的,也就是说在调用BService之前的创建的那个事务状态对象在这里要先被判断,但是由于在调用BService的方法之前已经创建了一个Transaction和Session(假设我们使用的是hibernate3),这时候在创建第二个TransactionInfo(再强调一下吧,TransactionInfo并不是Transaction,Transaction是真正的事务对象,TransactionInfo只不过是一个辅助类而已,用来记录一系列状态的辅助类)的TransactionStatus的时候就会进入下面这个方法(当然在这之前会判断一下当前线程中是否已经有了一个SessionHolder对象,不清楚SessionHolder作用的同学情况第一篇文章),这个方法其实应该放到第一篇文章中讲的,但是想到如果不讲事务提交就讲这个方法好像没有这么贴切,废话少说,我们来看一下吧:
代码
1. private TransactionStatus handleExistingTransaction(
2. TransactionDefinition definition, Object transaction, boolean debugEnabled)
3. throws TransactionException {
4.
5. if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
6. throw new IllegalTransactionStateException(
7. "Transaction propagation 'never' but existing transaction found");
8. }
9.
10. if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
11. if (debugEnabled) {
12. logger.debug("Suspending current transaction");
13. }
14. Object suspendedResources = suspend(transaction);
15. boolean newSynchronization = (this.transactionSynchronization == SYNCHRONIZATION_ALWAYS);
16. return newTransactionStatus(
17. definition, null, false, newSynchronization, debugEnabled, suspendedResources);
18. }
19.
20. if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
21. if (debugEnabled) {
22. logger.debug("Suspending current transaction, creating new transaction with name [" +
23. definition.getName() + "]");
24. }
25. Object suspendedResources = suspend(transaction);
26. doBegin(transaction, definition);
27. boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);
28. return newTransactionStatus(
29. definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
30. }
31.
32. if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
33. if (!isNestedTransactionAllowed()) {
34. throw new NestedTransactionNotSupportedException(
35. "Transaction manager does not allow nested transactions by default - " +
36. "specify 'nestedTransactionAllowed' property with value 'true'");
37. }
38. if (debugEnabled) {
39. logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
40. }
41. if (useSavepointForNestedTransaction()) {
42. // Create savepoint within existing Spring-managed transaction,
43. // through the SavepointManager API implemented by TransactionStatus.
44. // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
45. DefaultTransactionStatus status =
46. newTransactionStatus(definition, transaction, false, false, debugEnabled, null);
47. status.createAndHoldSavepoint();
48. return status;
49. }
50. else {
51. // Nested transaction through nested begin and commit/rollback calls.
52. // Usually only for JTA: Spring synchronization might get activated here
53. // in case of a pre-existing JTA transaction.
54. doBegin(transaction, definition);
55. boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);
56. return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);
57. }
58. }
59.
60. // Assumably PROPAGATION_SUPPORTS.
61. if (debugEnabled) {
62. logger.debug("Participating in existing transaction");
63. }
64. boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);
65. return newTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
66. }
我们看到这个方法其实很明了,就是什么样的传播途径就创建什么样的transactionstatus,这个方法是在事务开始时被调用的,拿到我们之前举的例子中来看下,我们就恍然大悟了,原来,如果之前已经创建过事务,那个这个新建的transactionstauts就不应该是属于一个newTransaction了,所以第3个参数就是false了。
也就是说,在BService的方法出栈要要执行processcommit,但是由于BService的那个TransactionStatus不是一个newTransaction,所以它根本不会触发这个动作:
代码
1. else if (status.isNewTransaction()) {//这个判断非常重要,下面会详细讲解这个判断的作用
2. if (status.isDebug()) {
3. logger.debug("Initiating transaction commit");
4. }
5. boolean globalRollbackOnly = status.isGlobalRollbackOnly();
6. doCommit(status);
7. }
也就是说在BService的方法出栈后,事务是不会提交的。这完全符合propragation_required的模型。 而在AService的方法出栈后,AService的方法所对应的那个TransactionStatus对象的newTransaction属性是为true的,即它会触发上面这段代码,进行真正的事务提交。让我们回想一下AService方法入栈之前创建TransactionStatus对象的情形吧:
newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);看到第3个参数为true没有。 那么事务该提交了吧,事务的提交我想使用过hibernate的人都知道怎么提交了:
txObject.getSessionHolder().getTransaction().commit();
从当前线程中拿到SessionHolder,再拿到开始事务的那个Transaction对象,然后再commit事务。在没有用spring之前,我们经常这么做。呵呵。
好吧,我已经说到了spring声明式事务管理的70%到80%的内容了,这70%到80%的内容看上去还是非常容易理解的,如果把这两篇文章认真看过,我相信会有所收获的,剩下的内容需要靠大家自己去挖掘了,因为另剩下的内容可是需要花费很多时间的,因为牵扯的东西实在是太多了,呵呵。最后祝大家阅读愉快,因为我的文笔实在是让大家的眼睛受罪了。
chinaunix网友2009-10-27 00:23:07
详细研究了下仁兄的代码,有个疑问就是Spring自动事务管理时对事务范畴之内的业务方法运行之前会自动创建一个session后再创建一个transaction,之后会自动打开事务,待业务方法运行完成之后事务会自动提交,但此时session好像并未提交,先前打开的session续人工手动关闭吗