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}