点击上方 IT牧场 ,选择 置顶或者星标
技术干货每日送达
缓存作为日常开发中必备的环节,主流的缓存中间件Redis、Guava、Mongo等等可以很好的缓解服务器压力,提高系统响应。
为什么要引入SpringCache管理缓存
现在技术栈以Spring为核心, SpringCache的作为缓存的治理,可以很好的引入到项目当中,不会对现有的架构体系造成冲突,方便维护和管理。
业务逻辑:Spring-Cache的引入,将缓存逻辑和策略 和业务代码进行解藕,做到分而治之的效果。
缓存管理:Spring-Cache 内库中包含操作Redis、缓存管理器、承接Spring上下文、动态代理等模块
缓存策略:在缓存策略当中,不仅包含cache-aside、cache-read/write-though 等等策略模型,在Spring-cache中同样包含
操作便捷:通过配置redisteplate、rediscachemanger结合注解的形式,去除多余的模版/工具操作
自定义配置
application.yml
spring:
redis:
host: 127.0.0.1
port: 6379
database: 0
pool:
max-active: 20
max-wait: 5000
max-idle: 10
min-idle: 2
timeout: 6000
expires:
cache: 10000
Bean 配置:
@Configuration
@EnableCaching@ConfigurationProperties(prefix ="spring.redis")
public class redisConfig extends CachingConfigurerSupport {
@Value("${spring.application.name}")
private String prefixName;
private Map<String, Long> expires;
@Bean
@SuppressWarnings("rawtypes")
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
//设置redis的名称
//cacheManager.setCacheNames(Arrays.asList("redis_first_"));
//设置开启前缀
cacheManager.setUsePrefix(true);
//设置默认的前缀
//cacheManager.setCachePrefix(new DefaultRedisCachePrefix(prefixName));
cacheManager.setDefaultExpiration(3000);
//自定义过期时间
cacheManager.setExpires(expires);
return cacheManager;
}
/**
* 配置redis
*/
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory){
StringRedisTemplate template = new StringRedisTemplate(factory);
Jackson2JsonRedisSerializer<Object> serializer =
new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
/*@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(factory);
return stringRedisTemplate;
}*/
public Map<String, Long> getExpires() {
return expires;
}
public void setExpires(Map<String, Long> expires) {
this.expires = expires;
}
}
实现原理
@Cacheable(value = "cache",key = "#p0")
@Override
public Student queryList(String name){
System.out.println(name);
Student s = new Student();
s.setId(1);
s.setFieldName("first-bllod");
s.setName(name);
return s;
}
基于动态代理模式对当前方法进行增强,之后通过CacheInterceptor拦截器 执行缓存管理,CacheAspectSupport做通用的缓存逻辑,包括组装目标类、处理注解(@Cacheable、@Cacheconfig)、解析SpeL表达式、维护CacheManager和RedisCache关系等等;
此外,spring-context作为缓存的超类, 基于spi的思想:只提供功能接口,实现由接入源(redis、guava等)实现Cache/CacheManager处理自己的源逻辑。
源码分析
org.springframework.cache.support.AbstractCacheManager
private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<String, Cache>(16);@Override
public Cache getCache(String name) {
Cache cache = this.cacheMap.get(name);
if (cache != null) {
return cache;
}
else {
// Fully synchronize now for missing cache creation...
synchronized (this.cacheMap) {
cache = this.cacheMap.get(name);
if (cache == null) {
cache = getMissingCache(name);
if (cache != null) {
cache = decorateCache(cache);
this.cacheMap.put(name, cache);
updateCacheNames(name);
}
}
return cache;
}
}
}
AbstractCacheMamager 中维护一份RedisCache的本地缓存, 未找到key->cacheName,调用getMissingCache()方法创建当前cachename 的cache对象, 并设置缓存时间。
ps:缓存时间可以通过expires,Bean配置中进行自定义。
@Override
protected Cache getMissingCache(String name) {
return this.dynamic ? createCache(name) : null;
}
@SuppressWarnings("unchecked")
protected RedisCache createCache(String cacheName) {
long expiration = computeExpiration(cacheName);
return new RedisCache(cacheName, (usePrefix ? cachePrefix.prefix(cacheName) : null), redisOperations, expiration,
cacheNullValues);
}
protected long computeExpiration(String name) {
Long expiration = null;
if (expires != null) {
expiration = expires.get(name);
}
return (expiration != null ? expiration.longValue() : defaultExpiration);
}
下一步通过org.springframework.cache.interceptor.CacheAspectSupport#execute处理目标方法上的注解,然后对目标方法进行缓存操作。
// Check if we have a cached item matching the conditions
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));
// Collect puts from any @Cacheable miss, if no cached item is found
List<CachePutRequest> cachePutRequests = new LinkedList<CachePutRequest>();
if (cacheHit == null) {
collectPutRequests(contexts.get(CacheableOperation.class),
CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
}
org.springframework.data.redis.cache.RedisCache#get(),cachekey不存在返回null,存在进行反序列化获得value。
public RedisCacheElement get(final RedisCacheKey cacheKey) {
Assert.notNull(cacheKey, "CacheKey must not be null!");
Boolean exists = (Boolean) redisOperations.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
return connection.exists(cacheKey.getKeyBytes());
}
});
if (!exists.booleanValue()) {
return null;
}
return new RedisCacheElement(cacheKey, fromStoreValue(lookup(cacheKey)));
}
写在最后:
Springcache 通过配置--注解的形式解藕业务逻辑代码,方便管理和操作。可以结合特定的缓存策略提高资源利用率和缓存命中率,此外还包括双写一致性问题都可以适当的处理这类问题。
干货分享
最近将个人学习笔记整理成册,使用PDF分享。关注我,回复如下代码,即可获得百度盘地址,无套路领取!
•001:《Java并发与高并发解决方案》学习笔记;•002:《深入JVM内核——原理、诊断与优化》学习笔记;•003:《Java面试宝典》•004:《Docker开源书》•005:《Kubernetes开源书》•006:《DDD速成(领域驱动设计速成)》•007:全部•008:加技术群讨论
近期热文
•LinkedBlockingQueue vs ConcurrentLinkedQueue•解读Java 8 中为并发而生的 ConcurrentHashMap•Redis性能监控指标汇总•最全的DevOps工具集合,再也不怕选型了!•微服务架构下,解决数据库跨库查询的一些思路•聊聊大厂面试官必问的 MySQL 锁机制
关注我
喜欢就点个"在看"呗^_^