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.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.aop.support.AopUtils; 028import org.springframework.core.MethodClassKey; 029import org.springframework.lang.Nullable; 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 static final 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<>(1024); 055 056 057 @Override 058 public JCacheOperation<?> getCacheOperation(Method method, @Nullable 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 @Nullable 081 private JCacheOperation<?> computeCacheOperation(Method method, @Nullable Class<?> targetClass) { 082 // Don't allow no-public methods as required. 083 if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { 084 return null; 085 } 086 087 // The method may be on an interface, but we need attributes from the target class. 088 // If the target class is null, the method will be unchanged. 089 Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); 090 091 // First try is the method in the target class. 092 JCacheOperation<?> operation = findCacheOperation(specificMethod, targetClass); 093 if (operation != null) { 094 return operation; 095 } 096 if (specificMethod != method) { 097 // Fallback is to look at the original method. 098 operation = findCacheOperation(method, targetClass); 099 if (operation != null) { 100 return operation; 101 } 102 } 103 return null; 104 } 105 106 107 /** 108 * Subclasses need to implement this to return the caching operation 109 * for the given method, if any. 110 * @param method the method to retrieve the operation for 111 * @param targetType the target class 112 * @return the cache operation associated with this method 113 * (or {@code null} if none) 114 */ 115 @Nullable 116 protected abstract JCacheOperation<?> findCacheOperation(Method method, @Nullable 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}