1.引入一下starter:
web、cache、Mybatis、MySQL
@MapperScan("com.everjiankang.cache.dao") @SpringBootApplication @EnableCaching //啟用緩存 public class Springboot01CacheApplication { public static void main(String[] args) { SpringApplication.run(Springboot01CacheApplication.class, args); } }
@Service public class UserServiceImpl implements UserService { @Autowired UserMapper userMapper; /** * 將方法的運行結果進行緩存;以后再要相同的數據,直接從緩存中獲取,不用調用方法; * CacheManager管理多個Cache組件的,對緩存的真正CRUD操作在Cache組件中,每一個緩存組件有自己唯一一個名字; * * * 原理: * 1、自動配置類;CacheAutoConfiguration * 2、緩存的配置類 * org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration * org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration * org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration * org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration * org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration * org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration * org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration * org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration * org.springframework.boot.autoconfigure.cache.GuavaCacheConfiguration * org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration【默認】 * org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration * 3、哪個配置類默認生效:SimpleCacheConfiguration; * * 4、給容器中注冊了一個CacheManager:ConcurrentMapCacheManager * 5、可以獲取和創建ConcurrentMapCache類型的緩存組件;他的作用將數據保存在ConcurrentMap中; * * 運行流程: * @Cacheable: * 1、方法運行之前,先去查詢Cache(緩存組件),按照cacheNames指定的名字獲取; * (CacheManager先獲取相應的緩存),第一次獲取緩存如果沒有Cache組件會自動創建。 * 2、去Cache中查找緩存的內容,使用一個key,默認就是方法的參數; * key是按照某種策略生成的;默認是使用keyGenerator生成的,默認使用SimpleKeyGenerator生成key; * SimpleKeyGenerator生成key的默認策略; * 如果沒有參數;key=new SimpleKey(); * 如果有一個參數:key=參數的值 * 如果有多個參數:key=new SimpleKey(params); * 3、沒有查到緩存就調用目標方法; * 4、將目標方法返回的結果,放進緩存中 * * @Cacheable標注的方法執行之前先來檢查緩存中有沒有這個數據,默認按照參數的值作為key去查詢緩存, * 如果沒有就運行方法並將結果放入緩存;以后再來調用就可以直接使用緩存中的數據; * * 核心: * 1)、使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】組件 * 2)、key使用keyGenerator生成的,默認是SimpleKeyGenerator * * * 幾個屬性: * cacheNames/value:指定緩存組件的名字;將方法的返回結果放在哪個緩存中,是數組的方式,可以指定多個緩存; * * key:緩存數據使用的key;可以用它來指定。默認是使用方法參數的值 1-方法的返回值 * 編寫SpEL; #i d;參數id的值 #a0 #p0 #root.args[0] * getEmp[2] * * keyGenerator:key的生成器;可以自己指定key的生成器的組件id * key/keyGenerator:二選一使用; * * * cacheManager:指定緩存管理器;或者cacheResolver指定獲取解析器 * * condition:指定符合條件的情況下才緩存; * ,condition = "#id>0" * condition = "#a0>1":第一個參數的值》1的時候才進行緩存 * * unless:否定緩存;當unless指定的條件為true,方法的返回值就不會被緩存;可以獲取到結果進行判斷 * unless = "#result == null" * unless = "#a0==2":如果第一個參數的值是2,結果不緩存; * sync:是否使用異步模式 * @param id * @return * */ @Cacheable(cacheNames = {"user"}) @Override public User selectByPrimaryKey(Integer id) { return userMapper.selectByPrimaryKey(id); } }
package org.springframework.boot.autoconfigure.cache; @Configuration @ConditionalOnClass({CacheManager.class}) @ConditionalOnBean({CacheAspectSupport.class}) @ConditionalOnMissingBean( value = {CacheManager.class}, name = {"cacheResolver"} ) @EnableConfigurationProperties({CacheProperties.class}) @AutoConfigureAfter({CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class, HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class}) @Import({CacheAutoConfiguration.CacheConfigurationImportSelector.class}) public class CacheAutoConfiguration { public CacheAutoConfiguration() { } @Bean @ConditionalOnMissingBean public CacheManagerCustomizers cacheManagerCustomizers(ObjectProvider<CacheManagerCustomizer<?>> customizers) { return new CacheManagerCustomizers((List)customizers.orderedStream().collect(Collectors.toList())); } @Bean public CacheAutoConfiguration.CacheManagerValidator cacheAutoConfigurationValidator(CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) { return new CacheAutoConfiguration.CacheManagerValidator(cacheProperties, cacheManager); } static class CacheConfigurationImportSelector implements ImportSelector { CacheConfigurationImportSelector() { }
/**在Spring Boot項目啟動的過程中,導入緩存的配置組件*/ public String[] selectImports(AnnotationMetadata importingClassMetadata) { CacheType[] types = CacheType.values(); String[] imports = new String[types.length]; for(int i = 0; i < types.length; ++i) { imports[i] = CacheConfigurations.getConfigurationClass(types[i]); } /** imports的值,緩存配置類
org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration
org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration
org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
*/
return imports; } } static class CacheManagerValidator implements InitializingBean { private final CacheProperties cacheProperties; private final ObjectProvider<CacheManager> cacheManager; CacheManagerValidator(CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) { this.cacheProperties = cacheProperties; this.cacheManager = cacheManager; } public void afterPropertiesSet() { Assert.notNull(this.cacheManager.getIfAvailable(), () -> { return "No cache manager could be auto-configured, check your configuration (caching type is '" + this.cacheProperties.getType() + "')"; }); } } @Configuration @ConditionalOnClass({LocalContainerEntityManagerFactoryBean.class}) @ConditionalOnBean({AbstractEntityManagerFactoryBean.class}) protected static class CacheManagerJpaDependencyConfiguration extends EntityManagerFactoryDependsOnPostProcessor { public CacheManagerJpaDependencyConfiguration() { super(new String[]{"cacheManager"}); } } }
在配置文件中加入
debug=true
然后啟動項目,控制台關於緩存的啟動log如下:
SimpleCacheConfiguration matched: - Cache org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration automatic cache type (CacheCondition) - @ConditionalOnMissingBean (types: org.springframework.cache.CacheManager; SearchStrategy: all) did not find any beans (OnBeanCondition) CaffeineCacheConfiguration: Did not match: - @ConditionalOnClass did not find required class 'com.github.benmanes.caffeine.cache.Caffeine' (OnClassCondition) CouchbaseCacheConfiguration: Did not match: - @ConditionalOnClass did not find required classes 'com.couchbase.client.java.Bucket', 'com.couchbase.client.spring.cache.CouchbaseCacheManager' (OnClassCondition) EhCacheCacheConfiguration: Did not match: - @ConditionalOnClass did not find required class 'net.sf.ehcache.Cache' (OnClassCondition) GenericCacheConfiguration: Did not match: - @ConditionalOnBean (types: org.springframework.cache.Cache; SearchStrategy: all) did not find any beans of type org.springframework.cache.Cache (OnBeanCondition) Matched: - Cache org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration automatic cache type (CacheCondition) HazelcastCacheConfiguration: Did not match: - @ConditionalOnClass did not find required classes 'com.hazelcast.core.HazelcastInstance', 'com.hazelcast.spring.cache.HazelcastCacheManager' (OnClassCondition) InfinispanCacheConfiguration: Did not match: - @ConditionalOnClass did not find required class 'org.infinispan.spring.provider.SpringEmbeddedCacheManager' (OnClassCondition) JCacheCacheConfiguration: Did not match: - @ConditionalOnClass did not find required class 'javax.cache.Caching' (OnClassCondition) NoOpCacheConfiguration: Did not match: - @ConditionalOnMissingBean (types: org.springframework.cache.CacheManager; SearchStrategy: all)
found beans of type 'org.springframework.cache.CacheManager' cacheManager (OnBeanCondition) Matched: - Cache org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration automatic cache type (CacheCondition) RedisCacheConfiguration: Did not match: - @ConditionalOnClass did not find required class 'org.springframework.data.redis.connection.RedisConnectionFactory' (OnClassCondition)
@Configuration @ConditionalOnMissingBean({CacheManager.class}) @Conditional({CacheCondition.class}) class SimpleCacheConfiguration { private final CacheProperties cacheProperties; private final CacheManagerCustomizers customizerInvoker; SimpleCacheConfiguration(CacheProperties cacheProperties, CacheManagerCustomizers customizerInvoker) { this.cacheProperties = cacheProperties; this.customizerInvoker = customizerInvoker; } @Bean public ConcurrentMapCacheManager cacheManager() { ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager(); List<String> cacheNames = this.cacheProperties.getCacheNames(); if (!cacheNames.isEmpty()) { cacheManager.setCacheNames(cacheNames); } return (ConcurrentMapCacheManager)this.customizerInvoker.customize(cacheManager); } }
package org.springframework.cache; import java.util.Collection; import org.springframework.lang.Nullable; public interface CacheManager { @Nullable Cache getCache(String var1); Collection<String> getCacheNames(); }
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.springframework.cache.concurrent;
public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderAware { private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap(16); private boolean dynamic = true; private boolean allowNullValues = true; private boolean storeByValue = false; @Nullable private SerializationDelegate serialization; public ConcurrentMapCacheManager() { } public ConcurrentMapCacheManager(String... cacheNames) { this.setCacheNames(Arrays.asList(cacheNames)); } public void setCacheNames(@Nullable Collection<String> cacheNames) { if (cacheNames != null) { Iterator var2 = cacheNames.iterator(); while(var2.hasNext()) { String name = (String)var2.next(); this.cacheMap.put(name, this.createConcurrentMapCache(name)); } this.dynamic = false; } else { this.dynamic = true; } } public void setAllowNullValues(boolean allowNullValues) { if (allowNullValues != this.allowNullValues) { this.allowNullValues = allowNullValues; this.recreateCaches(); } } public boolean isAllowNullValues() { return this.allowNullValues; } public void setStoreByValue(boolean storeByValue) { if (storeByValue != this.storeByValue) { this.storeByValue = storeByValue; this.recreateCaches(); } } public boolean isStoreByValue() { return this.storeByValue; } public void setBeanClassLoader(ClassLoader classLoader) { this.serialization = new SerializationDelegate(classLoader); if (this.isStoreByValue()) { this.recreateCaches(); } } public Collection<String> getCacheNames() { return Collections.unmodifiableSet(this.cacheMap.keySet()); } @Nullable public Cache getCache(String name) { Cache cache = (Cache)this.cacheMap.get(name); if (cache == null && this.dynamic) { ConcurrentMap var3 = this.cacheMap; synchronized(this.cacheMap) { cache = (Cache)this.cacheMap.get(name); if (cache == null) { cache = this.createConcurrentMapCache(name); this.cacheMap.put(name, cache); } } } return cache; } private void recreateCaches() { Iterator var1 = this.cacheMap.entrySet().iterator(); while(var1.hasNext()) { Entry<String, Cache> entry = (Entry)var1.next(); entry.setValue(this.createConcurrentMapCache((String)entry.getKey())); } } protected Cache createConcurrentMapCache(String name) { SerializationDelegate actualSerialization = this.isStoreByValue() ? this.serialization : null; return new ConcurrentMapCache(name, new ConcurrentHashMap(256), this.isAllowNullValues(), actualSerialization); } }
package org.springframework.cache.concurrent; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Function; import org.springframework.cache.Cache.ValueRetrievalException; import org.springframework.cache.Cache.ValueWrapper; import org.springframework.cache.support.AbstractValueAdaptingCache; import org.springframework.core.serializer.support.SerializationDelegate; import org.springframework.lang.Nullable; import org.springframework.util.Assert; public class ConcurrentMapCache extends AbstractValueAdaptingCache { private final String name; private final ConcurrentMap<Object, Object> store; //真正存數據的地方 @Nullable private final SerializationDelegate serialization; public ConcurrentMapCache(String name) { this(name, new ConcurrentHashMap(256), true); } public ConcurrentMapCache(String name, boolean allowNullValues) { this(name, new ConcurrentHashMap(256), allowNullValues); } public ConcurrentMapCache(String name, ConcurrentMap<Object, Object> store, boolean allowNullValues) { this(name, store, allowNullValues, (SerializationDelegate)null); } protected ConcurrentMapCache(String name, ConcurrentMap<Object, Object> store, boolean allowNullValues, @Nullable SerializationDelegate serialization) { super(allowNullValues); Assert.notNull(name, "Name must not be null"); Assert.notNull(store, "Store must not be null"); this.name = name; this.store = store; this.serialization = serialization; } public final boolean isStoreByValue() { return this.serialization != null; } public final String getName() { return this.name; } public final ConcurrentMap<Object, Object> getNativeCache() { return this.store; } @Nullable protected Object lookup(Object key) { return this.store.get(key); } @Nullable public <T> T get(Object key, Callable<T> valueLoader) { return this.fromStoreValue(this.store.computeIfAbsent(key, (r) -> { try { return this.toStoreValue(valueLoader.call()); } catch (Throwable var5) { throw new ValueRetrievalException(key, valueLoader, var5); } })); } public void put(Object key, @Nullable Object value) { this.store.put(key, this.toStoreValue(value)); } @Nullable public ValueWrapper putIfAbsent(Object key, @Nullable Object value) { Object existing = this.store.putIfAbsent(key, this.toStoreValue(value)); return this.toValueWrapper(existing); } public void evict(Object key) { this.store.remove(key); } public void clear() { this.store.clear(); } protected Object toStoreValue(@Nullable Object userValue) { Object storeValue = super.toStoreValue(userValue); if (this.serialization != null) { try { return this.serializeValue(this.serialization, storeValue); } catch (Throwable var4) { throw new IllegalArgumentException("Failed to serialize cache value '" + userValue + "'. Does it implement Serializable?", var4); } } else { return storeValue; } } private Object serializeValue(SerializationDelegate serialization, Object storeValue) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] var4; try { serialization.serialize(storeValue, out); var4 = out.toByteArray(); } finally { out.close(); } return var4; } protected Object fromStoreValue(@Nullable Object storeValue) { if (storeValue != null && this.serialization != null) { try { return super.fromStoreValue(this.deserializeValue(this.serialization, storeValue)); } catch (Throwable var3) { throw new IllegalArgumentException("Failed to deserialize cache value '" + storeValue + "'", var3); } } else { return super.fromStoreValue(storeValue); } } private Object deserializeValue(SerializationDelegate serialization, Object storeValue) throws IOException { ByteArrayInputStream in = new ByteArrayInputStream((byte[])((byte[])storeValue)); Object var4; try { var4 = serialization.deserialize(in); } finally { in.close(); } return var4; } }
package org.springframework.cache.interceptor; public abstract class CacheAspectSupport extends AbstractCacheInvoker implements BeanFactoryAware, InitializingBean, SmartInitializingSingleton { @Nullable private Object execute(CacheOperationInvoker invoker, Method method, CacheAspectSupport.CacheOperationContexts contexts) { if (contexts.isSynchronized()) { CacheAspectSupport.CacheOperationContext context = (CacheAspectSupport.CacheOperationContext)contexts.get(CacheableOperation.class).iterator().next(); if (this.isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) { Object key = this.generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT); Cache cache = (Cache)context.getCaches().iterator().next(); try { return this.wrapCacheValue(method, cache.get(key, () -> { return this.unwrapReturnValue(this.invokeOperation(invoker)); })); } catch (ValueRetrievalException var10) { throw (ThrowableWrapper)var10.getCause(); } } else { return this.invokeOperation(invoker); } } else { this.processCacheEvicts(contexts.get(CacheEvictOperation.class), true, CacheOperationExpressionEvaluator.NO_RESULT); ValueWrapper cacheHit = this.findCachedItem(contexts.get(CacheableOperation.class)); List<CacheAspectSupport.CachePutRequest> cachePutRequests = new LinkedList(); if (cacheHit == null) { this.collectPutRequests(contexts.get(CacheableOperation.class), CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests); } Object cacheValue; Object returnValue; if (cacheHit != null && !this.hasCachePut(contexts)) { cacheValue = cacheHit.get(); returnValue = this.wrapCacheValue(method, cacheValue); } else { returnValue = this.invokeOperation(invoker); cacheValue = this.unwrapReturnValue(returnValue); } this.collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests); Iterator var8 = cachePutRequests.iterator(); while(var8.hasNext()) { CacheAspectSupport.CachePutRequest cachePutRequest = (CacheAspectSupport.CachePutRequest)var8.next(); cachePutRequest.apply(cacheValue); } this.processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue); return returnValue; } } private void performCacheEvict(CacheAspectSupport.CacheOperationContext context, CacheEvictOperation operation, @Nullable Object result) { Object key = null; Iterator var5 = context.getCaches().iterator(); while(var5.hasNext()) { Cache cache = (Cache)var5.next(); if (operation.isCacheWide()) { this.logInvalidating(context, operation, (Object)null); this.doClear(cache); } else { if (key == null) { key = this.generateKey(context, result); } this.logInvalidating(context, operation, key); this.doEvict(cache, key); } } } }
Key生成策略
package org.springframework.cache.interceptor; import java.lang.reflect.Method; public class SimpleKeyGenerator implements KeyGenerator { public SimpleKeyGenerator() { } public Object generate(Object target, Method method, Object... params) { return generateKey(params); } public static Object generateKey(Object... params) { if (params.length == 0) { return SimpleKey.EMPTY; } else { if (params.length == 1) { Object param = params[0]; if (param != null && !param.getClass().isArray()) { return param; } } return new SimpleKey(params); } } }