Reduce PlatformTransactionManager lookups
Prior to this commit, cache operations mentioning a qualifier led to a lookup for the same PlatformTransactionManager over and over again. The same applied when a transactionManager bean name was specified on the interceptor. This commit adds a cache to store the reference of such transaction managers. As a convenience, the default PlatformTransactionManager is also initialized if it has not been through configuration. Issue: SPR-11954
This commit is contained in:
@@ -18,7 +18,7 @@ package org.springframework.transaction.interceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.dao.OptimisticLockingFailureException;
|
||||
import org.springframework.tests.sample.beans.ITestBean;
|
||||
@@ -33,6 +33,7 @@ import org.springframework.transaction.TransactionSystemException;
|
||||
import org.springframework.transaction.UnexpectedRollbackException;
|
||||
import org.springframework.transaction.interceptor.TransactionAspectSupport.TransactionInfo;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
|
||||
/**
|
||||
@@ -47,7 +48,7 @@ import static org.mockito.BDDMockito.*;
|
||||
* @author Rod Johnson
|
||||
* @since 16.03.2003
|
||||
*/
|
||||
public abstract class AbstractTransactionAspectTests extends TestCase {
|
||||
public abstract class AbstractTransactionAspectTests {
|
||||
|
||||
protected Method exceptionalMethod;
|
||||
|
||||
@@ -69,7 +70,8 @@ public abstract class AbstractTransactionAspectTests extends TestCase {
|
||||
}
|
||||
|
||||
|
||||
public void testNoTransaction() throws Exception {
|
||||
@Test
|
||||
public void noTransaction() throws Exception {
|
||||
PlatformTransactionManager ptm = mock(PlatformTransactionManager.class);
|
||||
|
||||
TestBean tb = new TestBean();
|
||||
@@ -91,7 +93,8 @@ public abstract class AbstractTransactionAspectTests extends TestCase {
|
||||
/**
|
||||
* Check that a transaction is created and committed.
|
||||
*/
|
||||
public void testTransactionShouldSucceed() throws Exception {
|
||||
@Test
|
||||
public void transactionShouldSucceed() throws Exception {
|
||||
TransactionAttribute txatt = new DefaultTransactionAttribute();
|
||||
|
||||
MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
|
||||
@@ -116,7 +119,8 @@ public abstract class AbstractTransactionAspectTests extends TestCase {
|
||||
* Check that a transaction is created and committed using
|
||||
* CallbackPreferringPlatformTransactionManager.
|
||||
*/
|
||||
public void testTransactionShouldSucceedWithCallbackPreference() throws Exception {
|
||||
@Test
|
||||
public void transactionShouldSucceedWithCallbackPreference() throws Exception {
|
||||
TransactionAttribute txatt = new DefaultTransactionAttribute();
|
||||
|
||||
MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
|
||||
@@ -135,7 +139,8 @@ public abstract class AbstractTransactionAspectTests extends TestCase {
|
||||
assertFalse(ptm.getStatus().isRollbackOnly());
|
||||
}
|
||||
|
||||
public void testTransactionExceptionPropagatedWithCallbackPreference() throws Throwable {
|
||||
@Test
|
||||
public void transactionExceptionPropagatedWithCallbackPreference() throws Throwable {
|
||||
TransactionAttribute txatt = new DefaultTransactionAttribute();
|
||||
|
||||
MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
|
||||
@@ -163,7 +168,8 @@ public abstract class AbstractTransactionAspectTests extends TestCase {
|
||||
/**
|
||||
* Check that two transactions are created and committed.
|
||||
*/
|
||||
public void testTwoTransactionsShouldSucceed() throws Exception {
|
||||
@Test
|
||||
public void twoTransactionsShouldSucceed() throws Exception {
|
||||
TransactionAttribute txatt = new DefaultTransactionAttribute();
|
||||
|
||||
MapTransactionAttributeSource tas1 = new MapTransactionAttributeSource();
|
||||
@@ -191,7 +197,8 @@ public abstract class AbstractTransactionAspectTests extends TestCase {
|
||||
/**
|
||||
* Check that a transaction is created and committed.
|
||||
*/
|
||||
public void testTransactionShouldSucceedWithNotNew() throws Exception {
|
||||
@Test
|
||||
public void transactionShouldSucceedWithNotNew() throws Exception {
|
||||
TransactionAttribute txatt = new DefaultTransactionAttribute();
|
||||
|
||||
MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
|
||||
@@ -213,7 +220,8 @@ public abstract class AbstractTransactionAspectTests extends TestCase {
|
||||
verify(ptm).commit(status);
|
||||
}
|
||||
|
||||
public void testEnclosingTransactionWithNonTransactionMethodOnAdvisedInside() throws Throwable {
|
||||
@Test
|
||||
public void enclosingTransactionWithNonTransactionMethodOnAdvisedInside() throws Throwable {
|
||||
TransactionAttribute txatt = new DefaultTransactionAttribute();
|
||||
|
||||
MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
|
||||
@@ -258,7 +266,8 @@ public abstract class AbstractTransactionAspectTests extends TestCase {
|
||||
verify(ptm).commit(status);
|
||||
}
|
||||
|
||||
public void testEnclosingTransactionWithNestedTransactionOnAdvisedInside() throws Throwable {
|
||||
@Test
|
||||
public void enclosingTransactionWithNestedTransactionOnAdvisedInside() throws Throwable {
|
||||
final TransactionAttribute outerTxatt = new DefaultTransactionAttribute();
|
||||
final TransactionAttribute innerTxatt = new DefaultTransactionAttribute(TransactionDefinition.PROPAGATION_NESTED);
|
||||
|
||||
@@ -314,35 +323,43 @@ public abstract class AbstractTransactionAspectTests extends TestCase {
|
||||
verify(ptm).commit(outerStatus);
|
||||
}
|
||||
|
||||
public void testRollbackOnCheckedException() throws Throwable {
|
||||
@Test
|
||||
public void rollbackOnCheckedException() throws Throwable {
|
||||
doTestRollbackOnException(new Exception(), true, false);
|
||||
}
|
||||
|
||||
public void testNoRollbackOnCheckedException() throws Throwable {
|
||||
@Test
|
||||
public void noRollbackOnCheckedException() throws Throwable {
|
||||
doTestRollbackOnException(new Exception(), false, false);
|
||||
}
|
||||
|
||||
public void testRollbackOnUncheckedException() throws Throwable {
|
||||
@Test
|
||||
public void rollbackOnUncheckedException() throws Throwable {
|
||||
doTestRollbackOnException(new RuntimeException(), true, false);
|
||||
}
|
||||
|
||||
public void testNoRollbackOnUncheckedException() throws Throwable {
|
||||
@Test
|
||||
public void noRollbackOnUncheckedException() throws Throwable {
|
||||
doTestRollbackOnException(new RuntimeException(), false, false);
|
||||
}
|
||||
|
||||
public void testRollbackOnCheckedExceptionWithRollbackException() throws Throwable {
|
||||
@Test
|
||||
public void rollbackOnCheckedExceptionWithRollbackException() throws Throwable {
|
||||
doTestRollbackOnException(new Exception(), true, true);
|
||||
}
|
||||
|
||||
public void testNoRollbackOnCheckedExceptionWithRollbackException() throws Throwable {
|
||||
@Test
|
||||
public void noRollbackOnCheckedExceptionWithRollbackException() throws Throwable {
|
||||
doTestRollbackOnException(new Exception(), false, true);
|
||||
}
|
||||
|
||||
public void testRollbackOnUncheckedExceptionWithRollbackException() throws Throwable {
|
||||
@Test
|
||||
public void rollbackOnUncheckedExceptionWithRollbackException() throws Throwable {
|
||||
doTestRollbackOnException(new RuntimeException(), true, true);
|
||||
}
|
||||
|
||||
public void testNoRollbackOnUncheckedExceptionWithRollbackException() throws Throwable {
|
||||
@Test
|
||||
public void noRollbackOnUncheckedExceptionWithRollbackException() throws Throwable {
|
||||
doTestRollbackOnException(new RuntimeException(), false, true);
|
||||
}
|
||||
|
||||
@@ -413,7 +430,8 @@ public abstract class AbstractTransactionAspectTests extends TestCase {
|
||||
/**
|
||||
* Test that TransactionStatus.setRollbackOnly works.
|
||||
*/
|
||||
public void testProgrammaticRollback() throws Exception {
|
||||
@Test
|
||||
public void programmaticRollback() throws Exception {
|
||||
TransactionAttribute txatt = new DefaultTransactionAttribute();
|
||||
|
||||
Method m = getNameMethod;
|
||||
@@ -447,7 +465,8 @@ public abstract class AbstractTransactionAspectTests extends TestCase {
|
||||
* Simulate a transaction infrastructure failure.
|
||||
* Shouldn't invoke target method.
|
||||
*/
|
||||
public void testCannotCreateTransaction() throws Exception {
|
||||
@Test
|
||||
public void cannotCreateTransaction() throws Exception {
|
||||
TransactionAttribute txatt = new DefaultTransactionAttribute();
|
||||
|
||||
Method m = getNameMethod;
|
||||
@@ -482,7 +501,8 @@ public abstract class AbstractTransactionAspectTests extends TestCase {
|
||||
* Check that the target method was invoked, but that the transaction
|
||||
* infrastructure exception was thrown to the client
|
||||
*/
|
||||
public void testCannotCommitTransaction() throws Exception {
|
||||
@Test
|
||||
public void cannotCommitTransaction() throws Exception {
|
||||
TransactionAttribute txatt = new DefaultTransactionAttribute();
|
||||
|
||||
Method m = setNameMethod;
|
||||
|
||||
@@ -16,10 +16,20 @@
|
||||
|
||||
package org.springframework.transaction.interceptor;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
@@ -34,6 +44,9 @@ import org.springframework.util.SerializationTestUtils;
|
||||
*/
|
||||
public class TransactionInterceptorTests extends AbstractTransactionAspectTests {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Override
|
||||
protected Object advised(Object target, PlatformTransactionManager ptm, TransactionAttributeSource[] tas) throws Exception {
|
||||
TransactionInterceptor ti = new TransactionInterceptor();
|
||||
@@ -63,11 +76,12 @@ public class TransactionInterceptorTests extends AbstractTransactionAspectTests
|
||||
return pf.getProxy();
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* A TransactionInterceptor should be serializable if its
|
||||
* PlatformTransactionManager is.
|
||||
*/
|
||||
public void testSerializableWithAttributeProperties() throws Exception {
|
||||
@Test
|
||||
public void serializableWithAttributeProperties() throws Exception {
|
||||
TransactionInterceptor ti = new TransactionInterceptor();
|
||||
Properties props = new Properties();
|
||||
props.setProperty("methodName", "PROPAGATION_REQUIRED");
|
||||
@@ -82,7 +96,8 @@ public class TransactionInterceptorTests extends AbstractTransactionAspectTests
|
||||
assertNotNull(ti.getTransactionAttributeSource());
|
||||
}
|
||||
|
||||
public void testSerializableWithCompositeSource() throws Exception {
|
||||
@Test
|
||||
public void serializableWithCompositeSource() throws Exception {
|
||||
NameMatchTransactionAttributeSource tas1 = new NameMatchTransactionAttributeSource();
|
||||
Properties props = new Properties();
|
||||
props.setProperty("methodName", "PROPAGATION_REQUIRED");
|
||||
@@ -106,6 +121,84 @@ public class TransactionInterceptorTests extends AbstractTransactionAspectTests
|
||||
assertTrue(ctas.getTransactionAttributeSources()[1] instanceof NameMatchTransactionAttributeSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void determineTransactionManagerWithQualifierUnknown() {
|
||||
BeanFactory beanFactory = mock(BeanFactory.class);
|
||||
TransactionInterceptor ti = createTestTransactionInterceptor(beanFactory);
|
||||
DefaultTransactionAttribute attribute = new DefaultTransactionAttribute();
|
||||
attribute.setQualifier("fooTransactionManager");
|
||||
|
||||
thrown.expect(NoSuchBeanDefinitionException.class);
|
||||
thrown.expectMessage("'fooTransactionManager'");
|
||||
ti.determineTransactionManager(attribute);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void determineTransactionManagerWithQualifierSeveralTimes() {
|
||||
BeanFactory beanFactory = mock(BeanFactory.class);
|
||||
TransactionInterceptor ti = createTestTransactionInterceptor(beanFactory);
|
||||
|
||||
PlatformTransactionManager txManager = mock(PlatformTransactionManager.class);
|
||||
given(beanFactory.containsBean("fooTransactionManager")).willReturn(true);
|
||||
given(beanFactory.getBean("fooTransactionManager", PlatformTransactionManager.class)).willReturn(txManager);
|
||||
|
||||
DefaultTransactionAttribute attribute = new DefaultTransactionAttribute();
|
||||
attribute.setQualifier("fooTransactionManager");
|
||||
PlatformTransactionManager actual = ti.determineTransactionManager(attribute);
|
||||
assertSame(txManager, actual);
|
||||
|
||||
// Call again, should be cached
|
||||
PlatformTransactionManager actual2 = ti.determineTransactionManager(attribute);
|
||||
assertSame(txManager, actual2);
|
||||
verify(beanFactory, times(1)).containsBean("fooTransactionManager");
|
||||
verify(beanFactory, times(1)).getBean("fooTransactionManager", PlatformTransactionManager.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void determineTransactionManagerWithBeanNameSeveralTimes() {
|
||||
BeanFactory beanFactory = mock(BeanFactory.class);
|
||||
TransactionInterceptor ti = createTestTransactionInterceptor(beanFactory);
|
||||
ti.setTransactionManagerBeanName("fooTransactionManager");
|
||||
|
||||
PlatformTransactionManager txManager = mock(PlatformTransactionManager.class);
|
||||
given(beanFactory.getBean("fooTransactionManager", PlatformTransactionManager.class)).willReturn(txManager);
|
||||
|
||||
DefaultTransactionAttribute attribute = new DefaultTransactionAttribute();
|
||||
PlatformTransactionManager actual = ti.determineTransactionManager(attribute);
|
||||
assertSame(txManager, actual);
|
||||
|
||||
// Call again, should be cached
|
||||
PlatformTransactionManager actual2 = ti.determineTransactionManager(attribute);
|
||||
assertSame(txManager, actual2);
|
||||
verify(beanFactory, times(1)).getBean("fooTransactionManager", PlatformTransactionManager.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void determineTransactionManagerDefaultSeveralTimes() {
|
||||
BeanFactory beanFactory = mock(BeanFactory.class);
|
||||
TransactionInterceptor ti = createTestTransactionInterceptor(beanFactory);
|
||||
|
||||
PlatformTransactionManager txManager = mock(PlatformTransactionManager.class);
|
||||
given(beanFactory.getBean(PlatformTransactionManager.class)).willReturn(txManager);
|
||||
|
||||
DefaultTransactionAttribute attribute = new DefaultTransactionAttribute();
|
||||
PlatformTransactionManager actual = ti.determineTransactionManager(attribute);
|
||||
assertSame(txManager, actual);
|
||||
|
||||
// Call again, should be cached
|
||||
PlatformTransactionManager actual2 = ti.determineTransactionManager(attribute);
|
||||
assertSame(txManager, actual2);
|
||||
verify(beanFactory, times(1)).getBean(PlatformTransactionManager.class);
|
||||
}
|
||||
|
||||
private TransactionInterceptor createTestTransactionInterceptor(BeanFactory beanFactory) {
|
||||
TransactionInterceptor ti = new TransactionInterceptor();
|
||||
ti.setBeanFactory(beanFactory);
|
||||
ti.setTransactionAttributeSource(new NameMatchTransactionAttributeSource());
|
||||
ti.afterPropertiesSet();
|
||||
return ti;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* We won't use this: we just want to know it's serializable.
|
||||
|
||||
Reference in New Issue
Block a user