소스 검색

修复@CacheConfig 无法在子类上使用的问题

zhouhao 7 년 전
부모
커밋
0deaedf4c4

+ 6 - 1
hsweb-concurrent/hsweb-concurrent-cache/pom.xml

@@ -28,5 +28,10 @@
 
     <artifactId>hsweb-concurrent-cache</artifactId>
 
-
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+        </dependency>
+    </dependencies>
 </project>

+ 22 - 0
hsweb-concurrent/hsweb-concurrent-cache/src/main/java/org/hswebframework/web/cache/FixUseSupperClassAutoConfiguration.java

@@ -0,0 +1,22 @@
+package org.hswebframework.web.cache;
+
+import org.hswebframework.web.cache.spring.fix.FixUseSupperClassAnnotationParser;
+import org.hswebframework.web.cache.spring.fix.FixUseSupperClassCacheOperationSource;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.cache.interceptor.CacheOperationSource;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Role;
+
+/**
+ *
+ * @author zhouhao
+ */
+@Configuration
+public class FixUseSupperClassAutoConfiguration {
+    @Bean
+    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+    public CacheOperationSource cacheOperationSource() {
+        return new FixUseSupperClassCacheOperationSource(new FixUseSupperClassAnnotationParser());
+    }
+}

+ 296 - 0
hsweb-concurrent/hsweb-concurrent-cache/src/main/java/org/hswebframework/web/cache/spring/fix/FixUseSupperClassAnnotationParser.java

