Skip to content

Commit 2b7a629

Browse files
committed
Support TransactionManagementConfigurer in the TCF
Currently the Spring TestContext Framework looks up a PlatformTransactionManager bean named "transactionManager". The exact name of the bean can be overridden via @TransactionConfiguration or @transactional; however, the bean will always be looked up 'by name'. The TransactionManagementConfigurer interface that was introduced in Spring 3.1 provides a programmatic approach to specifying the PlatformTransactionManager bean to be used for annotation-driven transaction management, and that bean is not required to be named "transactionManager". However, as of Spring 3.1.2, using the TransactionManagementConfigurer on a @configuration class has no effect on how the TestContext framework looks up the transaction manager. Consequently, if an explicit name or qualifier has not been specified, the bean must be named "transactionManager" in order for a transactional integration test to work. This commit addresses this issue by refactoring the TransactionalTestExecutionListener so that it looks up and delegates to a single TransactionManagementConfigurer as part of the algorithm for determining the transaction manager. Issue: SPR-9604
1 parent f21fe33 commit 2b7a629

11 files changed

+155
-44
lines changed

spring-test/src/main/java/org/springframework/test/context/transaction/TransactionConfiguration.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,19 @@
4040

4141
/**
4242
* The bean name of the {@link org.springframework.transaction.PlatformTransactionManager
43-
* PlatformTransactionManager} that is to be used to drive transactions.
43+
* PlatformTransactionManager} that should be used to drive transactions.
4444
*
45-
* <p>This attribute is not required and only needs to be specified explicitly
46-
* if there are multiple beans of type {@code PlatformTransactionManager} in
47-
* the test's {@code ApplicationContext} and the bean name of the desired
48-
* {@code PlatformTransactionManager} is not "transactionManager".
45+
* <p>This attribute is not required and only needs to be declared if there
46+
* are multiple beans of type {@code PlatformTransactionManager} in the test's
47+
* {@code ApplicationContext} <em>and</em> if one of the following is true.
48+
* <ul>
49+
* <li>the bean name of the desired {@code PlatformTransactionManager} is not
50+
* "transactionManager"</li>
51+
* <li>{@link org.springframework.transaction.annotation.TransactionManagementConfigurer
52+
* TransactionManagementConfigurer} was not implemented to specify which
53+
* {@code PlatformTransactionManager} bean should be used for annotation-driven
54+
* transaction management
55+
* </ul>
4956
*
5057
* <p><b>NOTE:</b> The XML {@code <tx:annotation-driven>} element also refers
5158
* to a bean named "transactionManager" by default. If you are using both

spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.springframework.transaction.TransactionException;
4444
import org.springframework.transaction.TransactionStatus;
4545
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
46+
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
4647
import org.springframework.transaction.interceptor.DelegatingTransactionAttribute;
4748
import org.springframework.transaction.interceptor.TransactionAttribute;
4849
import org.springframework.transaction.interceptor.TransactionAttributeSource;
@@ -68,11 +69,15 @@
6869
*
6970
* <p>Transactional commit and rollback behavior can be configured via the
7071
* class-level {@link TransactionConfiguration @TransactionConfiguration} and
71-
* method-level {@link Rollback @Rollback} annotations. In case there are multiple
72-
* instances of {@code PlatformTransactionManager} within the test's
73-
* {@code ApplicationContext}, {@code @TransactionConfiguration} supports
74-
* configuring the bean name of the {@code PlatformTransactionManager} that is
75-
* to be used to drive transactions.
72+
* method-level {@link Rollback @Rollback} annotations.
73+
*
74+
* <p>In case there are multiple instances of {@code PlatformTransactionManager}
75+
* within the test's {@code ApplicationContext}, @{@code TransactionConfiguration}
76+
* supports configuring the bean name of the {@code PlatformTransactionManager}
77+
* that should be used to drive transactions. Alternatively,
78+
* {@link TransactionManagementConfigurer} can be implemented in an
79+
* {@link org.springframework.context.annotation.Configuration @Configuration}
80+
* class.
7681
*
7782
* <p>When executing transactional tests, it is sometimes useful to be able to
7883
* execute certain <em>set up</em> or <em>tear down</em> code outside of a
@@ -349,13 +354,25 @@ protected final PlatformTransactionManager getTransactionManager(TestContext tes
349354
return bf.getBean(tmName, PlatformTransactionManager.class);
350355
}
351356

352-
// look up single bean by type
353357
if (bf instanceof ListableBeanFactory) {
354358
ListableBeanFactory lbf = (ListableBeanFactory) bf;
355-
Map<String, PlatformTransactionManager> beansOfType = BeanFactoryUtils.beansOfTypeIncludingAncestors(
359+
360+
// look up single bean by type
361+
Map<String, PlatformTransactionManager> txMgrs = BeanFactoryUtils.beansOfTypeIncludingAncestors(
356362
lbf, PlatformTransactionManager.class);
357-
if (beansOfType.size() == 1) {
358-
return beansOfType.values().iterator().next();
363+
if (txMgrs.size() == 1) {
364+
return txMgrs.values().iterator().next();
365+
}
366+
367+
// look up single TransactionManagementConfigurer
368+
Map<String, TransactionManagementConfigurer> configurers = BeanFactoryUtils.beansOfTypeIncludingAncestors(
369+
lbf, TransactionManagementConfigurer.class);
370+
if (configurers.size() > 1) {
371+
throw new IllegalStateException(
372+
"Only one TransactionManagementConfigurer may exist in the ApplicationContext");
373+
}
374+
if (configurers.size() == 1) {
375+
return configurers.values().iterator().next().annotationDrivenTransactionManager();
359376
}
360377
}
361378

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2002-2012 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.context.junit4.spr9604;
18+
19+
import static org.junit.Assert.*;
20+
21+
import org.junit.Test;
22+
import org.junit.runner.RunWith;
23+
import org.springframework.context.annotation.Bean;
24+
import org.springframework.context.annotation.Configuration;
25+
import org.springframework.test.context.ContextConfiguration;
26+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
27+
import org.springframework.test.context.transaction.AfterTransaction;
28+
import org.springframework.test.context.transaction.BeforeTransaction;
29+
import org.springframework.test.transaction.CallCountingTransactionManager;
30+
import org.springframework.transaction.PlatformTransactionManager;
31+
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
32+
import org.springframework.transaction.annotation.Transactional;
33+
34+
/**
35+
* Integration tests that verify the behavior requested in
36+
* <a href="https://jira.springsource.org/browse/SPR-9604">SPR-9604</a>.
37+
*
38+
* @author Sam Brannen
39+
* @since 3.2
40+
*/
41+
@RunWith(SpringJUnit4ClassRunner.class)
42+
@ContextConfiguration
43+
@Transactional
44+
public class LookUpTxMgrViaTransactionManagementConfigurerTests {
45+
46+
private static final CallCountingTransactionManager txManager1 = new CallCountingTransactionManager();
47+
private static final CallCountingTransactionManager txManager2 = new CallCountingTransactionManager();
48+
49+
50+
@Configuration
51+
static class Config implements TransactionManagementConfigurer {
52+
53+
public PlatformTransactionManager annotationDrivenTransactionManager() {
54+
return txManager1();
55+
}
56+
57+
@Bean
58+
public PlatformTransactionManager txManager1() {
59+
return txManager1;
60+
}
61+
62+
@Bean
63+
public PlatformTransactionManager txManager2() {
64+
return txManager2;
65+
}
66+
}
67+
68+
69+
@BeforeTransaction
70+
public void beforeTransaction() {
71+
txManager1.clear();
72+
txManager2.clear();
73+
}
74+
75+
@Test
76+
public void transactionalTest() {
77+
assertEquals(1, txManager1.begun);
78+
assertEquals(1, txManager1.inflight);
79+
assertEquals(0, txManager1.commits);
80+
assertEquals(0, txManager1.rollbacks);
81+
82+
assertEquals(0, txManager2.begun);
83+
assertEquals(0, txManager2.inflight);
84+
assertEquals(0, txManager2.commits);
85+
assertEquals(0, txManager2.rollbacks);
86+
}
87+
88+
@AfterTransaction
89+
public void afterTransaction() {
90+
assertEquals(1, txManager1.begun);
91+
assertEquals(0, txManager1.inflight);
92+
assertEquals(0, txManager1.commits);
93+
assertEquals(1, txManager1.rollbacks);
94+
95+
assertEquals(0, txManager2.begun);
96+
assertEquals(0, txManager2.inflight);
97+
assertEquals(0, txManager2.commits);
98+
assertEquals(0, txManager2.rollbacks);
99+
}
100+
101+
}

spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpNonexistentTxMgrTests.java

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
import org.springframework.context.annotation.Configuration;
2525
import org.springframework.test.context.ContextConfiguration;
2626
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
27-
import org.springframework.test.context.transaction.AfterTransaction;
28-
import org.springframework.test.context.transaction.BeforeTransaction;
2927
import org.springframework.test.transaction.CallCountingTransactionManager;
3028
import org.springframework.transaction.PlatformTransactionManager;
3129

@@ -42,7 +40,6 @@ public class LookUpNonexistentTxMgrTests {
4240

4341
private static final CallCountingTransactionManager txManager = new CallCountingTransactionManager();
4442

45-
4643
@Configuration
4744
static class Config {
4845

@@ -52,26 +49,11 @@ public PlatformTransactionManager transactionManager() {
5249
}
5350
}
5451

55-
56-
@BeforeTransaction
57-
public void beforeTransaction() {
58-
txManager.clear();
59-
}
60-
6152
@Test
62-
public void lookUpNothing() {
53+
public void nonTransactionalTest() {
6354
assertEquals(0, txManager.begun);
6455
assertEquals(0, txManager.inflight);
6556
assertEquals(0, txManager.commits);
6657
assertEquals(0, txManager.rollbacks);
6758
}
68-
69-
@AfterTransaction
70-
public void afterTransaction() {
71-
assertEquals(0, txManager.begun);
72-
assertEquals(0, txManager.inflight);
73-
assertEquals(0, txManager.commits);
74-
assertEquals(0, txManager.rollbacks);
75-
}
76-
7759
}

spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndDefaultNameTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public void beforeTransaction() {
6666
}
6767

6868
@Test
69-
public void lookUpByTypeAndDefaultName() {
69+
public void transactionalTest() {
7070
assertEquals(1, txManager1.begun);
7171
assertEquals(1, txManager1.inflight);
7272
assertEquals(0, txManager1.commits);

spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndNameTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public void beforeTransaction() {
6868
}
6969

7070
@Test
71-
public void lookUpByTypeAndName() {
71+
public void transactionalTest() {
7272
assertEquals(1, txManager1.begun);
7373
assertEquals(1, txManager1.inflight);
7474
assertEquals(0, txManager1.commits);

spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndQualifierAtClassLevelTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public void beforeTransaction() {
6666
}
6767

6868
@Test
69-
public void lookUpByTypeAndQualifier() {
69+
public void transactionalTest() {
7070
assertEquals(1, txManager1.begun);
7171
assertEquals(1, txManager1.inflight);
7272
assertEquals(0, txManager1.commits);

spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndQualifierAtMethodLevelTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public void beforeTransaction() {
6666

6767
@Transactional("txManager1")
6868
@Test
69-
public void lookUpByTypeAndQualifier() {
69+
public void transactionalTest() {
7070
assertEquals(1, txManager1.begun);
7171
assertEquals(1, txManager1.inflight);
7272
assertEquals(0, txManager1.commits);

spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public void beforeTransaction() {
5959
}
6060

6161
@Test
62-
public void lookUpByType() {
62+
public void transactionalTest() {
6363
assertEquals(1, txManager.begun);
6464
assertEquals(1, txManager.inflight);
6565
assertEquals(0, txManager.commits);

src/dist/changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Changes in version 3.2 M2 (2012-08-xx)
2929
* support content negotiation options in MVC namespace and MVC Java config
3030
* support named dispatchers in MockServletContext (SPR-9587)
3131
* support single, unqualified tx manager in the TestContext framework (SPR-9645)
32+
* support TransactionManagementConfigurer in the TestContext framework (SPR-9604)
3233

3334

3435
Changes in version 3.2 M1 (2012-05-28)

src/reference/docbook/testing.xml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -630,9 +630,9 @@ public class CustomTestExecutionListenerTests {
630630

631631
<para>Defines class-level metadata for configuring transactional
632632
tests. Specifically, the bean name of the
633-
<interfacename>PlatformTransactionManager</interfacename> that is
634-
to be used to drive transactions can be explicitly specified if
635-
there are multiple beans of type
633+
<interfacename>PlatformTransactionManager</interfacename> that
634+
should be used to drive transactions can be explicitly specified
635+
if there are multiple beans of type
636636
<interfacename>PlatformTransactionManager</interfacename> in the
637637
test's <interfacename>ApplicationContext</interfacename> and the
638638
bean name of the desired
@@ -657,9 +657,12 @@ public class CustomConfiguredTransactionalTests {
657657
configuration, you can avoid using
658658
<interfacename>@TransactionConfiguration</interfacename>
659659
altogether. In other words, if you have only one transaction
660-
manger (or your transaction manager bean is named
661-
"transactionManager") and if you want transactions to roll back
662-
automatically, there is no need to annotate your test class with
660+
manger — or if you have multiple transaction mangers but the
661+
transaction manager for tests is named "transactionManager" or
662+
specified via a
663+
<interfacename>TransactionManagementConfigurer</interfacename> —
664+
and if you want transactions to roll back automatically, then
665+
there is no need to annotate your test class with
663666
<interfacename>@TransactionConfiguration</interfacename>.</para>
664667
</note>
665668
</listitem>

0 commit comments

Comments
 (0)