/* * Copyright 2002-2007 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.transaction.annotation; import java.io.Serializable; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource; import org.springframework.transaction.interceptor.TransactionAttribute; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** * Implementation of the * {@link org.springframework.transaction.interceptor.TransactionAttributeSource} * interface for working with transaction metadata in JDK 1.5+ annotation format. * * <p>This class reads Spring's JDK 1.5+ {@link Transactional} annotation and * exposes corresponding transaction attributes to Spring's transaction infrastructure. * Also supports 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. * * <p>This is a direct alternative to * {@link org.springframework.transaction.interceptor.AttributesTransactionAttributeSource}, * which is able to read in source-level attributes via Commons Attributes. * * @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 */ public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource implements Serializable { private static final boolean ejb3Present = ClassUtils.isPresent( "javax.ejb.TransactionAttribute", AnnotationTransactionAttributeSource.class.getClassLoader()); private final boolean publicMethodsOnly; private final Set<TransactionAnnotationParser> annotationParsers; /** * Create a default AnnotationTransactionAttributeSource, supporting * public methods that carry the <code>Transactional</code> annotation * or the EJB3 {@link javax.ejb.TransactionAttribute} annotation. */ public AnnotationTransactionAttributeSource() { this(true); } /** * Create a custom AnnotationTransactionAttributeSource, supporting * public methods that carry the <code>Transactional</code> annotation * or the EJB3 {@link javax.ejb.TransactionAttribute} annotation. * @param publicMethodsOnly whether to support public methods that carry * the <code>Transactional</code> annotation only (typically for use * with proxy-based AOP), or protected/private methods as well * (typically used with AspectJ class weaving) */ public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) { this.publicMethodsOnly = publicMethodsOnly; this.annotationParsers = new LinkedHashSet<TransactionAnnotationParser>(2); this.annotationParsers.add(new SpringTransactionAnnotationParser()); if (ejb3Present) { this.annotationParsers.add(new Ejb3TransactionAnnotationParser()); } } /** * Create a custom AnnotationTransactionAttributeSource. * @param annotationParser the TransactionAnnotationParser to use */ public AnnotationTransactionAttributeSource(TransactionAnnotationParser annotationParser) { this.publicMethodsOnly = true; Assert.notNull(annotationParser, "TransactionAnnotationParser must not be null"); this.annotationParsers = Collections.singleton(annotationParser); } /** * Create a custom AnnotationTransactionAttributeSource. * @param annotationParsers the TransactionAnnotationParsers to use */ public AnnotationTransactionAttributeSource(Set<TransactionAnnotationParser> annotationParsers) { this.publicMethodsOnly = true; Assert.notEmpty(annotationParsers, "At least one TransactionAnnotationParser needs to be specified"); this.annotationParsers = annotationParsers; } protected TransactionAttribute findTransactionAttribute(Method method) { return determineTransactionAttribute(method); } protected TransactionAttribute findTransactionAttribute(Class clazz) { return determineTransactionAttribute(clazz); } /** * Determine the transaction attribute for the given method or class. * <p>This implementation delegates to configured * {@link TransactionAnnotationParser TransactionAnnotationParsers} * for parsing known annotations into Spring's metadata attribute class. * Returns <code>null</code> if it's not transactional. * <p>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</code> 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. */ protected boolean allowPublicMethodsOnly() { return this.publicMethodsOnly; } public boolean equals(Object other) { return (this == other || other instanceof AnnotationTransactionAttributeSource); } public int hashCode() { return AnnotationTransactionAttributeSource.class.hashCode(); } }