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 a5e6a5861..95fb67cb9 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
@@ -16,17 +16,9 @@
package org.springframework.data.repository.core.support;
import java.io.Serializable;
-import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
@@ -34,18 +26,12 @@ import org.springframework.core.BridgeMethodResolver;
import org.springframework.dao.support.PersistenceExceptionTranslationInterceptor;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.util.ProxyUtils;
-import org.springframework.transaction.annotation.Ejb3TransactionAnnotationParser;
-import org.springframework.transaction.annotation.JtaTransactionAnnotationParser;
-import org.springframework.transaction.annotation.SpringTransactionAnnotationParser;
-import org.springframework.transaction.annotation.TransactionAnnotationParser;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
+import org.springframework.lang.Nullable;
+import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionAttribute;
-import org.springframework.transaction.interceptor.TransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
-import org.springframework.util.ObjectUtils;
/**
* {@link RepositoryProxyPostProcessor} to add transactional behaviour to repository proxies. Adds a
@@ -54,6 +40,7 @@ import org.springframework.util.ObjectUtils;
*
* @author Oliver Gierke
* @author Christoph Strobl
+ * @author Mark Paluch
*/
class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostProcessor {
@@ -86,12 +73,11 @@ class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostPr
*/
public void postProcess(ProxyFactory factory, RepositoryInformation repositoryInformation) {
- CustomAnnotationTransactionAttributeSource transactionAttributeSource = new CustomAnnotationTransactionAttributeSource();
- transactionAttributeSource.setRepositoryInformation(repositoryInformation);
- transactionAttributeSource.setEnableDefaultTransactions(enableDefaultTransactions);
+ RepositoryInformationPreferringAnnotationTransactionAttributeSource transactionAttributeSource = new RepositoryInformationPreferringAnnotationTransactionAttributeSource(
+ enableDefaultTransactions, repositoryInformation);
- @SuppressWarnings("null") // TODO: Remove
- TransactionInterceptor transactionInterceptor = new TransactionInterceptor(null, transactionAttributeSource);
+ TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
+ transactionInterceptor.setTransactionAttributeSource(transactionAttributeSource);
transactionInterceptor.setTransactionManagerBeanName(transactionManagerName);
transactionInterceptor.setBeanFactory(beanFactory);
transactionInterceptor.afterPropertiesSet();
@@ -99,303 +85,55 @@ class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostPr
factory.addAdvice(transactionInterceptor);
}
- // The section below contains copies of two core Spring classes that slightly modify the algorithm transaction
- // configuration is discovered. The original Spring implementation favours 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.
- //
- // Unfortunately the Spring classes do not allow modifying this algorithm easily. That's why we have to copy the two
- // classes 1:1. Only modifications done are inside
- // AbstractFallbackTransactionAttributeSource#computeTransactionAttribute(Method, Class>).
-
/**
- * Implementation of the {@link org.springframework.transaction.interceptor.TransactionAttributeSource} interface for
- * working with transaction metadata in JDK 1.5+ annotation format.
+ * Custom implementation of {@link AnnotationTransactionAttributeSource} that that slightly modify the algorithm
+ * transaction configuration is discovered.
*
- * This class reads Spring's JDK 1.5+ {@link Transactional} annotation and exposes corresponding transaction
- * attributes to Spring's transaction infrastructure. Also supports JTA 1.2's {@link javax.transaction.Transactional}
- * and EJB3's {@link javax.ejb.TransactionAttribute} annotation (if present). This class may also serve as base class
- * for a custom TransactionAttributeSource, or get customized through {@link TransactionAnnotationParser} strategies.
+ * The original Spring implementation favours 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.
*
- * @author Colin Sampaleanu
- * @author Juergen Hoeller
- * @since 1.2
- * @see Transactional
- * @see TransactionAnnotationParser
- * @see SpringTransactionAnnotationParser
- * @see Ejb3TransactionAnnotationParser
- * @see org.springframework.transaction.interceptor.TransactionInterceptor#setTransactionAttributeSource
- * @see org.springframework.transaction.interceptor.TransactionProxyFactoryBean#setTransactionAttributeSource
+ * @author Oliver Drotbohm
+ * @author Mark Paluch
*/
-
@SuppressWarnings({ "serial", "null" })
- static class CustomAnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource
- implements Serializable {
-
- private static final boolean jta12Present = ClassUtils.isPresent("javax.transaction.Transactional",
- CustomAnnotationTransactionAttributeSource.class.getClassLoader());
+ static class RepositoryInformationPreferringAnnotationTransactionAttributeSource
+ extends AnnotationTransactionAttributeSource implements Serializable {
- private static final boolean ejb3Present = ClassUtils.isPresent("javax.ejb.TransactionAttribute",
- CustomAnnotationTransactionAttributeSource.class.getClassLoader());
+ private final boolean enableDefaultTransactions;
- private final boolean publicMethodsOnly;
-
- private final Set annotationParsers;
+ private final @Nullable RepositoryInformation repositoryInformation;
/**
* Create a default CustomAnnotationTransactionAttributeSource, supporting public methods that carry the
* {@code Transactional} annotation or the EJB3 {@link javax.ejb.TransactionAttribute} annotation.
*/
- public CustomAnnotationTransactionAttributeSource() {
- this(true);
+ public RepositoryInformationPreferringAnnotationTransactionAttributeSource() {
+ this(true, null);
}
/**
- * Create a custom CustomAnnotationTransactionAttributeSource, supporting public methods that carry the
+ * Create a default CustomAnnotationTransactionAttributeSource, supporting public methods that carry the
* {@code Transactional} annotation or the EJB3 {@link javax.ejb.TransactionAttribute} annotation.
- *
- * @param publicMethodsOnly whether to support public methods that carry the {@code Transactional} annotation only
- * (typically for use with proxy-based AOP), or protected/private methods as well (typically used with
- * AspectJ class weaving)
- */
- public CustomAnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
- this.publicMethodsOnly = publicMethodsOnly;
- this.annotationParsers = new LinkedHashSet<>(2);
- this.annotationParsers.add(new SpringTransactionAnnotationParser());
- if (jta12Present) {
- this.annotationParsers.add(new JtaTransactionAnnotationParser());
- }
- if (ejb3Present) {
- this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
- }
- }
-
- /**
- * Create a custom CustomAnnotationTransactionAttributeSource.
- *
- * @param annotationParser the TransactionAnnotationParser to use
- */
- public CustomAnnotationTransactionAttributeSource(TransactionAnnotationParser annotationParser) {
- this.publicMethodsOnly = true;
- Assert.notNull(annotationParser, "TransactionAnnotationParser must not be null");
- this.annotationParsers = Collections.singleton(annotationParser);
- }
-
- /**
- * Create a custom CustomAnnotationTransactionAttributeSource.
- *
- * @param annotationParsers the TransactionAnnotationParsers to use
- */
- public CustomAnnotationTransactionAttributeSource(TransactionAnnotationParser... annotationParsers) {
- this.publicMethodsOnly = true;
- Assert.notEmpty(annotationParsers, "At least one TransactionAnnotationParser needs to be specified");
- Set parsers = new LinkedHashSet<>(annotationParsers.length);
- Collections.addAll(parsers, annotationParsers);
- this.annotationParsers = parsers;
- }
-
- /**
- * Create a custom CustomAnnotationTransactionAttributeSource.
- *
- * @param annotationParsers the TransactionAnnotationParsers to use
- */
- public CustomAnnotationTransactionAttributeSource(Set annotationParsers) {
- this.publicMethodsOnly = true;
- Assert.notEmpty(annotationParsers, "At least one TransactionAnnotationParser needs to be specified");
- this.annotationParsers = annotationParsers;
- }
-
- @Override
- protected TransactionAttribute findTransactionAttribute(Method method) {
- return determineTransactionAttribute(method);
- }
-
- @Override
- protected TransactionAttribute findTransactionAttribute(Class> clazz) {
- return determineTransactionAttribute(clazz);
- }
-
- /**
- * Determine the transaction attribute for the given method or class.
- *
- * This implementation delegates to configured {@link TransactionAnnotationParser TransactionAnnotationParsers} for
- * parsing known annotations into Spring's metadata attribute class. Returns {@code null} if it's not transactional.
- *
- * Can be overridden to support custom annotations that carry transaction metadata.
- *
- * @param ae the annotated method or class
- * @return TransactionAttribute the configured transaction attribute, or {@code null} if none was found
- */
- protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {
- for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
- TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
- if (attr != null) {
- return attr;
- }
- }
- return null;
- }
-
- /**
- * By default, only public methods can be made transactional.
- */
- @Override
- protected boolean allowPublicMethodsOnly() {
- return this.publicMethodsOnly;
- }
-
- /*
- * (non-Javadoc)
- * @see java.lang.Object#equals(java.lang.Object)
- */
- @Override
- public boolean equals(Object other) {
- if (this == other) {
- return true;
- }
- if (!(other instanceof CustomAnnotationTransactionAttributeSource)) {
- return false;
- }
- CustomAnnotationTransactionAttributeSource otherTas = (CustomAnnotationTransactionAttributeSource) other;
- return (this.annotationParsers.equals(otherTas.annotationParsers)
- && this.publicMethodsOnly == otherTas.publicMethodsOnly);
- }
-
- /*
- * (non-Javadoc)
- * @see java.lang.Object#hashCode()
- */
- @Override
- public int hashCode() {
- return this.annotationParsers.hashCode();
- }
- }
-
- /**
- * Abstract implementation of {@link TransactionAttributeSource} that caches attributes for methods and implements a
- * fallback policy: 1. specific target method; 2. target class; 3. declaring method; 4. declaring class/interface.
- *
- * Defaults to using the target class's transaction attribute if none is associated with the target method. Any
- * transaction attribute associated with the target method completely overrides a class transaction attribute. If none
- * found on the target class, the interface that the invoked method has been called through (in case of a JDK proxy)
- * will be checked.
- *
- * This implementation caches attributes by method after they are first used. If it is ever desirable to allow dynamic
- * changing of transaction attributes (which is very unlikely), caching could be made configurable. Caching is
- * desirable because of the cost of evaluating rollback rules.
- *
- * @author Rod Johnson
- * @author Juergen Hoeller
- * @since 1.1
- */
- @SuppressWarnings({ "null", "unused" })
- abstract static class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource {
-
- /**
- * Canonical value held in cache to indicate no transaction attribute was found for this method, and we don't need
- * to look again.
- */
- private final static TransactionAttribute NULL_TRANSACTION_ATTRIBUTE = new DefaultTransactionAttribute();
-
- /**
- * Logger available to subclasses.
- *
- * As this base class is not marked Serializable, the logger will be recreated after serialization - provided that
- * the concrete subclass is Serializable.
- */
- protected final Logger logger = LoggerFactory.getLogger(getClass());
-
- /**
- * Cache of TransactionAttributes, keyed by DefaultCacheKey (Method + target Class).
- *
- * As this base class is not marked Serializable, the cache will be recreated after serialization - provided that
- * the concrete subclass is Serializable.
*/
- final Map