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.support; 018 019import java.util.Collection; 020import java.util.Collections; 021import java.util.LinkedHashSet; 022import java.util.Set; 023import java.util.concurrent.ConcurrentHashMap; 024import java.util.concurrent.ConcurrentMap; 025 026import org.springframework.beans.factory.InitializingBean; 027import org.springframework.cache.Cache; 028import org.springframework.cache.CacheManager; 029import org.springframework.lang.Nullable; 030 031/** 032 * Abstract base class implementing the common {@link CacheManager} methods. 033 * Useful for 'static' environments where the backing caches do not change. 034 * 035 * @author Costin Leau 036 * @author Juergen Hoeller 037 * @author Stephane Nicoll 038 * @since 3.1 039 */ 040public abstract class AbstractCacheManager implements CacheManager, InitializingBean { 041 042 private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16); 043 044 private volatile Set<String> cacheNames = Collections.emptySet(); 045 046 047 // Early cache initialization on startup 048 049 @Override 050 public void afterPropertiesSet() { 051 initializeCaches(); 052 } 053 054 /** 055 * Initialize the static configuration of caches. 056 * <p>Triggered on startup through {@link #afterPropertiesSet()}; 057 * can also be called to re-initialize at runtime. 058 * @since 4.2.2 059 * @see #loadCaches() 060 */ 061 public void initializeCaches() { 062 Collection<? extends Cache> caches = loadCaches(); 063 064 synchronized (this.cacheMap) { 065 this.cacheNames = Collections.emptySet(); 066 this.cacheMap.clear(); 067 Set<String> cacheNames = new LinkedHashSet<>(caches.size()); 068 for (Cache cache : caches) { 069 String name = cache.getName(); 070 this.cacheMap.put(name, decorateCache(cache)); 071 cacheNames.add(name); 072 } 073 this.cacheNames = Collections.unmodifiableSet(cacheNames); 074 } 075 } 076 077 /** 078 * Load the initial caches for this cache manager. 079 * <p>Called by {@link #afterPropertiesSet()} on startup. 080 * The returned collection may be empty but must not be {@code null}. 081 */ 082 protected abstract Collection<? extends Cache> loadCaches(); 083 084 085 // Lazy cache initialization on access 086 087 @Override 088 @Nullable 089 public Cache getCache(String name) { 090 // Quick check for existing cache... 091 Cache cache = this.cacheMap.get(name); 092 if (cache != null) { 093 return cache; 094 } 095 096 // The provider may support on-demand cache creation... 097 Cache missingCache = getMissingCache(name); 098 if (missingCache != null) { 099 // Fully synchronize now for missing cache registration 100 synchronized (this.cacheMap) { 101 cache = this.cacheMap.get(name); 102 if (cache == null) { 103 cache = decorateCache(missingCache); 104 this.cacheMap.put(name, cache); 105 updateCacheNames(name); 106 } 107 } 108 } 109 return cache; 110 } 111 112 @Override 113 public Collection<String> getCacheNames() { 114 return this.cacheNames; 115 } 116 117 118 // Common cache initialization delegates for subclasses 119 120 /** 121 * Check for a registered cache of the given name. 122 * In contrast to {@link #getCache(String)}, this method does not trigger 123 * the lazy creation of missing caches via {@link #getMissingCache(String)}. 124 * @param name the cache identifier (must not be {@code null}) 125 * @return the associated Cache instance, or {@code null} if none found 126 * @since 4.1 127 * @see #getCache(String) 128 * @see #getMissingCache(String) 129 */ 130 @Nullable 131 protected final Cache lookupCache(String name) { 132 return this.cacheMap.get(name); 133 } 134 135 /** 136 * Dynamically register an additional Cache with this manager. 137 * @param cache the Cache to register 138 * @deprecated as of Spring 4.3, in favor of {@link #getMissingCache(String)} 139 */ 140 @Deprecated 141 protected final void addCache(Cache cache) { 142 String name = cache.getName(); 143 synchronized (this.cacheMap) { 144 if (this.cacheMap.put(name, decorateCache(cache)) == null) { 145 updateCacheNames(name); 146 } 147 } 148 } 149 150 /** 151 * Update the exposed {@link #cacheNames} set with the given name. 152 * <p>This will always be called within a full {@link #cacheMap} lock 153 * and effectively behaves like a {@code CopyOnWriteArraySet} with 154 * preserved order but exposed as an unmodifiable reference. 155 * @param name the name of the cache to be added 156 */ 157 private void updateCacheNames(String name) { 158 Set<String> cacheNames = new LinkedHashSet<>(this.cacheNames.size() + 1); 159 cacheNames.addAll(this.cacheNames); 160 cacheNames.add(name); 161 this.cacheNames = Collections.unmodifiableSet(cacheNames); 162 } 163 164 165 // Overridable template methods for cache initialization 166 167 /** 168 * Decorate the given Cache object if necessary. 169 * @param cache the Cache object to be added to this CacheManager 170 * @return the decorated Cache object to be used instead, 171 * or simply the passed-in Cache object by default 172 */ 173 protected Cache decorateCache(Cache cache) { 174 return cache; 175 } 176 177 /** 178 * Return a missing cache with the specified {@code name}, or {@code null} if 179 * such a cache does not exist or could not be created on demand. 180 * <p>Caches may be lazily created at runtime if the native provider supports it. 181 * If a lookup by name does not yield any result, an {@code AbstractCacheManager} 182 * subclass gets a chance to register such a cache at runtime. The returned cache 183 * will be automatically added to this cache manager. 184 * @param name the name of the cache to retrieve 185 * @return the missing cache, or {@code null} if no such cache exists or could be 186 * created on demand 187 * @since 4.1 188 * @see #getCache(String) 189 */ 190 @Nullable 191 protected Cache getMissingCache(String name) { 192 return null; 193 } 194 195}