001/*
002 * Copyright 2002-2019 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.transaction.annotation;
018
019import java.io.Serializable;
020import java.lang.reflect.AnnotatedElement;
021import java.lang.reflect.Method;
022import java.util.Arrays;
023import java.util.Collections;
024import java.util.LinkedHashSet;
025import java.util.Set;
026
027import org.springframework.lang.Nullable;
028import org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource;
029import org.springframework.transaction.interceptor.TransactionAttribute;
030import org.springframework.util.Assert;
031import org.springframework.util.ClassUtils;
032
033/**
034 * Implementation of the
035 * {@link org.springframework.transaction.interceptor.TransactionAttributeSource}
036 * interface for working with transaction metadata in JDK 1.5+ annotation format.
037 *
038 * <p>This class reads Spring's JDK 1.5+ {@link Transactional} annotation and
039 * exposes corresponding transaction attributes to Spring's transaction infrastructure.
040 * Also supports JTA 1.2's {@link javax.transaction.Transactional} and EJB3's
041 * {@link javax.ejb.TransactionAttribute} annotation (if present).
042 * This class may also serve as base class for a custom TransactionAttributeSource,
043 * or get customized through {@link TransactionAnnotationParser} strategies.
044 *
045 * @author Colin Sampaleanu
046 * @author Juergen Hoeller
047 * @since 1.2
048 * @see Transactional
049 * @see TransactionAnnotationParser
050 * @see SpringTransactionAnnotationParser
051 * @see Ejb3TransactionAnnotationParser
052 * @see org.springframework.transaction.interceptor.TransactionInterceptor#setTransactionAttributeSource
053 * @see org.springframework.transaction.interceptor.TransactionProxyFactoryBean#setTransactionAttributeSource
054 */
055@SuppressWarnings("serial")
056public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource
057                implements Serializable {
058
059        private static final boolean jta12Present;
060
061        private static final boolean ejb3Present;
062
063        static {
064                ClassLoader classLoader = AnnotationTransactionAttributeSource.class.getClassLoader();
065                jta12Present = ClassUtils.isPresent("javax.transaction.Transactional", classLoader);
066                ejb3Present = ClassUtils.isPresent("javax.ejb.TransactionAttribute", classLoader);
067        }
068
069        private final boolean publicMethodsOnly;
070
071        private final Set<TransactionAnnotationParser> annotationParsers;
072
073
074        /**
075         * Create a default AnnotationTransactionAttributeSource, supporting
076         * public methods that carry the {@code Transactional} annotation
077         * or the EJB3 {@link javax.ejb.TransactionAttribute} annotation.
078         */
079        public AnnotationTransactionAttributeSource() {
080                this(true);
081        }
082
083        /**
084         * Create a custom AnnotationTransactionAttributeSource, supporting
085         * public methods that carry the {@code Transactional} annotation
086         * or the EJB3 {@link javax.ejb.TransactionAttribute} annotation.
087         * @param publicMethodsOnly whether to support public methods that carry
088         * the {@code Transactional} annotation only (typically for use
089         * with proxy-based AOP), or protected/private methods as well
090         * (typically used with AspectJ class weaving)
091         */
092        public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
093                this.publicMethodsOnly = publicMethodsOnly;
094                if (jta12Present || ejb3Present) {
095                        this.annotationParsers = new LinkedHashSet<>(4);
096                        this.annotationParsers.add(new SpringTransactionAnnotationParser());
097                        if (jta12Present) {
098                                this.annotationParsers.add(new JtaTransactionAnnotationParser());
099                        }
100                        if (ejb3Present) {
101                                this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
102                        }
103                }
104                else {
105                        this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());
106                }
107        }
108
109        /**
110         * Create a custom AnnotationTransactionAttributeSource.
111         * @param annotationParser the TransactionAnnotationParser to use
112         */
113        public AnnotationTransactionAttributeSource(TransactionAnnotationParser annotationParser) {
114                this.publicMethodsOnly = true;
115                Assert.notNull(annotationParser, "TransactionAnnotationParser must not be null");
116                this.annotationParsers = Collections.singleton(annotationParser);
117        }
118
119        /**
120         * Create a custom AnnotationTransactionAttributeSource.
121         * @param annotationParsers the TransactionAnnotationParsers to use
122         */
123        public AnnotationTransactionAttributeSource(TransactionAnnotationParser... annotationParsers) {
124                this.publicMethodsOnly = true;
125                Assert.notEmpty(annotationParsers, "At least one TransactionAnnotationParser needs to be specified");
126                this.annotationParsers = new LinkedHashSet<>(Arrays.asList(annotationParsers));
127        }
128
129        /**
130         * Create a custom AnnotationTransactionAttributeSource.
131         * @param annotationParsers the TransactionAnnotationParsers to use
132         */
133        public AnnotationTransactionAttributeSource(Set<TransactionAnnotationParser> annotationParsers) {
134                this.publicMethodsOnly = true;
135                Assert.notEmpty(annotationParsers, "At least one TransactionAnnotationParser needs to be specified");
136                this.annotationParsers = annotationParsers;
137        }
138
139
140        @Override
141        public boolean isCandidateClass(Class<?> targetClass) {
142                for (TransactionAnnotationParser parser : this.annotationParsers) {
143                        if (parser.isCandidateClass(targetClass)) {
144                                return true;
145                        }
146                }
147                return false;
148        }
149
150        @Override
151        @Nullable
152        protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {
153                return determineTransactionAttribute(clazz);
154        }
155
156        @Override
157        @Nullable
158        protected TransactionAttribute findTransactionAttribute(Method method) {
159                return determineTransactionAttribute(method);
160        }
161
162        /**
163         * Determine the transaction attribute for the given method or class.
164         * <p>This implementation delegates to configured
165         * {@link TransactionAnnotationParser TransactionAnnotationParsers}
166         * for parsing known annotations into Spring's metadata attribute class.
167         * Returns {@code null} if it's not transactional.
168         * <p>Can be overridden to support custom annotations that carry transaction metadata.
169         * @param element the annotated method or class
170         * @return the configured transaction attribute, or {@code null} if none was found
171         */
172        @Nullable
173        protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
174                for (TransactionAnnotationParser parser : this.annotationParsers) {
175                        TransactionAttribute attr = parser.parseTransactionAnnotation(element);
176                        if (attr != null) {
177                                return attr;
178                        }
179                }
180                return null;
181        }
182
183        /**
184         * By default, only public methods can be made transactional.
185         */
186        @Override
187        protected boolean allowPublicMethodsOnly() {
188                return this.publicMethodsOnly;
189        }
190
191
192        @Override
193        public boolean equals(@Nullable Object other) {
194                if (this == other) {
195                        return true;
196                }
197                if (!(other instanceof AnnotationTransactionAttributeSource)) {
198                        return false;
199                }
200                AnnotationTransactionAttributeSource otherTas = (AnnotationTransactionAttributeSource) other;
201                return (this.annotationParsers.equals(otherTas.annotationParsers) &&
202                                this.publicMethodsOnly == otherTas.publicMethodsOnly);
203        }
204
205        @Override
206        public int hashCode() {
207                return this.annotationParsers.hashCode();
208        }
209
210}