001/*
002 * Copyright 2002-2019 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.annotation;
018
019import java.io.Serializable;
020import java.lang.reflect.Method;
021import java.util.ArrayList;
022import java.util.Arrays;
023import java.util.Collection;
024import java.util.Collections;
025import java.util.LinkedHashSet;
026import java.util.Set;
027
028import org.springframework.cache.interceptor.AbstractFallbackCacheOperationSource;
029import org.springframework.cache.interceptor.CacheOperation;
030import org.springframework.lang.Nullable;
031import org.springframework.util.Assert;
032
033/**
034 * Implementation of the {@link org.springframework.cache.interceptor.CacheOperationSource
035 * CacheOperationSource} interface for working with caching metadata in annotation format.
036 *
037 * <p>This class reads Spring's {@link Cacheable}, {@link CachePut} and {@link CacheEvict}
038 * annotations and exposes corresponding caching operation definition to Spring's cache
039 * infrastructure. This class may also serve as base class for a custom
040 * {@code CacheOperationSource}.
041 *
042 * @author Costin Leau
043 * @author Juergen Hoeller
044 * @author Stephane Nicoll
045 * @since 3.1
046 */
047@SuppressWarnings("serial")
048public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperationSource implements Serializable {
049
050        private final boolean publicMethodsOnly;
051
052        private final Set<CacheAnnotationParser> annotationParsers;
053
054
055        /**
056         * Create a default AnnotationCacheOperationSource, supporting public methods
057         * that carry the {@code Cacheable} and {@code CacheEvict} annotations.
058         */
059        public AnnotationCacheOperationSource() {
060                this(true);
061        }
062
063        /**
064         * Create a default {@code AnnotationCacheOperationSource}, supporting public methods
065         * that carry the {@code Cacheable} and {@code CacheEvict} annotations.
066         * @param publicMethodsOnly whether to support only annotated public methods
067         * typically for use with proxy-based AOP), or protected/private methods as well
068         * (typically used with AspectJ class weaving)
069         */
070        public AnnotationCacheOperationSource(boolean publicMethodsOnly) {
071                this.publicMethodsOnly = publicMethodsOnly;
072                this.annotationParsers = Collections.singleton(new SpringCacheAnnotationParser());
073        }
074
075        /**
076         * Create a custom AnnotationCacheOperationSource.
077         * @param annotationParser the CacheAnnotationParser to use
078         */
079        public AnnotationCacheOperationSource(CacheAnnotationParser annotationParser) {
080                this.publicMethodsOnly = true;
081                Assert.notNull(annotationParser, "CacheAnnotationParser must not be null");
082                this.annotationParsers = Collections.singleton(annotationParser);
083        }
084
085        /**
086         * Create a custom AnnotationCacheOperationSource.
087         * @param annotationParsers the CacheAnnotationParser to use
088         */
089        public AnnotationCacheOperationSource(CacheAnnotationParser... annotationParsers) {
090                this.publicMethodsOnly = true;
091                Assert.notEmpty(annotationParsers, "At least one CacheAnnotationParser needs to be specified");
092                this.annotationParsers = new LinkedHashSet<>(Arrays.asList(annotationParsers));
093        }
094
095        /**
096         * Create a custom AnnotationCacheOperationSource.
097         * @param annotationParsers the CacheAnnotationParser to use
098         */
099        public AnnotationCacheOperationSource(Set<CacheAnnotationParser> annotationParsers) {
100                this.publicMethodsOnly = true;
101                Assert.notEmpty(annotationParsers, "At least one CacheAnnotationParser needs to be specified");
102                this.annotationParsers = annotationParsers;
103        }
104
105
106        @Override
107        public boolean isCandidateClass(Class<?> targetClass) {
108                for (CacheAnnotationParser parser : this.annotationParsers) {
109                        if (parser.isCandidateClass(targetClass)) {
110                                return true;
111                        }
112                }
113                return false;
114        }
115
116        @Override
117        @Nullable
118        protected Collection<CacheOperation> findCacheOperations(Class<?> clazz) {
119                return determineCacheOperations(parser -> parser.parseCacheAnnotations(clazz));
120        }
121
122        @Override
123        @Nullable
124        protected Collection<CacheOperation> findCacheOperations(Method method) {
125                return determineCacheOperations(parser -> parser.parseCacheAnnotations(method));
126        }
127
128        /**
129         * Determine the cache operation(s) for the given {@link CacheOperationProvider}.
130         * <p>This implementation delegates to configured
131         * {@link CacheAnnotationParser CacheAnnotationParsers}
132         * for parsing known annotations into Spring's metadata attribute class.
133         * <p>Can be overridden to support custom annotations that carry caching metadata.
134         * @param provider the cache operation provider to use
135         * @return the configured caching operations, or {@code null} if none found
136         */
137        @Nullable
138        protected Collection<CacheOperation> determineCacheOperations(CacheOperationProvider provider) {
139                Collection<CacheOperation> ops = null;
140                for (CacheAnnotationParser parser : this.annotationParsers) {
141                        Collection<CacheOperation> annOps = provider.getCacheOperations(parser);
142                        if (annOps != null) {
143                                if (ops == null) {
144                                        ops = annOps;
145                                }
146                                else {
147                                        Collection<CacheOperation> combined = new ArrayList<>(ops.size() + annOps.size());
148                                        combined.addAll(ops);
149                                        combined.addAll(annOps);
150                                        ops = combined;
151                                }
152                        }
153                }
154                return ops;
155        }
156
157        /**
158         * By default, only public methods can be made cacheable.
159         */
160        @Override
161        protected boolean allowPublicMethodsOnly() {
162                return this.publicMethodsOnly;
163        }
164
165
166        @Override
167        public boolean equals(@Nullable Object other) {
168                if (this == other) {
169                        return true;
170                }
171                if (!(other instanceof AnnotationCacheOperationSource)) {
172                        return false;
173                }
174                AnnotationCacheOperationSource otherCos = (AnnotationCacheOperationSource) other;
175                return (this.annotationParsers.equals(otherCos.annotationParsers) &&
176                                this.publicMethodsOnly == otherCos.publicMethodsOnly);
177        }
178
179        @Override
180        public int hashCode() {
181                return this.annotationParsers.hashCode();
182        }
183
184
185        /**
186         * Callback interface providing {@link CacheOperation} instance(s) based on
187         * a given {@link CacheAnnotationParser}.
188         */
189        @FunctionalInterface
190        protected interface CacheOperationProvider {
191
192                /**
193                 * Return the {@link CacheOperation} instance(s) provided by the specified parser.
194                 * @param parser the parser to use
195                 * @return the cache operations, or {@code null} if none found
196                 */
197                @Nullable
198                Collection<CacheOperation> getCacheOperations(CacheAnnotationParser parser);
199        }
200
201}