SpringCache缓存初探

摘要:
classTestModel{Stringname;Stringaddress;//省略getter和setter}1.Cacheable例子:@Cacheable(value="models",key="#testModel.name",condition="#testModel.address!表示删除哪个命名空间中的缓存-allEntries:标记是否删除命名空间下所有缓存,默认为false-key:同Cacheable注解,代表需要删除的命名空间下唯一的缓存key。第二段,调用此方法后删除命名空间models下,key==参数的缓存同样含有unless与condition3.CachePut例子@CachePutpublicTestModelsaveModel{returnnewTestModel;}例子里的注解@CachePut中存在有以下几个元素value:同上key:同上condition:同上比如可用于后台保存配置时及时刷新缓存。
简易入门

一、作用

当我们在调用一个缓存方法时会根据相关信息和返回结果作为一个键值对存放在缓存中,等到下次利用同样的参数来调用该方法时将不再执行该方法,而是直接从缓存中获取结果进行返回。

二、启用方式

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* 注解中未提到的元素

共同的:

  1. keyGenerator: 实现 org.springframework.cache.interceptor.KeyGenerator 接口的类bean,用于统一自定义生成key
  2. cacheManager: 用于选择使用哪个cacheManager
  3. cacheResolver: 实现 org.springframework.cache.interceptor.CacheResolver 接口的类bean,用于自定义如何处理缓存

CacheEvict:

  1. beforeInvocation: bool值,标志是否在调用前就清除缓存。防止方法因为异常意外退出。

Cacheable:

  1. 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.htmlSpringCache - 请求级别缓存的简易实现 有进一步的讨论)

5. 关于缓存TTL等设置

请查看各 CacheManager实现 的配置。

免责声明:文章转载自《SpringCache缓存初探》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇数据库——Oracle(增删改查,单行函数,多行函数,多表查询)css中position(absolute)与margin同时使用的情况下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

第九章:(3)Spring Boot 与 缓存 之 @CachePut 注解

一、@CachePut @CachePut:既调用方法,又更新缓存数据,同步更新缓存,修改了数据库的某个数据,同时更新缓存。注意:存取使用的 key 必须是同一个。 运行时机:1、先调用目标方法2、将目标方法的结果缓存起来 测试步骤: 1、先查询1号员工,查到的结果会放到缓存中 key:id=1 的参数 value: 1号员工 2、以后查询还是之前缓存中...

磁盘缓存

磁盘缓存 目录 什么是磁盘缓存 磁盘缓存方式 硬盘的缓冲区 编辑本段什么是磁盘缓存   磁盘缓存分为读缓存和写缓存。  读缓存是指,操作系统为已读取的文件数据,在内存较空闲的情况下留在内存空间中(这个内存空间被称之为“内存池”),当下次软件或用户再次读取同一文件时就不必重新从磁盘上读取,从而提高速度。  写缓存实际上就是将要写入磁盘的数据先保存...

HTTP请求流程你了解了么?

我又回来了,先来波推广,最硬的资源来自公众号:前端美食汇,欢迎大家关注公众号获取最新的技术。提示,文末有福利,最硬的文章会首先发布在公众号上喔 预备知识 前文没有描述到传输和协议直接的层级对应关系,大概补充下网络通信中数据传输对应的协议,首先了解下OSI(开放式系统互联:Open System InterConnection)七层 模式,及其对应不同层次的...

重建win7桌面图标缓存

在整理了桌面图标后,经常出现一些快捷方式的图标无法显示的问题。这就需要重建桌面图标缓存。 1、进入C:\Users\Admin\AppData\Local找到IconCache.db并删除 2、删除explorer.exe进程,重建explorer.exe进程...

C#正则表达式引发的CPU跑高问题以及解决方法团队

3月23日(周日)下午16:30左右,博客园主站负载均衡中的2台Web服务器CPU玩起了爬楼梯的游戏(见上图),一直爬到了接近100%。发现这个状况后,我们立即将这2台阿里云临时磁盘云服务器从负载均衡中摘下来,挂上1台云盘云服务器,恢复了正常。 由于曾经多次遇到过阿里云云服务器CPU问题,现在对阿里云云服务器产生了一种偏见,只要出现CPU问题,就会首先怀...

mybatis 详解(九)------ 一级缓存、二级缓存

上一章节,我们讲解了通过mybatis的懒加载来提高查询效率,那么除了懒加载,还有什么方法能提高查询效率呢?这就是我们本章讲的缓存。   本篇源码下载链接:http://pan.baidu.com/s/1eRHTsIm 密码:a5wn   mybatis 为我们提供了一级缓存和二级缓存,可以通过下图来理解:      ①、一级缓存是SqlSession级别...