001/* 002 * Copyright 2002-2018 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.Collections; 023import java.util.LinkedHashSet; 024import java.util.Set; 025 026import org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource; 027import org.springframework.transaction.interceptor.TransactionAttribute; 028import org.springframework.util.Assert; 029import org.springframework.util.ClassUtils; 030 031/** 032 * Implementation of the 033 * {@link org.springframework.transaction.interceptor.TransactionAttributeSource} 034 * interface for working with transaction metadata in JDK 1.5+ annotation format. 035 * 036 * <p>This class reads Spring's JDK 1.5+ {@link Transactional} annotation and 037 * exposes corresponding transaction attributes to Spring's transaction infrastructure. 038 * Also supports JTA 1.2's {@link javax.transaction.Transactional} and EJB3's 039 * {@link javax.ejb.TransactionAttribute} annotation (if present). 040 * This class may also serve as base class for a custom TransactionAttributeSource, 041 * or get customized through {@link TransactionAnnotationParser} strategies. 042 * 043 * @author Colin Sampaleanu 044 * @author Juergen Hoeller 045 * @since 1.2 046 * @see Transactional 047 * @see TransactionAnnotationParser 048 * @see SpringTransactionAnnotationParser 049 * @see Ejb3TransactionAnnotationParser 050 * @see org.springframework.transaction.interceptor.TransactionInterceptor#setTransactionAttributeSource 051 * @see org.springframework.transaction.interceptor.TransactionProxyFactoryBean#setTransactionAttributeSource 052 */ 053@SuppressWarnings("serial") 054public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource 055 implements Serializable { 056 057 private static final boolean jta12Present = ClassUtils.isPresent( 058 "javax.transaction.Transactional", AnnotationTransactionAttributeSource.class.getClassLoader()); 059 060 private static final boolean ejb3Present = ClassUtils.isPresent( 061 "javax.ejb.TransactionAttribute", AnnotationTransactionAttributeSource.class.getClassLoader()); 062 063 private final boolean publicMethodsOnly; 064 065 private final Set<TransactionAnnotationParser> annotationParsers; 066 067 068 /** 069 * Create a default AnnotationTransactionAttributeSource, supporting 070 * public methods that carry the {@code Transactional} annotation 071 * or the EJB3 {@link javax.ejb.TransactionAttribute} annotation. 072 */ 073 public AnnotationTransactionAttributeSource() { 074 this(true); 075 } 076 077 /** 078 * Create a custom AnnotationTransactionAttributeSource, supporting 079 * public methods that carry the {@code Transactional} annotation 080 * or the EJB3 {@link javax.ejb.TransactionAttribute} annotation. 081 * @param publicMethodsOnly whether to support public methods that carry 082 * the {@code Transactional} annotation only (typically for use 083 * with proxy-based AOP), or protected/private methods as well 084 * (typically used with AspectJ class weaving) 085 */ 086 public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) { 087 this.publicMethodsOnly = publicMethodsOnly; 088 this.annotationParsers = new LinkedHashSet<TransactionAnnotationParser>(4); 089 this.annotationParsers.add(new SpringTransactionAnnotationParser()); 090 if (jta12Present) { 091 this.annotationParsers.add(new JtaTransactionAnnotationParser()); 092 } 093 if (ejb3Present) { 094 this.annotationParsers.add(new Ejb3TransactionAnnotationParser()); 095 } 096 } 097 098 /** 099 * Create a custom AnnotationTransactionAttributeSource. 100 * @param annotationParser the TransactionAnnotationParser to use 101 */ 102 public AnnotationTransactionAttributeSource(TransactionAnnotationParser annotationParser) { 103 this.publicMethodsOnly = true; 104 Assert.notNull(annotationParser, "TransactionAnnotationParser must not be null"); 105 this.annotationParsers = Collections.singleton(annotationParser); 106 } 107 108 /** 109 * Create a custom AnnotationTransactionAttributeSource. 110 * @param annotationParsers the TransactionAnnotationParsers to use 111 */ 112 public AnnotationTransactionAttributeSource(TransactionAnnotationParser... annotationParsers) { 113 this.publicMethodsOnly = true; 114 Assert.notEmpty(annotationParsers, "At least one TransactionAnnotationParser needs to be specified"); 115 Set<TransactionAnnotationParser> parsers = new LinkedHashSet<TransactionAnnotationParser>(annotationParsers.length); 116 Collections.addAll(parsers, annotationParsers); 117 this.annotationParsers = parsers; 118 } 119 120 /** 121 * Create a custom AnnotationTransactionAttributeSource. 122 * @param annotationParsers the TransactionAnnotationParsers to use 123 */ 124 public AnnotationTransactionAttributeSource(Set<TransactionAnnotationParser> annotationParsers) { 125 this.publicMethodsOnly = true; 126 Assert.notEmpty(annotationParsers, "At least one TransactionAnnotationParser needs to be specified"); 127 this.annotationParsers = annotationParsers; 128 } 129 130 131 @Override 132 protected TransactionAttribute findTransactionAttribute(Class<?> clazz) { 133 return determineTransactionAttribute(clazz); 134 } 135 136 @Override 137 protected TransactionAttribute findTransactionAttribute(Method method) { 138 return determineTransactionAttribute(method); 139 } 140 141 /** 142 * Determine the transaction attribute for the given method or class. 143 * <p>This implementation delegates to configured 144 * {@link TransactionAnnotationParser TransactionAnnotationParsers} 145 * for parsing known annotations into Spring's metadata attribute class. 146 * Returns {@code null} if it's not transactional. 147 * <p>Can be overridden to support custom annotations that carry transaction metadata. 148 * @param element the annotated method or class 149 * @return the configured transaction attribute, or {@code null} if none was found 150 */ 151 protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) { 152 if (element.getAnnotations().length > 0) { 153 for (TransactionAnnotationParser annotationParser : this.annotationParsers) { 154 TransactionAttribute attr = annotationParser.parseTransactionAnnotation(element); 155 if (attr != null) { 156 return attr; 157 } 158 } 159 } 160 return null; 161 } 162 163 /** 164 * By default, only public methods can be made transactional. 165 */ 166 @Override 167 protected boolean allowPublicMethodsOnly() { 168 return this.publicMethodsOnly; 169 } 170 171 172 @Override 173 public boolean equals(Object other) { 174 if (this == other) { 175 return true; 176 } 177 if (!(other instanceof AnnotationTransactionAttributeSource)) { 178 return false; 179 } 180 AnnotationTransactionAttributeSource otherTas = (AnnotationTransactionAttributeSource) other; 181 return (this.annotationParsers.equals(otherTas.annotationParsers) && 182 this.publicMethodsOnly == otherTas.publicMethodsOnly); 183 } 184 185 @Override 186 public int hashCode() { 187 return this.annotationParsers.hashCode(); 188 } 189 190}