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}