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