提交 f21fe33e 编写于 作者: S Sam Brannen

Support single, unqualified tx manager in the TCF

TransactionalTestExecutionListener currently requires that the
PlatformTransactionManager bean be named "transactionManager" by
default. Otherwise, the bean name can only be overridden via the
transactionManager attribute of @TransactionConfiguration or the value
attribute of @Transactional.

However, if there is only a single PlatformTransactionManager in the
test's ApplicationContext, then the requirement to specify the exact
name of that bean (or to name it exactly "transactionManager") is often
superfluous.

This commit addresses this issue by refactoring the
TransactionalTestExecutionListener so that it is comparable to the
algorithm for determining the transaction manager used in
TransactionAspectSupport for "production" code. Specifically, the TTEL
now uses the following algorithm to retrieve the transaction manager.

 - look up by type and qualifier from @Transactional
 - else, look up by type and explicit name from
   @TransactionConfiguration
 - else, look up single bean by type
 - else, look up by type and default name from @TransactionConfiguration

Issue: SPR-9645
上级 c0b4e368
/* /*
* Copyright 2002-2010 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -23,17 +23,14 @@ import java.lang.annotation.Retention; ...@@ -23,17 +23,14 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.transaction.PlatformTransactionManager;
/** /**
* TransactionConfiguration defines class-level metadata for configuring * {@code TransactionConfiguration} defines class-level metadata for configuring
* transactional tests. * transactional tests.
* *
* @author Sam Brannen * @author Sam Brannen
* @since 2.5 * @since 2.5
* @see ContextConfiguration
* @see TransactionalTestExecutionListener * @see TransactionalTestExecutionListener
* @see org.springframework.test.context.ContextConfiguration
*/ */
@Documented @Documented
@Inherited @Inherited
...@@ -42,15 +39,19 @@ import org.springframework.transaction.PlatformTransactionManager; ...@@ -42,15 +39,19 @@ import org.springframework.transaction.PlatformTransactionManager;
public @interface TransactionConfiguration { public @interface TransactionConfiguration {
/** /**
* The bean name of the {@link PlatformTransactionManager} that is to be * The bean name of the {@link org.springframework.transaction.PlatformTransactionManager
* used to drive transactions. This attribute is not required and only needs * PlatformTransactionManager} that is to be used to drive transactions.
* to be specified explicitly if the bean name of the desired *
* PlatformTransactionManager is not &quot;transactionManager&quot;. * <p>This attribute is not required and only needs to be specified explicitly
* <p><b>NOTE:</b> The XML <code>&lt;tx:annotation-driven&gt;</code> element * if there are multiple beans of type {@code PlatformTransactionManager} in
* also refers to a bean named "transactionManager" by default. If you are * the test's {@code ApplicationContext} and the bean name of the desired
* using both features in combination, make sure to point to the same * {@code PlatformTransactionManager} is not "transactionManager".
* transaction manager bean - here in <code>@TransactionConfiguration</code> and *
* also in <code>&lt;tx:annotation-driven transaction-manager="..."&gt;</code>. * <p><b>NOTE:</b> The XML {@code <tx:annotation-driven>} element also refers
* to a bean named "transactionManager" by default. If you are using both
* features in combination, make sure to point to the same transaction manager
* bean - here in {@code @TransactionConfiguration} and also in
* {@code <tx:annotation-driven transaction-manager="...">}.
*/ */
String transactionManager() default "transactionManager"; String transactionManager() default "transactionManager";
......
...@@ -19,7 +19,6 @@ package org.springframework.test.context.transaction; ...@@ -19,7 +19,6 @@ package org.springframework.test.context.transaction;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
...@@ -30,7 +29,10 @@ import org.apache.commons.logging.Log; ...@@ -30,7 +29,10 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.test.annotation.NotTransactional; import org.springframework.test.annotation.NotTransactional;
import org.springframework.test.annotation.Rollback; import org.springframework.test.annotation.Rollback;
...@@ -49,37 +51,35 @@ import org.springframework.util.ReflectionUtils; ...@@ -49,37 +51,35 @@ import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
* <p> * {@code TestExecutionListener} that provides support for executing tests
* <code>TestExecutionListener</code> that provides support for executing * within transactions by honoring the
* tests within transactions by using the
* {@link org.springframework.transaction.annotation.Transactional &#064;Transactional} * {@link org.springframework.transaction.annotation.Transactional &#064;Transactional}
* and {@link NotTransactional &#064;NotTransactional} annotations. * and {@link NotTransactional &#064;NotTransactional} annotations. Expects a
* </p> * {@link PlatformTransactionManager} bean to be defined in the Spring
* <p> * {@link ApplicationContext} for the test.
* Changes to the database during a test that is run with &#064;Transactional will be *
* run within a transaction that will, by default, be automatically * <p>Changes to the database during a test that is run with {@code @Transactional}
* will be run within a transaction that will, by default, be automatically
* <em>rolled back</em> after completion of the test; whereas, changes to the * <em>rolled back</em> after completion of the test; whereas, changes to the
* database during a test that is run with &#064;NotTransactional will <strong>not</strong> * database during a test that is run with {@code @NotTransactional} will
* be run within a transaction. Test methods that are not annotated with either * <strong>not</strong> be run within a transaction. Test methods that are not
* &#064;Transactional (at the class or method level) or &#064;NotTransactional * annotated with either {@code @Transactional} (at the class or method level)
* will not be run within a transaction. * or {@code @NotTransactional} will not be run within a transaction.
* </p> *
* <p> * <p>Transactional commit and rollback behavior can be configured via the
* Transactional commit and rollback behavior can be configured via the * class-level {@link TransactionConfiguration @TransactionConfiguration} and
* class-level {@link TransactionConfiguration &#064;TransactionConfiguration} and * method-level {@link Rollback @Rollback} annotations. In case there are multiple
* method-level {@link Rollback &#064;Rollback} annotations. * instances of {@code PlatformTransactionManager} within the test's
* {@link TransactionConfiguration &#064;TransactionConfiguration} also provides * {@code ApplicationContext}, {@code @TransactionConfiguration} supports
* configuration of the bean name of the {@link PlatformTransactionManager} that * configuring the bean name of the {@code PlatformTransactionManager} that is
* is to be used to drive transactions. * to be used to drive transactions.
* </p> *
* <p> * <p>When executing transactional tests, it is sometimes useful to be able to
* When executing transactional tests, it is sometimes useful to be able to execute * execute certain <em>set up</em> or <em>tear down</em> code outside of a
* certain <em>set up</em> or <em>tear down</em> code outside of a * transaction. {@code TransactionalTestExecutionListener} provides such
* transaction. <code>TransactionalTestExecutionListener</code> provides such
* support for methods annotated with * support for methods annotated with
* {@link BeforeTransaction &#064;BeforeTransaction} and * {@link BeforeTransaction @BeforeTransaction} and
* {@link AfterTransaction &#064;AfterTransaction}. * {@link AfterTransaction @AfterTransaction}.
* </p>
* *
* @author Sam Brannen * @author Sam Brannen
* @author Juergen Hoeller * @author Juergen Hoeller
...@@ -96,25 +96,33 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis ...@@ -96,25 +96,33 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
private static final Log logger = LogFactory.getLog(TransactionalTestExecutionListener.class); private static final Log logger = LogFactory.getLog(TransactionalTestExecutionListener.class);
private static final String DEFAULT_TRANSACTION_MANAGER_NAME = (String) AnnotationUtils.getDefaultValue(
TransactionConfiguration.class, "transactionManager");
private static final Boolean DEFAULT_DEFAULT_ROLLBACK = (Boolean) AnnotationUtils.getDefaultValue(
TransactionConfiguration.class, "defaultRollback");
protected final TransactionAttributeSource attributeSource = new AnnotationTransactionAttributeSource(); protected final TransactionAttributeSource attributeSource = new AnnotationTransactionAttributeSource();
private final Map<Method, TransactionContext> transactionContextCache =
Collections.synchronizedMap(new IdentityHashMap<Method, TransactionContext>());
private TransactionConfigurationAttributes configurationAttributes; private TransactionConfigurationAttributes configurationAttributes;
private volatile int transactionsStarted = 0; private volatile int transactionsStarted = 0;
private final Map<Method, TransactionContext> transactionContextCache = Collections.synchronizedMap(new IdentityHashMap<Method, TransactionContext>());
/** /**
* If the test method of the supplied {@link TestContext test context} is * If the test method of the supplied {@link TestContext test context} is
* configured to run within a transaction, this method will run * configured to run within a transaction, this method will run
* {@link BeforeTransaction &#064;BeforeTransaction methods} and start a new * {@link BeforeTransaction &#064;BeforeTransaction methods} and start a new
* transaction. * transaction.
* <p>Note that if a {@link BeforeTransaction &#064;BeforeTransaction method} fails, * <p>Note that if a {@code BeforeTransaction &#064;BeforeTransaction method} fails,
* remaining {@link BeforeTransaction &#064;BeforeTransaction methods} will not * remaining {@code BeforeTransaction &#064;BeforeTransaction methods} will not
* be invoked, and a transaction will not be started. * be invoked, and a transaction will not be started.
* @see org.springframework.transaction.annotation.Transactional * @see org.springframework.transaction.annotation.Transactional
* @see org.springframework.test.annotation.NotTransactional * @see org.springframework.test.annotation.NotTransactional
* @see #getTransactionManager(TestContext, String)
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
@Override @Override
...@@ -131,36 +139,27 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis ...@@ -131,36 +139,27 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
return; return;
} }
PlatformTransactionManager tm = null;
TransactionAttribute transactionAttribute = this.attributeSource.getTransactionAttribute(testMethod, TransactionAttribute transactionAttribute = this.attributeSource.getTransactionAttribute(testMethod,
testContext.getTestClass()); testContext.getTestClass());
TransactionDefinition transactionDefinition = null;
if (transactionAttribute != null) { if (transactionAttribute != null) {
transactionDefinition = new DelegatingTransactionAttribute(transactionAttribute) { transactionAttribute = new DelegatingTransactionAttribute(transactionAttribute) {
public String getName() { public String getName() {
return testMethod.getName(); return testMethod.getName();
} }
}; };
}
if (transactionDefinition != null) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Explicit transaction definition [" + transactionDefinition + "] found for test context [" logger.debug("Explicit transaction definition [" + transactionAttribute + "] found for test context "
+ testContext + "]"); + testContext);
} }
String qualifier = transactionAttribute.getQualifier(); tm = getTransactionManager(testContext, transactionAttribute.getQualifier());
PlatformTransactionManager tm; }
if (StringUtils.hasLength(qualifier)) {
// Use autowire-capable factory in order to support extended if (tm != null) {
// qualifier matching (only exposed on the internal BeanFactory, TransactionContext txContext = new TransactionContext(tm, transactionAttribute);
// not on the ApplicationContext).
BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory();
tm = BeanFactoryAnnotationUtils.qualifiedBeanOfType(bf, PlatformTransactionManager.class, qualifier);
}
else {
tm = getTransactionManager(testContext);
}
TransactionContext txContext = new TransactionContext(tm, transactionDefinition);
runBeforeTransactionMethods(testContext); runBeforeTransactionMethods(testContext);
startNewTransaction(testContext, txContext); startNewTransaction(testContext, txContext);
this.transactionContextCache.put(testMethod, txContext); this.transactionContextCache.put(testMethod, txContext);
...@@ -171,7 +170,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis ...@@ -171,7 +170,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
* If a transaction is currently active for the test method of the supplied * If a transaction is currently active for the test method of the supplied
* {@link TestContext test context}, this method will end the transaction * {@link TestContext test context}, this method will end the transaction
* and run {@link AfterTransaction &#064;AfterTransaction methods}. * and run {@link AfterTransaction &#064;AfterTransaction methods}.
* <p>{@link AfterTransaction &#064;AfterTransaction methods} are guaranteed to be * <p>{@code AfterTransaction &#064;AfterTransaction methods} are guaranteed to be
* invoked even if an error occurs while ending the transaction. * invoked even if an error occurs while ending the transaction.
*/ */
@Override @Override
...@@ -205,15 +204,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis ...@@ -205,15 +204,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
Collections.reverse(methods); Collections.reverse(methods);
for (Method method : methods) { for (Method method : methods) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Executing @BeforeTransaction method [" + method + "] for test context [" logger.debug("Executing @BeforeTransaction method [" + method + "] for test context " + testContext);
+ testContext + "]");
} }
method.invoke(testContext.getTestInstance()); method.invoke(testContext.getTestInstance());
} }
} }
catch (InvocationTargetException ex) { catch (InvocationTargetException ex) {
logger.error("Exception encountered while executing @BeforeTransaction methods for test context [" logger.error("Exception encountered while executing @BeforeTransaction methods for test context "
+ testContext + "]", ex.getTargetException()); + testContext + ".", ex.getTargetException());
ReflectionUtils.rethrowException(ex.getTargetException()); ReflectionUtils.rethrowException(ex.getTargetException());
} }
} }
...@@ -233,8 +231,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis ...@@ -233,8 +231,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
for (Method method : methods) { for (Method method : methods) {
try { try {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Executing @AfterTransaction method [" + method + "] for test context [" + testContext logger.debug("Executing @AfterTransaction method [" + method + "] for test context " + testContext);
+ "]");
} }
method.invoke(testContext.getTestInstance()); method.invoke(testContext.getTestInstance());
} }
...@@ -244,14 +241,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis ...@@ -244,14 +241,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
afterTransactionException = targetException; afterTransactionException = targetException;
} }
logger.error("Exception encountered while executing @AfterTransaction method [" + method logger.error("Exception encountered while executing @AfterTransaction method [" + method
+ "] for test context [" + testContext + "]", targetException); + "] for test context " + testContext, targetException);
} }
catch (Exception ex) { catch (Exception ex) {
if (afterTransactionException == null) { if (afterTransactionException == null) {
afterTransactionException = ex; afterTransactionException = ex;
} }
logger.error("Exception encountered while executing @AfterTransaction method [" + method logger.error("Exception encountered while executing @AfterTransaction method [" + method
+ "] for test context [" + testContext + "]", ex); + "] for test context " + testContext, ex);
} }
} }
...@@ -287,14 +284,50 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis ...@@ -287,14 +284,50 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
private void endTransaction(TestContext testContext, TransactionContext txContext) throws Exception { private void endTransaction(TestContext testContext, TransactionContext txContext) throws Exception {
boolean rollback = isRollback(testContext); boolean rollback = isRollback(testContext);
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Ending transaction for test context [" + testContext + "]; transaction manager [" logger.trace("Ending transaction for test context " + testContext + "; transaction manager ["
+ txContext.transactionStatus + "]; rollback [" + rollback + "]"); + txContext.transactionStatus + "]; rollback [" + rollback + "]");
} }
txContext.endTransaction(rollback); txContext.endTransaction(rollback);
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
logger.info((rollback ? "Rolled back" : "Committed") logger.info((rollback ? "Rolled back" : "Committed")
+ " transaction after test execution for test context [" + testContext + "]"); + " transaction after test execution for test context " + testContext);
}
}
/**
* Get the {@link PlatformTransactionManager transaction manager} to use
* for the supplied {@link TestContext test context} and {@code qualifier}.
* <p>Delegates to {@link #getTransactionManager(TestContext)} if the
* supplied {@code qualifier} is <code>null</code> or empty.
* @param testContext the test context for which the transaction manager
* should be retrieved
* @param qualifier the qualifier for selecting between multiple bean matches;
* may be <code>null</code> or empty
* @return the transaction manager to use, or <code>null</code> if not found
* @throws BeansException if an error occurs while retrieving the transaction manager
* @see #getTransactionManager(TestContext)
*/
protected final PlatformTransactionManager getTransactionManager(TestContext testContext, String qualifier) {
// look up by type and qualifier from @Transactional
if (StringUtils.hasText(qualifier)) {
try {
// Use autowire-capable factory in order to support extended qualifier
// matching (only exposed on the internal BeanFactory, not on the
// ApplicationContext).
BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory();
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(bf, PlatformTransactionManager.class, qualifier);
} catch (RuntimeException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Caught exception while retrieving transaction manager for test context " + testContext
+ " and qualifier [" + qualifier + "]", ex);
}
throw ex;
}
} }
// else
return getTransactionManager(testContext);
} }
/** /**
...@@ -304,16 +337,34 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis ...@@ -304,16 +337,34 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
* should be retrieved * should be retrieved
* @return the transaction manager to use, or <code>null</code> if not found * @return the transaction manager to use, or <code>null</code> if not found
* @throws BeansException if an error occurs while retrieving the transaction manager * @throws BeansException if an error occurs while retrieving the transaction manager
* @see #getTransactionManager(TestContext, String)
*/ */
protected final PlatformTransactionManager getTransactionManager(TestContext testContext) { protected final PlatformTransactionManager getTransactionManager(TestContext testContext) {
BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory();
String tmName = retrieveConfigurationAttributes(testContext).getTransactionManagerName(); String tmName = retrieveConfigurationAttributes(testContext).getTransactionManagerName();
try { try {
return testContext.getApplicationContext().getBean(tmName, PlatformTransactionManager.class); // look up by type and explicit name from @TransactionConfiguration
} if (StringUtils.hasText(tmName) && !DEFAULT_TRANSACTION_MANAGER_NAME.equals(tmName)) {
catch (BeansException ex) { return bf.getBean(tmName, PlatformTransactionManager.class);
}
// look up single bean by type
if (bf instanceof ListableBeanFactory) {
ListableBeanFactory lbf = (ListableBeanFactory) bf;
Map<String, PlatformTransactionManager> beansOfType = BeanFactoryUtils.beansOfTypeIncludingAncestors(
lbf, PlatformTransactionManager.class);
if (beansOfType.size() == 1) {
return beansOfType.values().iterator().next();
}
}
// look up by type and default name from @TransactionConfiguration
return bf.getBean(DEFAULT_TRANSACTION_MANAGER_NAME, PlatformTransactionManager.class);
} catch (BeansException ex) {
if (logger.isWarnEnabled()) { if (logger.isWarnEnabled()) {
logger.warn("Caught exception while retrieving transaction manager with bean name [" + tmName logger.warn("Caught exception while retrieving transaction manager for test context " + testContext, ex);
+ "] for test context [" + testContext + "]", ex);
} }
throw ex; throw ex;
} }
...@@ -348,14 +399,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis ...@@ -348,14 +399,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
boolean rollbackOverride = rollbackAnnotation.value(); boolean rollbackOverride = rollbackAnnotation.value();
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Method-level @Rollback(" + rollbackOverride + ") overrides default rollback [" + rollback logger.debug("Method-level @Rollback(" + rollbackOverride + ") overrides default rollback [" + rollback
+ "] for test context [" + testContext + "]"); + "] for test context " + testContext);
} }
rollback = rollbackOverride; rollback = rollbackOverride;
} }
else { else {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("No method-level @Rollback override: using default rollback [" + rollback logger.debug("No method-level @Rollback override: using default rollback [" + rollback
+ "] for test context [" + testContext + "]"); + "] for test context " + testContext);
} }
} }
return rollback; return rollback;
...@@ -451,20 +502,19 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis ...@@ -451,20 +502,19 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
/** /**
* Retrieves the {@link TransactionConfigurationAttributes} for the * Retrieves the {@link TransactionConfigurationAttributes} for the
* specified {@link Class class} which may optionally declare or inherit a * specified {@link Class class} which may optionally declare or inherit
* {@link TransactionConfiguration &#064;TransactionConfiguration}. If a * {@link TransactionConfiguration &#064;TransactionConfiguration}. If
* {@link TransactionConfiguration} annotation is not present for the * {@code &#064;TransactionConfiguration} is not present for the supplied
* supplied class, the <em>default values</em> for attributes defined in * class, the <em>default values</em> for attributes defined in
* {@link TransactionConfiguration} will be used instead. * {@code &#064;TransactionConfiguration} will be used instead.
* @param clazz the Class object corresponding to the test class for which * @param testContext the test context for which the configuration
* the configuration attributes should be retrieved * attributes should be retrieved
* @return a new TransactionConfigurationAttributes instance * @return a new TransactionConfigurationAttributes instance
*/ */
private TransactionConfigurationAttributes retrieveConfigurationAttributes(TestContext testContext) { private TransactionConfigurationAttributes retrieveConfigurationAttributes(TestContext testContext) {
if (this.configurationAttributes == null) { if (this.configurationAttributes == null) {
Class<?> clazz = testContext.getTestClass(); Class<?> clazz = testContext.getTestClass();
Class<TransactionConfiguration> annotationType = TransactionConfiguration.class; TransactionConfiguration config = clazz.getAnnotation(TransactionConfiguration.class);
TransactionConfiguration config = clazz.getAnnotation(annotationType);
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Retrieved @TransactionConfiguration [" + config + "] for test class [" + clazz + "]"); logger.debug("Retrieved @TransactionConfiguration [" + config + "] for test class [" + clazz + "]");
} }
...@@ -476,14 +526,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis ...@@ -476,14 +526,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
defaultRollback = config.defaultRollback(); defaultRollback = config.defaultRollback();
} }
else { else {
transactionManagerName = (String) AnnotationUtils.getDefaultValue(annotationType, "transactionManager"); transactionManagerName = DEFAULT_TRANSACTION_MANAGER_NAME;
defaultRollback = (Boolean) AnnotationUtils.getDefaultValue(annotationType, "defaultRollback"); defaultRollback = DEFAULT_DEFAULT_ROLLBACK;
} }
TransactionConfigurationAttributes configAttributes = new TransactionConfigurationAttributes( TransactionConfigurationAttributes configAttributes = new TransactionConfigurationAttributes(
transactionManagerName, defaultRollback); transactionManagerName, defaultRollback);
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Retrieved TransactionConfigurationAttributes [" + configAttributes + "] for class [" logger.debug("Retrieved TransactionConfigurationAttributes " + configAttributes + " for class ["
+ clazz + "]"); + clazz + "]");
} }
this.configurationAttributes = configAttributes; this.configurationAttributes = configAttributes;
......
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.junit4.spr9645;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.test.transaction.CallCountingTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
/**
* Integration tests that verify the behavior requested in
* <a href="https://jira.springsource.org/browse/SPR-9645">SPR-9645</a>.
*
* @author Sam Brannen
* @since 3.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class LookUpNonexistentTxMgrTests {
private static final CallCountingTransactionManager txManager = new CallCountingTransactionManager();
@Configuration
static class Config {
@Bean
public PlatformTransactionManager transactionManager() {
return txManager;
}
}
@BeforeTransaction
public void beforeTransaction() {
txManager.clear();
}
@Test
public void lookUpNothing() {
assertEquals(0, txManager.begun);
assertEquals(0, txManager.inflight);
assertEquals(0, txManager.commits);
assertEquals(0, txManager.rollbacks);
}
@AfterTransaction
public void afterTransaction() {
assertEquals(0, txManager.begun);
assertEquals(0, txManager.inflight);
assertEquals(0, txManager.commits);
assertEquals(0, txManager.rollbacks);
}
}
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.junit4.spr9645;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.test.transaction.CallCountingTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration tests that verify the behavior requested in
* <a href="https://jira.springsource.org/browse/SPR-9645">SPR-9645</a>.
*
* @author Sam Brannen
* @since 3.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@Transactional
public class LookUpTxMgrByTypeAndDefaultNameTests {
private static final CallCountingTransactionManager txManager1 = new CallCountingTransactionManager();
private static final CallCountingTransactionManager txManager2 = new CallCountingTransactionManager();
@Configuration
static class Config {
@Bean
public PlatformTransactionManager transactionManager() {
return txManager1;
}
@Bean
public PlatformTransactionManager txManager2() {
return txManager2;
}
}
@BeforeTransaction
public void beforeTransaction() {
txManager1.clear();
txManager2.clear();
}
@Test
public void lookUpByTypeAndDefaultName() {
assertEquals(1, txManager1.begun);
assertEquals(1, txManager1.inflight);
assertEquals(0, txManager1.commits);
assertEquals(0, txManager1.rollbacks);
}
@AfterTransaction
public void afterTransaction() {
assertEquals(1, txManager1.begun);
assertEquals(0, txManager1.inflight);
assertEquals(0, txManager1.commits);
assertEquals(1, txManager1.rollbacks);
}
}
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.junit4.spr9645;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.transaction.CallCountingTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration tests that verify the behavior requested in
* <a href="https://jira.springsource.org/browse/SPR-9645">SPR-9645</a>.
*
* @author Sam Brannen
* @since 3.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@Transactional
@TransactionConfiguration(transactionManager = "txManager1")
public class LookUpTxMgrByTypeAndNameTests {
private static final CallCountingTransactionManager txManager1 = new CallCountingTransactionManager();
private static final CallCountingTransactionManager txManager2 = new CallCountingTransactionManager();
@Configuration
static class Config {
@Bean
public PlatformTransactionManager txManager1() {
return txManager1;
}
@Bean
public PlatformTransactionManager txManager2() {
return txManager2;
}
}
@BeforeTransaction
public void beforeTransaction() {
txManager1.clear();
txManager2.clear();
}
@Test
public void lookUpByTypeAndName() {
assertEquals(1, txManager1.begun);
assertEquals(1, txManager1.inflight);
assertEquals(0, txManager1.commits);
assertEquals(0, txManager1.rollbacks);
}
@AfterTransaction
public void afterTransaction() {
assertEquals(1, txManager1.begun);
assertEquals(0, txManager1.inflight);
assertEquals(0, txManager1.commits);
assertEquals(1, txManager1.rollbacks);
}
}
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.junit4.spr9645;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.test.transaction.CallCountingTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration tests that verify the behavior requested in
* <a href="https://jira.springsource.org/browse/SPR-9645">SPR-9645</a>.
*
* @author Sam Brannen
* @since 3.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@Transactional("txManager1")
public class LookUpTxMgrByTypeAndQualifierAtClassLevelTests {
private static final CallCountingTransactionManager txManager1 = new CallCountingTransactionManager();
private static final CallCountingTransactionManager txManager2 = new CallCountingTransactionManager();
@Configuration
static class Config {
@Bean
public PlatformTransactionManager txManager1() {
return txManager1;
}
@Bean
public PlatformTransactionManager txManager2() {
return txManager2;
}
}
@BeforeTransaction
public void beforeTransaction() {
txManager1.clear();
txManager2.clear();
}
@Test
public void lookUpByTypeAndQualifier() {
assertEquals(1, txManager1.begun);
assertEquals(1, txManager1.inflight);
assertEquals(0, txManager1.commits);
assertEquals(0, txManager1.rollbacks);
}
@AfterTransaction
public void afterTransaction() {
assertEquals(1, txManager1.begun);
assertEquals(0, txManager1.inflight);
assertEquals(0, txManager1.commits);
assertEquals(1, txManager1.rollbacks);
}
}
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.junit4.spr9645;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.test.transaction.CallCountingTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration tests that verify the behavior requested in
* <a href="https://jira.springsource.org/browse/SPR-9645">SPR-9645</a>.
*
* @author Sam Brannen
* @since 3.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class LookUpTxMgrByTypeAndQualifierAtMethodLevelTests {
private static final CallCountingTransactionManager txManager1 = new CallCountingTransactionManager();
private static final CallCountingTransactionManager txManager2 = new CallCountingTransactionManager();
@Configuration
static class Config {
@Bean
public PlatformTransactionManager txManager1() {
return txManager1;
}
@Bean
public PlatformTransactionManager txManager2() {
return txManager2;
}
}
@BeforeTransaction
public void beforeTransaction() {
txManager1.clear();
txManager2.clear();
}
@Transactional("txManager1")
@Test
public void lookUpByTypeAndQualifier() {
assertEquals(1, txManager1.begun);
assertEquals(1, txManager1.inflight);
assertEquals(0, txManager1.commits);
assertEquals(0, txManager1.rollbacks);
}
@AfterTransaction
public void afterTransaction() {
assertEquals(1, txManager1.begun);
assertEquals(0, txManager1.inflight);
assertEquals(0, txManager1.commits);
assertEquals(1, txManager1.rollbacks);
}
}
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.junit4.spr9645;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.test.transaction.CallCountingTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration tests that verify the behavior requested in
* <a href="https://jira.springsource.org/browse/SPR-9645">SPR-9645</a>.
*
* @author Sam Brannen
* @since 3.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@Transactional
public class LookUpTxMgrByTypeTests {
private static final CallCountingTransactionManager txManager = new CallCountingTransactionManager();
@Configuration
static class Config {
@Bean
public PlatformTransactionManager txManager() {
return txManager;
}
}
@BeforeTransaction
public void beforeTransaction() {
txManager.clear();
}
@Test
public void lookUpByType() {
assertEquals(1, txManager.begun);
assertEquals(1, txManager.inflight);
assertEquals(0, txManager.commits);
assertEquals(0, txManager.rollbacks);
}
@AfterTransaction
public void afterTransaction() {
assertEquals(1, txManager.begun);
assertEquals(0, txManager.inflight);
assertEquals(0, txManager.commits);
assertEquals(1, txManager.rollbacks);
}
}
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.transaction;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;
/**
* @author Rod Johnson
* @author Juergen Hoeller
*/
@SuppressWarnings("serial")
public class CallCountingTransactionManager extends AbstractPlatformTransactionManager {
public TransactionDefinition lastDefinition;
public int begun;
public int commits;
public int rollbacks;
public int inflight;
protected Object doGetTransaction() {
return new Object();
}
protected void doBegin(Object transaction, TransactionDefinition definition) {
this.lastDefinition = definition;
++begun;
++inflight;
}
protected void doCommit(DefaultTransactionStatus status) {
++commits;
--inflight;
}
protected void doRollback(DefaultTransactionStatus status) {
++rollbacks;
--inflight;
}
public void clear() {
begun = commits = rollbacks = inflight = 0;
}
}
...@@ -41,6 +41,10 @@ ...@@ -41,6 +41,10 @@
</logger> </logger>
--> -->
<logger name="org.springframework.test.context.transaction.TransactionalTestExecutionListener">
<level value="warn" />
</logger>
<logger name="org.springframework.test.context"> <logger name="org.springframework.test.context">
<level value="warn" /> <level value="warn" />
</logger> </logger>
......
...@@ -28,6 +28,7 @@ Changes in version 3.2 M2 (2012-08-xx) ...@@ -28,6 +28,7 @@ Changes in version 3.2 M2 (2012-08-xx)
* add exclude patterns for mapped interceptors in MVC namespace and MVC Java config * add exclude patterns for mapped interceptors in MVC namespace and MVC Java config
* support content negotiation options in MVC namespace and MVC Java config * support content negotiation options in MVC namespace and MVC Java config
* support named dispatchers in MockServletContext (SPR-9587) * support named dispatchers in MockServletContext (SPR-9587)
* support single, unqualified tx manager in the TestContext framework (SPR-9645)
Changes in version 3.2 M1 (2012-05-28) Changes in version 3.2 M1 (2012-05-28)
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册