@@ -0,0 +1,296 @@
+/*
+ * Copyright 2002-2017 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.hswebframework.web.cache.spring.fix;
+
+import org.springframework.cache.annotation.*;
+import org.springframework.cache.interceptor.CacheEvictOperation;
+import org.springframework.cache.interceptor.CacheOperation;
+import org.springframework.cache.interceptor.CachePutOperation;
+import org.springframework.cache.interceptor.CacheableOperation;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Strategy implementation for parsing Spring's {@link Caching}, {@link Cacheable},
+ * {@link CacheEvict}, and {@link CachePut} annotations.
+ *
+ * @author Costin Leau
+ * @author Juergen Hoeller
+ * @author Chris Beams
+ * @author Phillip Webb
+ * @author Stephane Nicoll
+ * @author Sam Brannen
+ * @since 3.1
+ */
+@SuppressWarnings("serial")
+public class FixUseSupperClassAnnotationParser implements FixUseSupperClassCacheAnnotationParser, Serializable {
+    @Override
+    public Collection<CacheOperation> parseCacheAnnotations(Class targetClass, Method method) {
+        DefaultCacheConfig defaultConfig = getDefaultCacheConfig(targetClass);
+        return parseCacheAnnotations(defaultConfig, method);
+    }
+
+    @Override
+    public Collection<CacheOperation> parseCacheAnnotations(Class<?> type) {
+        DefaultCacheConfig defaultConfig = getDefaultCacheConfig(type);
+        return parseCacheAnnotations(defaultConfig, type);
+    }
+
+    @Override
+    public Collection<CacheOperation> parseCacheAnnotations(Method method) {
+        DefaultCacheConfig defaultConfig = getDefaultCacheConfig(method.getDeclaringClass());
+        return parseCacheAnnotations(defaultConfig, method);
+    }
+
+    protected Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
+        Collection<CacheOperation> ops = null;
+
+        Collection<Cacheable> cacheables = AnnotatedElementUtils.getAllMergedAnnotations(ae, Cacheable.class);
+        if (!cacheables.isEmpty()) {
+            ops = lazyInit(ops);
+            for (Cacheable cacheable : cacheables) {
+                ops.add(parseCacheableAnnotation(ae, cachingConfig, cacheable));
+            }
+        }
+        Collection<CacheEvict> evicts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CacheEvict.class);
+        if (!evicts.isEmpty()) {
+            ops = lazyInit(ops);
+            for (CacheEvict evict : evicts) {
+                ops.add(parseEvictAnnotation(ae, cachingConfig, evict));
+            }
+        }
+        Collection<CachePut> puts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CachePut.class);
+        if (!puts.isEmpty()) {
+            ops = lazyInit(ops);
+            for (CachePut put : puts) {
+                ops.add(parsePutAnnotation(ae, cachingConfig, put));
+            }
+        }
+        Collection<Caching> cachings = AnnotatedElementUtils.getAllMergedAnnotations(ae, Caching.class);
+        if (!cachings.isEmpty()) {
+            ops = lazyInit(ops);
+            for (Caching caching : cachings) {
+                Collection<CacheOperation> cachingOps = parseCachingAnnotation(ae, cachingConfig, caching);
+                if (cachingOps != null) {
+                    ops.addAll(cachingOps);
+                }
+            }
+        }
+
+        return ops;
+    }
+
+    private <T extends Annotation> Collection<CacheOperation> lazyInit(Collection<CacheOperation> ops) {
+        return (ops != null ? ops : new ArrayList<CacheOperation>(1));
+    }
+
+    CacheableOperation parseCacheableAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Cacheable cacheable) {
+        CacheableOperation.Builder builder = new CacheableOperation.Builder();
+
+        builder.setName(ae.toString());
+        builder.setCacheNames(cacheable.cacheNames());
+        builder.setCondition(cacheable.condition());
+        builder.setUnless(cacheable.unless());
+        builder.setKey(cacheable.key());
+        builder.setKeyGenerator(cacheable.keyGenerator());
+        builder.setCacheManager(cacheable.cacheManager());
+        builder.setCacheResolver(cacheable.cacheResolver());
+        builder.setSync(cacheable.sync());
+
+        defaultConfig.applyDefault(builder);
+        CacheableOperation op = builder.build();
+        validateCacheOperation(ae, op);
+
+        return op;
+    }
+
+    CacheEvictOperation parseEvictAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, CacheEvict cacheEvict) {
+        CacheEvictOperation.Builder builder = new CacheEvictOperation.Builder();
+
+        builder.setName(ae.toString());
+        builder.setCacheNames(cacheEvict.cacheNames());
+        builder.setCondition(cacheEvict.condition());
+        builder.setKey(cacheEvict.key());
+        builder.setKeyGenerator(cacheEvict.keyGenerator());
+        builder.setCacheManager(cacheEvict.cacheManager());
+        builder.setCacheResolver(cacheEvict.cacheResolver());
+        builder.setCacheWide(cacheEvict.allEntries());
+        builder.setBeforeInvocation(cacheEvict.beforeInvocation());
+
+        defaultConfig.applyDefault(builder);
+        CacheEvictOperation op = builder.build();
+        validateCacheOperation(ae, op);
+
+        return op;
+    }
+
+    CacheOperation parsePutAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, CachePut cachePut) {
+        CachePutOperation.Builder builder = new CachePutOperation.Builder();
+
+        builder.setName(ae.toString());
+        builder.setCacheNames(cachePut.cacheNames());
+        builder.setCondition(cachePut.condition());
+        builder.setUnless(cachePut.unless());
+        builder.setKey(cachePut.key());
+        builder.setKeyGenerator(cachePut.keyGenerator());
+        builder.setCacheManager(cachePut.cacheManager());
+        builder.setCacheResolver(cachePut.cacheResolver());
+
+        defaultConfig.applyDefault(builder);
+        CachePutOperation op = builder.build();
+        validateCacheOperation(ae, op);
+
+        return op;
+    }
+
+    Collection<CacheOperation> parseCachingAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Caching caching) {
+        Collection<CacheOperation> ops = null;
+
+        Cacheable[] cacheables = caching.cacheable();
+        if (!ObjectUtils.isEmpty(cacheables)) {
+            ops = lazyInit(ops);
+            for (Cacheable cacheable : cacheables) {
+                ops.add(parseCacheableAnnotation(ae, defaultConfig, cacheable));
+            }
+        }
+        CacheEvict[] cacheEvicts = caching.evict();
+        if (!ObjectUtils.isEmpty(cacheEvicts)) {
+            ops = lazyInit(ops);
+            for (CacheEvict cacheEvict : cacheEvicts) {
+                ops.add(parseEvictAnnotation(ae, defaultConfig, cacheEvict));
+            }
+        }
+        CachePut[] cachePuts = caching.put();
+        if (!ObjectUtils.isEmpty(cachePuts)) {
+            ops = lazyInit(ops);
+            for (CachePut cachePut : cachePuts) {
+                ops.add(parsePutAnnotation(ae, defaultConfig, cachePut));
+            }
+        }
+
+        return ops;
+    }
+
+    /**
+     * Provides the {@link DefaultCacheConfig} instance for the specified {@link Class}.
+     *
+     * @param target the class-level to handle
+     * @return the default config (never {@code null})
+     */
+    DefaultCacheConfig getDefaultCacheConfig(Class<?> target) {
+        CacheConfig annotation = AnnotatedElementUtils.getMergedAnnotation(target, CacheConfig.class);
+        if (annotation != null) {
+            return new DefaultCacheConfig(annotation.cacheNames(), annotation.keyGenerator(),
+                    annotation.cacheManager(), annotation.cacheResolver());
+        }
+        return new DefaultCacheConfig();
+    }
+
+    /**
+     * Validates the specified {@link CacheOperation}.
+     * <p>Throws an {@link IllegalStateException} if the state of the operation is
+     * invalid. As there might be multiple sources for default values, this ensure
+     * that the operation is in a proper state before being returned.
+     *
+     * @param ae        the annotated element of the cache operation
+     * @param operation the {@link CacheOperation} to validate
+     */
+    private void validateCacheOperation(AnnotatedElement ae, CacheOperation operation) {
+        if (StringUtils.hasText(operation.getKey()) && StringUtils.hasText(operation.getKeyGenerator())) {
+            throw new IllegalStateException("Invalid cache annotation configuration on '" +
+                    ae.toString() + "'. Both 'key' and 'keyGenerator' attributes have been set. " +
+                    "These attributes are mutually exclusive: either set the SpEL expression used to" +
+                    "compute the key at runtime or set the name of the KeyGenerator bean to use.");
+        }
+        if (StringUtils.hasText(operation.getCacheManager()) && StringUtils.hasText(operation.getCacheResolver())) {
+            throw new IllegalStateException("Invalid cache annotation configuration on '" +
+                    ae.toString() + "'. Both 'cacheManager' and 'cacheResolver' attributes have been set. " +
+                    "These attributes are mutually exclusive: the cache manager is used to configure a" +
+                    "default cache resolver if none is set. If a cache resolver is set, the cache manager" +
+                    "won't be used.");
+        }
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        return (this == other || other instanceof FixUseSupperClassAnnotationParser);
+    }
+
+    @Override
+    public int hashCode() {
+        return FixUseSupperClassAnnotationParser.class.hashCode();
+    }
+
+
+    /**
+     * Provides default settings for a given set of cache operations.
+     */
+    static class DefaultCacheConfig {
+
+        private final String[] cacheNames;
+
+        private final String keyGenerator;
+
+        private final String cacheManager;
+
+        private final String cacheResolver;
+
+        public DefaultCacheConfig() {
+            this(null, null, null, null);
+        }
+
+        private DefaultCacheConfig(String[] cacheNames, String keyGenerator, String cacheManager, String cacheResolver) {
+            this.cacheNames = cacheNames;
+            this.keyGenerator = keyGenerator;
+            this.cacheManager = cacheManager;
+            this.cacheResolver = cacheResolver;
+        }
+
+        /**
+         * Apply the defaults to the specified {@link CacheOperation.Builder}.
+         *
+         * @param builder the operation builder to update
+         */
+        public void applyDefault(CacheOperation.Builder builder) {
+            if (builder.getCacheNames().isEmpty() && this.cacheNames != null) {
+                builder.setCacheNames(this.cacheNames);
+            }
+            if (!StringUtils.hasText(builder.getKey()) && !StringUtils.hasText(builder.getKeyGenerator()) &&
+                    StringUtils.hasText(this.keyGenerator)) {
+                builder.setKeyGenerator(this.keyGenerator);
+            }
+
+            if (StringUtils.hasText(builder.getCacheManager()) || StringUtils.hasText(builder.getCacheResolver())) {
+                // One of these is set so we should not inherit anything
+            } else if (StringUtils.hasText(this.cacheResolver)) {
+                builder.setCacheResolver(this.cacheResolver);
+            } else if (StringUtils.hasText(this.cacheManager)) {
+                builder.setCacheManager(this.cacheManager);
+            }
+        }
+
+    }
+
+}

