001/* 002 * Copyright 2002-2016 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.cache.jcache.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 JCacheOperationSource} that caches attributes 033 * for methods and implements a fallback policy: 1. specific target method; 034 * 2. declaring method. 035 * 036 * <p>This implementation caches attributes by method after they are first used. 037 * 038 * @author Stephane Nicoll 039 * @author Juergen Hoeller 040 * @since 4.1 041 * @see org.springframework.cache.interceptor.AbstractFallbackCacheOperationSource 042 */ 043public abstract class AbstractFallbackJCacheOperationSource implements JCacheOperationSource { 044 045 /** 046 * Canonical value held in cache to indicate no caching attribute was 047 * found for this method and we don't need to look again. 048 */ 049 private final static Object NULL_CACHING_ATTRIBUTE = new Object(); 050 051 052 protected final Log logger = LogFactory.getLog(getClass()); 053 054 private final Map<MethodClassKey, Object> cache = new ConcurrentHashMap<MethodClassKey, Object>(1024); 055 056 057 @Override 058 public JCacheOperation<?> getCacheOperation(Method method, Class<?> targetClass) { 059 MethodClassKey cacheKey = new MethodClassKey(method, targetClass); 060 Object cached = this.cache.get(cacheKey); 061 062 if (cached != null) { 063 return (cached != NULL_CACHING_ATTRIBUTE ? (JCacheOperation<?>) cached : null); 064 } 065 else { 066 JCacheOperation<?> operation = computeCacheOperation(method, targetClass); 067 if (operation != null) { 068 if (logger.isDebugEnabled()) { 069 logger.debug("Adding cacheable method '" + method.getName() + "' with operation: " + operation); 070 } 071 this.cache.put(cacheKey, operation); 072 } 073 else { 074 this.cache.put(cacheKey, NULL_CACHING_ATTRIBUTE); 075 } 076 return operation; 077 } 078 } 079 080 private JCacheOperation<?> computeCacheOperation(Method method, Class<?> targetClass) { 081 // Don't allow no-public methods as required. 082 if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { 083 return null; 084 } 085 086 // The method may be on an interface, but we need attributes from the target class. 087 // If the target class is null, the method will be unchanged. 088 Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); 089 // If we are dealing with method with generic parameters, find the original method. 090 specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); 091 092 // First try is the method in the target class. 093 JCacheOperation<?> operation = findCacheOperation(specificMethod, targetClass); 094 if (operation != null) { 095 return operation; 096 } 097 if (specificMethod != method) { 098 // Fallback is to look at the original method. 099 operation = findCacheOperation(method, targetClass); 100 if (operation != null) { 101 return operation; 102 } 103 } 104 return null; 105 } 106 107 108 /** 109 * Subclasses need to implement this to return the caching operation 110 * for the given method, if any. 111 * @param method the method to retrieve the operation for 112 * @param targetType the target class 113 * @return the cache operation associated with this method 114 * (or {@code null} if none) 115 */ 116 protected abstract JCacheOperation<?> findCacheOperation(Method method, Class<?> targetType); 117 118 /** 119 * Should only public methods be allowed to have caching semantics? 120 * <p>The default implementation returns {@code false}. 121 */ 122 protected boolean allowPublicMethodsOnly() { 123 return false; 124 } 125 126}