From f65451d777dc14deb826490825f889c306acc569 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Tue, 16 Jun 2020 14:29:51 +0200 Subject: [PATCH] DATACMNS-1745 - Polishing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made the reference to RepositoryInformation non-nullable from the RepositoryAnnotationTransactionAttributeSource (previously RepositoryInformationPreferring…). Adapted test cases accordingly. --- ...sactionalRepositoryProxyPostProcessor.java | 54 ++++++++++--------- ...onTransactionAttributeSourceUnitTests.java | 14 +++-- ...RepositoryProxyPostProcessorUnitTests.java | 10 ++-- 3 files changed, 43 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/springframework/data/repository/core/support/TransactionalRepositoryProxyPostProcessor.java b/src/main/java/org/springframework/data/repository/core/support/TransactionalRepositoryProxyPostProcessor.java index 95fb67cb9..ca161983a 100644 --- a/src/main/java/org/springframework/data/repository/core/support/TransactionalRepositoryProxyPostProcessor.java +++ b/src/main/java/org/springframework/data/repository/core/support/TransactionalRepositoryProxyPostProcessor.java @@ -15,7 +15,6 @@ */ package org.springframework.data.repository.core.support; -import java.io.Serializable; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -73,11 +72,9 @@ class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostPr */ public void postProcess(ProxyFactory factory, RepositoryInformation repositoryInformation) { - RepositoryInformationPreferringAnnotationTransactionAttributeSource transactionAttributeSource = new RepositoryInformationPreferringAnnotationTransactionAttributeSource( - enableDefaultTransactions, repositoryInformation); - TransactionInterceptor transactionInterceptor = new TransactionInterceptor(); - transactionInterceptor.setTransactionAttributeSource(transactionAttributeSource); + transactionInterceptor.setTransactionAttributeSource( + new RepositoryAnnotationTransactionAttributeSource(repositoryInformation, enableDefaultTransactions)); transactionInterceptor.setTransactionManagerBeanName(transactionManagerName); transactionInterceptor.setBeanFactory(beanFactory); transactionInterceptor.afterPropertiesSet(); @@ -89,7 +86,7 @@ class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostPr * Custom implementation of {@link AnnotationTransactionAttributeSource} that that slightly modify the algorithm * transaction configuration is discovered. *

- * The original Spring implementation favours the implementation class' transaction configuration over one declared at + * The original Spring implementation favors the implementation class' transaction configuration over one declared at * an interface. As we need to provide the capability to override transaction configuration of the implementation at * the interface level we pretty much invert this logic to inspect the originally invoked method first before digging * down into the implementation class. @@ -97,34 +94,34 @@ class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostPr * @author Oliver Drotbohm * @author Mark Paluch */ - @SuppressWarnings({ "serial", "null" }) - static class RepositoryInformationPreferringAnnotationTransactionAttributeSource - extends AnnotationTransactionAttributeSource implements Serializable { + static class RepositoryAnnotationTransactionAttributeSource extends AnnotationTransactionAttributeSource { - private final boolean enableDefaultTransactions; + private static final long serialVersionUID = 7229616838812819438L; - private final @Nullable RepositoryInformation repositoryInformation; + private final RepositoryInformation repositoryInformation; + private final boolean enableDefaultTransactions; /** * Create a default CustomAnnotationTransactionAttributeSource, supporting public methods that carry the * {@code Transactional} annotation or the EJB3 {@link javax.ejb.TransactionAttribute} annotation. */ - public RepositoryInformationPreferringAnnotationTransactionAttributeSource() { - this(true, null); - } + public RepositoryAnnotationTransactionAttributeSource(RepositoryInformation repositoryInformation, + boolean enableDefaultTransactions) { - /** - * Create a default CustomAnnotationTransactionAttributeSource, supporting public methods that carry the - * {@code Transactional} annotation or the EJB3 {@link javax.ejb.TransactionAttribute} annotation. - */ - public RepositoryInformationPreferringAnnotationTransactionAttributeSource(boolean enableDefaultTransactions, - @Nullable RepositoryInformation repositoryInformation) { super(true); + + Assert.notNull(repositoryInformation, "RepositoryInformation must not be null!"); + this.enableDefaultTransactions = enableDefaultTransactions; this.repositoryInformation = repositoryInformation; } + /* + * (non-Javadoc) + * @see org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource#computeTransactionAttribute(java.lang.reflect.Method, java.lang.Class) + */ @Override + @Nullable protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class targetClass) { // Don't allow no-public methods as required. @@ -133,21 +130,26 @@ class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostPr } // Ignore CGLIB subclasses - introspect the actual user class. - Class userClass = targetClass != null ? ProxyUtils.getUserClass(targetClass) : targetClass; + Class userClass = targetClass == null ? targetClass : ProxyUtils.getUserClass(targetClass); + // The method may be on an interface, but we need attributes from the target class. // If the target class is null, the method will be unchanged. Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass); + // If we are dealing with method with generic parameters, find the original method. specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); TransactionAttribute txAtt = null; if (specificMethod != method) { + // Fallback is to look at the original method. txAtt = findTransactionAttribute(method); + if (txAtt != null) { return txAtt; } + // Last fallback is the class of the original method. txAtt = findTransactionAttribute(method.getDeclaringClass()); @@ -156,21 +158,21 @@ class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostPr } } - // Start: Implementation class check block - // First try is the method in the target class. txAtt = findTransactionAttribute(specificMethod); + if (txAtt != null) { return txAtt; } // Second try is the transaction attribute on the target class. txAtt = findTransactionAttribute(specificMethod.getDeclaringClass()); + if (txAtt != null) { return txAtt; } - if (!enableDefaultTransactions || repositoryInformation == null) { + if (!enableDefaultTransactions) { return null; } @@ -183,18 +185,18 @@ class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostPr } txAtt = findTransactionAttribute(targetClassMethod); + if (txAtt != null) { return txAtt; } txAtt = findTransactionAttribute(targetClassMethod.getDeclaringClass()); + if (txAtt != null) { return txAtt; } return null; - // End: Implementation class check block } } - } diff --git a/src/test/java/org/springframework/data/repository/core/support/RepositoryInformationPreferringAnnotationTransactionAttributeSourceUnitTests.java b/src/test/java/org/springframework/data/repository/core/support/RepositoryInformationPreferringAnnotationTransactionAttributeSourceUnitTests.java index 2ae4600f8..aab0f7e60 100755 --- a/src/test/java/org/springframework/data/repository/core/support/RepositoryInformationPreferringAnnotationTransactionAttributeSourceUnitTests.java +++ b/src/test/java/org/springframework/data/repository/core/support/RepositoryInformationPreferringAnnotationTransactionAttributeSourceUnitTests.java @@ -16,23 +16,29 @@ package org.springframework.data.repository.core.support; import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; import org.junit.jupiter.api.Test; -import org.springframework.data.repository.core.support.TransactionalRepositoryProxyPostProcessor.RepositoryInformationPreferringAnnotationTransactionAttributeSource; +import org.springframework.data.repository.core.RepositoryInformation; +import org.springframework.data.repository.core.support.TransactionalRepositoryProxyPostProcessor.RepositoryAnnotationTransactionAttributeSource; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.interceptor.TransactionAttribute; /** - * Unit tests for {@link RepositoryInformationPreferringAnnotationTransactionAttributeSource}. + * Unit tests for {@link RepositoryAnnotationTransactionAttributeSource}. * * @author Oliver Gierke + * @author Mark Paluch */ class RepositoryInformationPreferringAnnotationTransactionAttributeSourceUnitTests { @Test - void usesCustomTransactionConfigurationOnInterface() throws SecurityException, NoSuchMethodException { + void usesCustomTransactionConfigurationOnInterface() throws Exception { - RepositoryInformationPreferringAnnotationTransactionAttributeSource source = new RepositoryInformationPreferringAnnotationTransactionAttributeSource(); + RepositoryInformation information = mock(RepositoryInformation.class); + + RepositoryAnnotationTransactionAttributeSource source = new RepositoryAnnotationTransactionAttributeSource( + information, true); TransactionAttribute attribute = source.getTransactionAttribute(Bar.class.getMethod("bar", Object.class), FooImpl.class); diff --git a/src/test/java/org/springframework/data/repository/core/support/TransactionRepositoryProxyPostProcessorUnitTests.java b/src/test/java/org/springframework/data/repository/core/support/TransactionRepositoryProxyPostProcessorUnitTests.java index 28211f30c..6f97f8546 100755 --- a/src/test/java/org/springframework/data/repository/core/support/TransactionRepositoryProxyPostProcessorUnitTests.java +++ b/src/test/java/org/springframework/data/repository/core/support/TransactionRepositoryProxyPostProcessorUnitTests.java @@ -26,12 +26,11 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; - import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.RepositoryInformation; -import org.springframework.data.repository.core.support.TransactionalRepositoryProxyPostProcessor.RepositoryInformationPreferringAnnotationTransactionAttributeSource; +import org.springframework.data.repository.core.support.TransactionalRepositoryProxyPostProcessor.RepositoryAnnotationTransactionAttributeSource; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.interceptor.TransactionAttribute; import org.springframework.transaction.interceptor.TransactionAttributeSource; @@ -87,7 +86,8 @@ class TransactionRepositoryProxyPostProcessorUnitTests { Method method = SampleRepository.class.getMethod("methodWithJtaOneDotTwoAtTransactional"); - TransactionAttributeSource attributeSource = new RepositoryInformationPreferringAnnotationTransactionAttributeSource(); + TransactionAttributeSource attributeSource = new RepositoryAnnotationTransactionAttributeSource( + repositoryInformation, true); TransactionAttribute attribute = attributeSource.getTransactionAttribute(method, SampleRepository.class); assertThat(attribute).isNotNull(); @@ -100,8 +100,8 @@ class TransactionRepositoryProxyPostProcessorUnitTests { when(repositoryInformation.getTargetClassMethod(repositorySaveMethod)).thenReturn(implementationClassMethod); - RepositoryInformationPreferringAnnotationTransactionAttributeSource attributeSource = new RepositoryInformationPreferringAnnotationTransactionAttributeSource( - true, repositoryInformation); + RepositoryAnnotationTransactionAttributeSource attributeSource = new RepositoryAnnotationTransactionAttributeSource( + repositoryInformation, true); TransactionAttribute attribute = attributeSource.getTransactionAttribute(repositorySaveMethod, SampleImplementation.class);