+ 17 - 0
hsweb-concurrent/hsweb-concurrent-cache/src/main/java/org/hswebframework/web/cache/spring/fix/FixUseSupperClassCacheAnnotationParser.java

@@ -0,0 +1,17 @@
+package org.hswebframework.web.cache.spring.fix;
+
+import org.springframework.cache.annotation.CacheAnnotationParser;
+import org.springframework.cache.interceptor.CacheOperation;
+
+import java.lang.reflect.Method;
+import java.util.Collection;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+public interface FixUseSupperClassCacheAnnotationParser extends CacheAnnotationParser {
+
+    Collection<CacheOperation> parseCacheAnnotations(Class targetClass, Method method);
+}

+ 193 - 0
hsweb-concurrent/hsweb-concurrent-cache/src/main/java/org/hswebframework/web/cache/spring/fix/FixUseSupperClassCacheOperationSource.java

@@ -0,0 +1,193 @@
+/*
+ * Copyright 2002-2016 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.hswebframework.web.cache.spring.fix;
+
+import org.springframework.cache.annotation.CacheAnnotationParser;
+import org.springframework.cache.interceptor.CacheOperation;
+import org.springframework.util.Assert;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.*;
+
+/**
+ * Implementation of the {@link org.springframework.cache.interceptor.CacheOperationSource
+ * CacheOperationSource} interface for working with caching metadata in annotation format.
+ * <p>
+ * <p>This class reads Spring's {@link org.springframework.cache.annotation.Cacheable}, {@link org.springframework.cache.annotation.CachePut} and {@link org.springframework.cache.annotation.CacheEvict}
+ * annotations and exposes corresponding caching operation definition to Spring's cache
+ * infrastructure. This class may also serve as base class for a custom
+ * {@code CacheOperationSource}.
+ *
+ * @author Costin Leau
+ * @author Juergen Hoeller
+ * @author Stephane Nicoll
+ * @since 3.1
+ */
+@SuppressWarnings("serial")
+public class FixUseSupperClassCacheOperationSource extends FixUseSupperClassFallbackCacheOperationSource implements Serializable {
+
+    private boolean publicMethodsOnly;
+
+    private final Set<FixUseSupperClassCacheAnnotationParser> annotationParsers;
+
+
+    /**
+     * Create a default AnnotationCacheOperationSource, supporting public methods
+     * that carry the {@code Cacheable} and {@code CacheEvict} annotations.
+     */
+    public FixUseSupperClassCacheOperationSource() {
+        this(true);
+    }
+
+    /**
+     * Create a default {@code AnnotationCacheOperationSource}, supporting public methods
+     * that carry the {@code Cacheable} and {@code CacheEvict} annotations.
+     *
+     * @param publicMethodsOnly whether to support only annotated public methods
+     *                          typically for use with proxy-based AOP), or protected/private methods as well
+     *                          (typically used with AspectJ class weaving)
+     */
+    public FixUseSupperClassCacheOperationSource(boolean publicMethodsOnly) {
+        this.publicMethodsOnly = publicMethodsOnly;
+        this.annotationParsers = new LinkedHashSet<>(1);
+        this.annotationParsers.add(new FixUseSupperClassAnnotationParser());
+    }
+
+    /**
+     * Create a custom AnnotationCacheOperationSource.
+     *
+     * @param annotationParser the CacheAnnotationParser to use
+     */
+    public FixUseSupperClassCacheOperationSource(FixUseSupperClassCacheAnnotationParser annotationParser) {
+        this.publicMethodsOnly = true;
+        Assert.notNull(annotationParser, "CacheAnnotationParser must not be null");
+        this.annotationParsers = Collections.singleton(annotationParser);
+    }
+
+    /**
+     * Create a custom AnnotationCacheOperationSource.
+     *
+     * @param annotationParsers the CacheAnnotationParser to use
+     */
+    public FixUseSupperClassCacheOperationSource(FixUseSupperClassCacheAnnotationParser... annotationParsers) {
+        this.publicMethodsOnly = true;
+        Assert.notEmpty(annotationParsers, "At least one CacheAnnotationParser needs to be specified");
+        Set<FixUseSupperClassCacheAnnotationParser> parsers = new LinkedHashSet<>(annotationParsers.length);
+        Collections.addAll(parsers, annotationParsers);
+        this.annotationParsers = parsers;
+    }
+
+    /**
+     * Create a custom AnnotationCacheOperationSource.
+     *
+     * @param annotationParsers the CacheAnnotationParser to use
+     */
+    public FixUseSupperClassCacheOperationSource(Set<FixUseSupperClassCacheAnnotationParser> annotationParsers) {
+        this.publicMethodsOnly = true;
+        Assert.notEmpty(annotationParsers, "At least one CacheAnnotationParser needs to be specified");
+        this.annotationParsers = annotationParsers;
+    }
+
+
+    @Override
+    protected Collection<CacheOperation> findCacheOperations(Class<?> targetClass, Method method) {
+        return determineCacheOperations(parser -> parser.parseCacheAnnotations(targetClass, method));
+    }
+
+    @Override
+    protected Collection<CacheOperation> findCacheOperations(final Class<?> clazz) {
+        return determineCacheOperations(parser -> parser.parseCacheAnnotations(clazz));
+    }
+
+//	@Override
+//	protected Collection<CacheOperation> findCacheOperations(final Method method) {
+//		return determineCacheOperations(parser -> parser.parseCacheAnnotations(method));
+//	}
+
+    /**
+     * Determine the cache operation(s) for the given {@link CacheOperationProvider}.
+     * <p>This implementation delegates to configured
+     * {@link CacheAnnotationParser}s for parsing known annotations into
+     * Spring's metadata attribute class.
+     * <p>Can be overridden to support custom annotations that carry
+     * caching metadata.
+     *
+     * @param provider the cache operation provider to use
+     * @return the configured caching operations, or {@code null} if none found
+     */
+    protected Collection<CacheOperation> determineCacheOperations(CacheOperationProvider provider) {
+        Collection<CacheOperation> ops = null;
+        for (FixUseSupperClassCacheAnnotationParser annotationParser : this.annotationParsers) {
+            Collection<CacheOperation> annOps = provider.getCacheOperations(annotationParser);
+            if (annOps != null) {
+                if (ops == null) {
+                    ops = new ArrayList<>();
+                }
+                ops.addAll(annOps);
+            }
+        }
+        return ops;
+    }
+
+    /**
+     * By default, only public methods can be made cacheable.
+     */
+    @Override
+    protected boolean allowPublicMethodsOnly() {
+        return this.publicMethodsOnly;
+    }
+
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof FixUseSupperClassCacheOperationSource)) {
+            return false;
+        }
+        FixUseSupperClassCacheOperationSource otherCos = (FixUseSupperClassCacheOperationSource) other;
+        return (this.annotationParsers.equals(otherCos.annotationParsers) &&
+                this.publicMethodsOnly == otherCos.publicMethodsOnly);
+    }
+
+    @Override
+    public int hashCode() {
+        return this.annotationParsers.hashCode();
+    }
+
+    public void setPublicMethodsOnly(boolean publicMethodsOnly) {
+        this.publicMethodsOnly = publicMethodsOnly;
+    }
+
+    /**
+     * Callback interface providing {@link CacheOperation} instance(s) based on
+     * a given {@link CacheAnnotationParser}.
+     */
+    protected interface CacheOperationProvider {
+
+        /**
+         * Return the {@link CacheOperation} instance(s) provided by the specified parser.
+         *
+         * @param parser the parser to use
+         * @return the cache operations, or {@code null} if none found
+         */
+        Collection<CacheOperation> getCacheOperations(FixUseSupperClassCacheAnnotationParser parser);
+    }
+
+}

