SpringCache缓存初探
简易入门
一、作用
当我们在调用一个缓存方法时会根据相关信息和返回结果作为一个键值对存放在缓存中,等到下次利用同样的参数来调用该方法时将不再执行该方法,而是直接从缓存中获取结果进行返回。
二、启用方式
1.POM.xml 文件中添加spring cache依赖(Spring Boot)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2.添加一种cacheManager的bean
若注解了@EnableCaching,则spring可自动发现并配置cacheManager,只要有一种可用于缓存提供的即可,详情见文献[1]。常用的有Ehcache、redis等实现
为方便起见,这里使用最简单的内存map实现。(可以使用Redis来提供CacheManager。详见附录)
@Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Collections.singletonList(new ConcurrentMapCache("models")));
return cacheManager;
}
3.启用注解@EnableCaching
@Configuration
@EnableCaching
public class CacheConfiguration {
//...
}
三、使用方式
三个主要的注解 Cacheable (最常用的注解,用于标注需要缓存方法)、CacheEvict(用于仅清除缓存)、CachePut(用于仅存放缓存)
先定义一个测试POJO: TestModel。 含有name和address两个字符串变量。
class TestModel {
String name;
String address;
// 省略getter和setter
}
1.Cacheable
例子:
@Cacheable(value = "models", key = "#testModel.name", condition = "#testModel.address != '' ")
public TestModel getFromMem(TestModel testModel) throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
testModel.setName(testModel.getName().toUpperCase());
return testModel;
}
例子里的注解@Cacheable中存在有以下几个元素
- value (也可使用 cacheNames) : 可看做命名空间,表示存到哪个缓存里了。
- key : 表示命名空间下缓存唯一key,使用Spring Expression Language(简称SpEL,详见参考文献[5])生成。
- condition : 表示在哪种情况下才缓存结果(对应的还有unless,哪种情况不缓存),同样使用SpEL
当第一次使用
{name: 'XiaoMing', address: 'ChengDu'}
调用getFromMem时,会等待一秒钟,然后返回
{name: 'XIAOMING', address: 'ChengDu'}
当再次使用name为’XiaoMing’的对象作为参数调用getFromMem时,会立即返回上一个结果,无论参数中的address是什么。
但是如果第一次调用时,address为空字符串,第二次调用仍然需要等待一秒钟,这就是condition的作用。
2.CacheEvict
例子:
@CacheEvict(value = "models", allEntries = true)
@Scheduled(fixedDelay = 10000)
public void deleteFromRedis() {
}
@CacheEvict(value = "models", key = "#name")
public void deleteFromRedis(String name) {
}
例子里的注解 @CacheEvict 中存在有以下几个元素
- value (也可使用 cacheNames) : 同Cacheable注解,可看做命名空间。表示删除哪个命名空间中的缓存
- allEntries: 标记是否删除命名空间下所有缓存,默认为false
- key: 同Cacheable注解,代表需要删除的命名空间下唯一的缓存key。
例子中第一段,与
@Scheduled
注解同时使用,每十秒删除命名空间name下所有的缓存。
第二段,调用此方法后删除命名空间models下, key == 参数 的缓存
同样含有unless与condition
3.CachePut
例子
@CachePut(value = "models", key = "#name")
public TestModel saveModel(String name, String address) {
return new TestModel(name, address);
}
例子里的注解 @CachePut 中存在有以下几个元素
- value: 同上
- key: 同上
- condition(unless): 同上
比如可用于后台保存配置时及时刷新缓存。
以上三个注解中还有少量未提到的元素,于附录中叙述。
文献与深入
1.文献
[1] Spring Boot中的缓存支持(一)注解配置与EhCache使用
[2]
Spring Boot中的缓存支持(二)使用Redis做集中式缓存
[3]
A Guide To Caching in Spring
[4]
spring cache官方文档
[5]
Spring Expression Language官方文档
2.创建RedisCacheManager
这里我们依赖spring redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
然后在配置中添加RedisConnectionFactory用于获取redis链接
@Bean
public RedisConnectionFactory redisConnectionFactory() {
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
jedisConnectionFactory.setHostName("127.0.0.1");
jedisConnectionFactory.setPort(6379);
return jedisConnectionFactory;
}
还需要一个RedisTemplate。RedisTemplate提供了对Jedis API的高级封装,使用serializers序列化key和value,用于将java与字符串相互转换。这里使用Jackson的serializers(详见附录),来将java对象序列化为json字符串。
jackson类似gson
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
Jackson2JsonRedisSerializer<Object> serializer = jackson2JsonRedisSerializer();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(serializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(serializer);
return redisTemplate;
}
然后就可以得到一个RedisCacheManager
@Bean
public CacheManager redisCacheManager() {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate());
cacheManager.setDefaultExpiration(300);
cacheManager.setLoadRemoteCachesOnStartup(true); // 启动时加载远程缓存
cacheManager.setUsePrefix(true); //是否使用前缀生成器
// 这里可进行一些配置 包括默认过期时间 每个cacheName的过期时间等 前缀生成等等
return cacheManager;
}
运行文中”三”示例程序,并且可以观察到redis中多了一个models:XiaoMing的key
其中的值为
[\"com.xxx.TestModel\",{\"name\":\"XIAOMING\",\"address\":\"ChengDu\"}]
注意这里的序列化的实现方式,保存了对象类的信息,保证方法返回与缓存返回一致(当然如果你的setter getter实现不一致,还是会造成不一致的)。
我曾经因为手动序列化造成了一个BUG,一个有序Map存进去,反序列化出来失去了有序性 ,当然方法返回值定义成了Map而不是SortedMap也是一个重要原因。
3. 上一条中使用的Jackson2JsonRedisSerializer
@Bean
public Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {
final Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
final ObjectMapper objectMapper = Jackson2ObjectMapperBuilder
.json().build();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
return jackson2JsonRedisSerializer;
}
4. Cache* 注解中未提到的元素
共同的:
- keyGenerator: 实现
org.springframework.cache.interceptor.KeyGenerator接口的类bean,用于统一自定义生成key - cacheManager: 用于选择使用哪个cacheManager
- cacheResolver: 实现
org.springframework.cache.interceptor.CacheResolver接口的类bean,用于自定义如何处理缓存
CacheEvict:
- beforeInvocation: bool值,标志是否在调用前就清除缓存。防止方法因为异常意外退出。
Cacheable:
- sync: 是否同步 从相同key加载值 的方法,原文为:
Synchronize the invocation of the underlying method if several threads are
attempting to load a value for the same key如何同步加载缓存,具体需要看CacheManager的实现。(2019年10月11日 此部分在https://www.cnblogs.com/imyijie/p/11651679.html SpringCache - 请求级别缓存的简易实现 有进一步的讨论)
5. 关于缓存TTL等设置
请查看各 CacheManager实现 的配置。
SpringCache缓存初探的更多相关文章
- WEB缓存初探
WEB缓存初探 概念理解 缓存--缓存就是数据交换的缓冲区(称作Cache) 缓存 的作用说白了就是用来就近获取东西,比如我们会把已经拿到的常用的东西放在手边(与自己相对较近的地方),方便下次需要时去 ...
- ibatis缓存初探(1)
一,IBATIS 缓存机制使用 1,sqlMapConfig.xml中配置 1.SqlMapConfig.xml中 <settings cacheModelsEnabled="tr ...
- HTTP缓存初探
缓存的作用 用户访问一个web页面的频率远高于web页面更新的频率,因此多数时候用户从服务器获取的html.js.css以及图片等内容都是相同的,如果每次访问都从服务器获取这些静态内容即降低了页面加载 ...
- SpringCache - 请求级别缓存的简易实现
前言 在SpringCache缓存初探中我们研究了如何利用spring cache已有的几种实现快速地满足我们对于缓存的需求.这一次我们有了新的更个性化的需求,想在一个请求的生命周期里实现缓存. 需求 ...
- @ENABLECACHING 基于注解的缓存
@EnableCaching• @Cacheable指定一个或多个Cache名字,同属性cacheNamesSpring Cache 使用 ---@EnableCaching @Cacheable 注 ...
- Atitit s2018 s4 doc list dvchomepc dvccompc.docx .docx \s2018 s4 doc compc dtS44 \s2018 s4 doc dvcCompc dtS420 \s2018 s4f doc homepc \s2018 s4 doc compc dtS44\(5 封私信 _ 44 条消息)WebSocket 有没有可能取代 AJAX
Atitit s2018 s4 doc list dvchomepc dvccompc.docx .docx \s2018 s4 doc compc dtS44 \s2018 s4 doc dvcCo ...
- SpringBoot基础系列-SpringCache使用
原创文章,转载请标注出处:<SpringBoot基础系列-SpringCache使用> 一.概述 SpringCache本身是一个缓存体系的抽象实现,并没有具体的缓存能力,要使用Sprin ...
- MyBatis 一级缓存、二级缓存全详解(一)
目录 MyBatis 一级缓存.二级缓存全详解(一) 什么是缓存 什么是MyBatis中的缓存 MyBatis 中的一级缓存 初探一级缓存 探究一级缓存是如何失效的 一级缓存原理探究 还有其他要补充的 ...
- Spring优雅整合Redis缓存
“小明,多系统的session共享,怎么处理?”“Redis缓存啊!” “小明,我想实现一个简单的消息队列?”“Redis缓存啊!” “小明,分布式锁这玩意有什么方案?”“Redis缓存啊!” “小明 ...
随机推荐
- 屏蔽ps联网激活
屏蔽ps联网激活C:\Windows\system32\drivers\etc\host127.0.0.1 lm.licenses.adobe.com127.0.0.1 na1r.services.a ...
- inheritprototype原型继承封装及综合继承最简实例
1.inheritprototype.js ;(function(){ var s = { inheritObject:function(o){//对象继承封装 ...
- DDOS攻击(流量攻击)防御步骤
DDOS全名是Distributed Denial of service (分布式拒绝服务攻击),很多DOS攻击源一起攻击某台服务器就组成了DDOS攻击,DDOS 最早可追溯到1996年最初,在中国2 ...
- 扩展BaseAdapter实现不存储列表项的ListView
下面的实例将会通过扩展BaseAdapter来实现Adapter,扩展BaseAdapter可以取得对Adapter最大的控制权:程序要创建多个列表项,每个列表项的组件都由开发者来决定. 下面的布局文 ...
- Java Swing paint repaint update 方法的关系
Java Swing paint repaint update 方法的关系: 参考:http://blog.csdn.net/xiaoliangmeiny/article/details/691665 ...
- WInform 创建一个简单的WPF应用
(一)创建一个简单的WPF应用 首先,在这里我要说明的是:这里的例子,都是通过控制台程序来创建WPF应用,而非使用现成的WPF模版.因为WPF模版封装了创建WPF应用所需要的各种基本元素,并不利于我们 ...
- 游戏开发之UE4添加角色到场景中
接着上次继续学习,现在我们已经有了一个场景并且运行了,我们需要添加一个角色到场景中.要这样做,我们必须从UE4的GameFramework类继承它. 一. 创建一个从Character类继承的类 从基 ...
- 比AutoMapper轻量快速简洁的实体映射库YeaJur.Mapper
之前在做实体深拷贝时一直使用的AutoMapper,但是总是觉得其写法比繁琐,效率也不算很高,自己写了一个轻量级的实体拷贝库YeaJur.Mapper,和AutoMapper 测试对比如下 PK项 Y ...
- 如何让微信小程序快速接入七牛云
如果你确定用七牛运行小程序的话,给大家分享一个九折优惠码:61d1fd4d1 月 9 日 微信小程序正式发布,小程序终于揭开了它神秘的面纱,开发者对小程序的追捧更是热度不减.从小程序的热门应用场景来看 ...
- ECMAScript 6 笔记(五)
Iterator和for...of循环 1. Iterator(遍历器)的概念 Iterator接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即for...of循环 遍历器(Iterato ...