26. SpringBoot 初识缓存及 SimpleCacheConfiguration源码解析
阅读原文时间:2024年07月01日阅读:1

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 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 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);  
    }  
}  

}

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器