+ 198 - 0
hsweb-concurrent/hsweb-concurrent-cache/src/main/java/org/hswebframework/web/cache/spring/fix/FixUseSupperClassFallbackCacheOperationSource.java

@@ -0,0 +1,198 @@
+/*
+ * Copyright 2002-2017 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.hswebframework.web.cache.spring.fix;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.cache.interceptor.CacheOperation;
+import org.springframework.cache.interceptor.CacheOperationSource;
+import org.springframework.core.BridgeMethodResolver;
+import org.springframework.core.MethodClassKey;
+import org.springframework.util.ClassUtils;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Abstract implementation of {@link CacheOperation} that caches attributes
+ * for methods and implements a fallback policy: 1. specific target method;
+ * 2. target class; 3. declaring method; 4. declaring class/interface.
+ * <p>
+ * <p>Defaults to using the target class's caching attribute if none is
+ * associated with the target method. Any caching attribute associated with
+ * the target method completely overrides a class caching attribute.
+ * If none found on the target class, the interface that the invoked method
+ * has been called through (in case of a JDK proxy) will be checked.
+ * <p>
+ * <p>This implementation caches attributes by method after they are first
+ * used. If it is ever desirable to allow dynamic changing of cacheable
+ * attributes (which is very unlikely), caching could be made configurable.
+ *
+ * @author Costin Leau
+ * @author Juergen Hoeller
+ * @author zhouhao
+ * @see org.springframework.cache.interceptor.AbstractFallbackCacheOperationSource
+ * @since 3.1
+ */
+public abstract class FixUseSupperClassFallbackCacheOperationSource implements CacheOperationSource {
+
+    /**
+     * Canonical value held in cache to indicate no caching attribute was
+     * found for this method and we don't need to look again.
+     */
+    private final static Collection<CacheOperation> NULL_CACHING_ATTRIBUTE = Collections.emptyList();
+
+
+    /**
+     * Logger available to subclasses.
+     * <p>As this base class is not marked Serializable, the logger will be recreated
+     * after serialization - provided that the concrete subclass is Serializable.
+     */
+    protected final Log logger = LogFactory.getLog(getClass());
+
+    /**
+     * Cache of CacheOperations, keyed by method on a specific target class.
+     * <p>As this base class is not marked Serializable, the cache will be recreated
+     * after serialization - provided that the concrete subclass is Serializable.
+     */
+    private final Map<Object, Collection<CacheOperation>> attributeCache =
+            new ConcurrentHashMap<Object, Collection<CacheOperation>>(1024);
+
+
+    /**
+     * Determine the caching attribute for this method invocation.
+     * <p>Defaults to the class's caching attribute if no method attribute is found.
+     *
+     * @param method      the method for the current invocation (never {@code null})
+     * @param targetClass the target class for this invocation (may be {@code null})
+     * @return {@link CacheOperation} for this method, or {@code null} if the method
+     * is not cacheable
+     */
+    @Override
+    public Collection<CacheOperation> getCacheOperations(Method method, Class<?> targetClass) {
+        if (method.getDeclaringClass() == Object.class) {
+            return null;
+        }
+
+        Object cacheKey = getCacheKey(method, targetClass);
+        Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);
+
+        if (cached != null) {
+            return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
+        } else {
+            Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
+            if (cacheOps != null) {
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheOps);
+                }
+                this.attributeCache.put(cacheKey, cacheOps);
+            } else {
+                this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
+            }
+            return cacheOps;
+        }
+    }
+
+    /**
+     * Determine a cache key for the given method and target class.
+     * <p>Must not produce same key for overloaded methods.
+     * Must produce same key for different instances of the same method.
+     *
+     * @param method      the method (never {@code null})
+     * @param targetClass the target class (may be {@code null})
+     * @return the cache key (never {@code null})
+     */
+    protected Object getCacheKey(Method method, Class<?> targetClass) {
+        return new MethodClassKey(method, targetClass);
+    }
+
+    private Collection<CacheOperation> computeCacheOperations(Method method, Class<?> targetClass) {
+        // Don't allow no-public methods as required.
+        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
+            return null;
+        }
+
+        // The method may be on an interface, but we need attributes from the target class.
+        // If the target class is null, the method will be unchanged.
+        Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
+        // If we are dealing with method with generic parameters, find the original method.
+        specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
+
+
+        // First try is the method in the target class.
+        // 解决@CacheConfig不能继承的问题
+        Collection<CacheOperation> opDef = findCacheOperations(targetClass, specificMethod);
+        if (opDef != null) {
+            return opDef;
+        }
+
+        // Second try is the caching operation on the target class.
+        opDef = findCacheOperations(specificMethod.getDeclaringClass());
+        if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
+            return opDef;
+        }
+
+        if (specificMethod != method) {
+            // Fallback is to look at the original method.
+            opDef = findCacheOperations(targetClass, method);
+            if (opDef != null) {
+                return opDef;
+            }
+            // Last fallback is the class of the original method.
+            opDef = findCacheOperations(method.getDeclaringClass());
+            if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
+                return opDef;
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Subclasses need to implement this to return the caching attribute
+     * for the given method, if any.
+     *
+     * @param method the method to retrieve the attribute for
+     * @return all caching attribute associated with this method
+     * (or {@code null} if none)
+     */
+    protected abstract Collection<CacheOperation> findCacheOperations(Class<?> targetClass, Method method);
+
+    /**
+     * Subclasses need to implement this to return the caching attribute
+     * for the given class, if any.
+     *
+     * @param clazz the class to retrieve the attribute for
+     * @return all caching attribute associated with this class
+     * (or {@code null} if none)
+     */
+    protected abstract Collection<CacheOperation> findCacheOperations(Class<?> clazz);
+
+    /**
+     * Should only public methods be allowed to have caching semantics?
+     * <p>The default implementation returns {@code false}.
+     */
+    protected boolean allowPublicMethodsOnly() {
+        return false;
+    }
+
+}