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