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.interceptor; 018 019import java.lang.reflect.Method; 020import java.lang.reflect.Modifier; 021import java.util.Map; 022import java.util.concurrent.ConcurrentHashMap; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026 027import org.springframework.aop.support.AopUtils; 028import org.springframework.core.MethodClassKey; 029import org.springframework.lang.Nullable; 030import org.springframework.util.ClassUtils; 031 032/** 033 * Abstract implementation of {@link TransactionAttributeSource} that caches 034 * attributes for methods and implements a fallback policy: 1. specific target 035 * method; 2. target class; 3. declaring method; 4. declaring class/interface. 036 * 037 * <p>Defaults to using the target class's transaction attribute if none is 038 * associated with the target method. Any transaction attribute associated with 039 * the target method completely overrides a class transaction attribute. 040 * If none found on the target class, the interface that the invoked method 041 * has been called through (in case of a JDK proxy) will be checked. 042 * 043 * <p>This implementation caches attributes by method after they are first used. 044 * If it is ever desirable to allow dynamic changing of transaction attributes 045 * (which is very unlikely), caching could be made configurable. Caching is 046 * desirable because of the cost of evaluating rollback rules. 047 * 048 * @author Rod Johnson 049 * @author Juergen Hoeller 050 * @since 1.1 051 */ 052public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource { 053 054 /** 055 * Canonical value held in cache to indicate no transaction attribute was 056 * found for this method, and we don't need to look again. 057 */ 058 @SuppressWarnings("serial") 059 private static final TransactionAttribute NULL_TRANSACTION_ATTRIBUTE = new DefaultTransactionAttribute() { 060 @Override 061 public String toString() { 062 return "null"; 063 } 064 }; 065 066 067 /** 068 * Logger available to subclasses. 069 * <p>As this base class is not marked Serializable, the logger will be recreated 070 * after serialization - provided that the concrete subclass is Serializable. 071 */ 072 protected final Log logger = LogFactory.getLog(getClass()); 073 074 /** 075 * Cache of TransactionAttributes, keyed by method on a specific target class. 076 * <p>As this base class is not marked Serializable, the cache will be recreated 077 * after serialization - provided that the concrete subclass is Serializable. 078 */ 079 private final Map<Object, TransactionAttribute> attributeCache = new ConcurrentHashMap<>(1024); 080 081 082 /** 083 * Determine the transaction attribute for this method invocation. 084 * <p>Defaults to the class's transaction attribute if no method attribute is found. 085 * @param method the method for the current invocation (never {@code null}) 086 * @param targetClass the target class for this invocation (may be {@code null}) 087 * @return a TransactionAttribute for this method, or {@code null} if the method 088 * is not transactional 089 */ 090 @Override 091 @Nullable 092 public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) { 093 if (method.getDeclaringClass() == Object.class) { 094 return null; 095 } 096 097 // First, see if we have a cached value. 098 Object cacheKey = getCacheKey(method, targetClass); 099 TransactionAttribute cached = this.attributeCache.get(cacheKey); 100 if (cached != null) { 101 // Value will either be canonical value indicating there is no transaction attribute, 102 // or an actual transaction attribute. 103 if (cached == NULL_TRANSACTION_ATTRIBUTE) { 104 return null; 105 } 106 else { 107 return cached; 108 } 109 } 110 else { 111 // We need to work it out. 112 TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass); 113 // Put it in the cache. 114 if (txAttr == null) { 115 this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE); 116 } 117 else { 118 String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass); 119 if (txAttr instanceof DefaultTransactionAttribute) { 120 ((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification); 121 } 122 if (logger.isTraceEnabled()) { 123 logger.trace("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr); 124 } 125 this.attributeCache.put(cacheKey, txAttr); 126 } 127 return txAttr; 128 } 129 } 130 131 /** 132 * Determine a cache key for the given method and target class. 133 * <p>Must not produce same key for overloaded methods. 134 * Must produce same key for different instances of the same method. 135 * @param method the method (never {@code null}) 136 * @param targetClass the target class (may be {@code null}) 137 * @return the cache key (never {@code null}) 138 */ 139 protected Object getCacheKey(Method method, @Nullable Class<?> targetClass) { 140 return new MethodClassKey(method, targetClass); 141 } 142 143 /** 144 * Same signature as {@link #getTransactionAttribute}, but doesn't cache the result. 145 * {@link #getTransactionAttribute} is effectively a caching decorator for this method. 146 * <p>As of 4.1.8, this method can be overridden. 147 * @since 4.1.8 148 * @see #getTransactionAttribute 149 */ 150 @Nullable 151 protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) { 152 // Don't allow no-public methods as required. 153 if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { 154 return null; 155 } 156 157 // The method may be on an interface, but we need attributes from the target class. 158 // If the target class is null, the method will be unchanged. 159 Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); 160 161 // First try is the method in the target class. 162 TransactionAttribute txAttr = findTransactionAttribute(specificMethod); 163 if (txAttr != null) { 164 return txAttr; 165 } 166 167 // Second try is the transaction attribute on the target class. 168 txAttr = findTransactionAttribute(specificMethod.getDeclaringClass()); 169 if (txAttr != null && ClassUtils.isUserLevelMethod(method)) { 170 return txAttr; 171 } 172 173 if (specificMethod != method) { 174 // Fallback is to look at the original method. 175 txAttr = findTransactionAttribute(method); 176 if (txAttr != null) { 177 return txAttr; 178 } 179 // Last fallback is the class of the original method. 180 txAttr = findTransactionAttribute(method.getDeclaringClass()); 181 if (txAttr != null && ClassUtils.isUserLevelMethod(method)) { 182 return txAttr; 183 } 184 } 185 186 return null; 187 } 188 189 190 /** 191 * Subclasses need to implement this to return the transaction attribute for the 192 * given class, if any. 193 * @param clazz the class to retrieve the attribute for 194 * @return all transaction attribute associated with this class, or {@code null} if none 195 */ 196 @Nullable 197 protected abstract TransactionAttribute findTransactionAttribute(Class<?> clazz); 198 199 /** 200 * Subclasses need to implement this to return the transaction attribute for the 201 * given method, if any. 202 * @param method the method to retrieve the attribute for 203 * @return all transaction attribute associated with this method, or {@code null} if none 204 */ 205 @Nullable 206 protected abstract TransactionAttribute findTransactionAttribute(Method method); 207 208 /** 209 * Should only public methods be allowed to have transactional semantics? 210 * <p>The default implementation returns {@code false}. 211 */ 212 protected boolean allowPublicMethodsOnly() { 213 return false; 214 } 215 216}