前言

今天有网友咨询了一个问题:如何在一个工程中使用多种缓存进行差异化缓存,即实现多个cacheManager灵活切换。原来没有遇见这种场景,今天下班抽空试了下,以下就把如何实现的简单记录下。

一点知识

SpringBoot中使用Spring Cache可以轻松实现缓存,是Spring框架提供的对缓存使用的抽象类,支持多种缓存,比如RedisEHCache等,集成很方便。同时提供了多种注解来简化缓存的使用,可对方法进行缓存。具体如何集成,之前的文章已经有详细介绍了,感兴趣的同学可点击:SpringBoot | 第十一章:Redis的集成和简单使用。这里就不再阐述了,一下简单较少下cacheManager

关于CacheMananger

针对不同的缓存技术,需要实现不同的cacheManager,Spring定义了如下的cacheManger实现。

CacheManger 描述
SimpleCacheManager 使用简单的Collection来存储缓存,主要用于测试
ConcurrentMapCacheManager 使用ConcurrentMap作为缓存技术(默认)
NoOpCacheManager 测试用
EhCacheCacheManager 使用EhCache作为缓存技术,以前在hibernate的时候经常用
GuavaCacheManager 使用google guava的GuavaCache作为缓存技术
HazelcastCacheManager 使用Hazelcast作为缓存技术
JCacheCacheManager 使用JCache标准的实现作为缓存技术,如Apache Commons JCS
RedisCacheManager 使用Redis作为缓存技术

常规的SpringBoot已经为我们自动配置了EhCacheCollectionGuavaConcurrentMap等缓存,默认使用ConcurrentMapCacheManagerSpringBootapplication.properties配置文件,使用spring.cache前缀的属性进行配置。

application配置

spring.cache.type=#缓存的技术类型
spring.cache.cache-names=应用程序启动创建缓存的名称
spring.cache.ehcache.config=ehcache的配置文件位置
spring.cache.infinispan.config=infinispan的配置文件位置
spring.cache.jcache.config=jcache配置文件位置
spring.cache.jcache.provider=当多个jcache实现类时,指定选择jcache的实现类

这里为了演示多cacheManager实现,这里使用redisehcache进行集成。

集成Redis和ehcache

0.pom文件依赖

	    <!-- redis cache -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- ehcache缓存 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>

1.创建配置类。

CacheConfig.java

/**
*
* @ClassName 类名:CacheConfig
* @Description 功能说明:缓存配置类
* <p>
* TODO
*</p>
************************************************************************
* @date 创建日期:2019年3月7日
* @author 创建人:oKong
* @version 版本号:V1.0
*<p>
***************************修订记录*************************************
*
* 2019年3月7日 oKong 创建该类功能。
*
***********************************************************************
*</p>
*/
@Configuration
@EnableCaching
public class CacheConfig { /**
* cacheManager名称
*/
public interface CacheManagerName {
/**
* redis
*/
String REDIS_CACHE_MANAGER = "redisCacheManager"; /**
* ehCache
*/
String EHCACHE_CACHE_MAANGER = "ehCacheCacheManager";
}
/**
* 定义 StringRedisTemplate ,指定序列号和反序列化的处理类
* @param factory
* @return
*/
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
StringRedisTemplate template = new StringRedisTemplate(factory);
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(
Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
//序列化 值时使用此序列化方法
template.setValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
} @Bean(CacheConfig.CacheManagerName.REDIS_CACHE_MANAGER)
@Primary
public RedisCacheManager redisCacheManager(RedisTemplate<String,String> redisTemplate) {
RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
//使用前缀
rcm.setUsePrefix(true);
//缓存分割符 默认为 ":"
// rcm.setCachePrefix(new DefaultRedisCachePrefix(":"));
//设置缓存过期时间
//rcm.setDefaultExpiration(60);//秒
return rcm;
} @Bean(CacheConfig.CacheManagerName.EHCACHE_CACHE_MAANGER)
public EhCacheCacheManager EhcacheManager() {
EhCacheCacheManager ehCacheManager = new EhCacheCacheManager();
return ehCacheManager;
}
}

注:其实就是配置多个cacheManager。但这里需要注意,要设置一个默认的cacheManager,即注解在未设置cacheManager时,自动使用此缓存管理类进行缓存,同时,因为注入了多个cacheManaager,需要在默认的管理器方法上加上@Primary注解。不然,会出现一下异常:

No CacheResolver specified, and no unique bean of type CacheManager found. Mark one as primary (or give it the name 'cacheManager') or declare a specific CacheManager to use, that serves as the default one.

至于原因。可以查看以下代码:

其实就是配置了多个bean,抛出了一个NoUniqueBeanDefinitionException异常。其实就是未指定一个默认的cacheManager,所以加上@Primary即可。

@Primary 优先考虑,优先考虑被注解的对象注入

2.编写测试类,默认是使用redis缓存,若想指定缓存,只需要设置cacheManager的值即可。

/**
*
* @ClassName 类名:DemoController
* @Description 功能说明:
* <p>
* TODO
*</p>
************************************************************************
* @date 创建日期:2019年3月7日
* @author 创建人:oKong
* @version 版本号:V1.0
*<p>
***************************修订记录*************************************
*
* 2019年3月7日 oKong 创建该类功能。
*
***********************************************************************
*</p>
*/
@RestController
@Slf4j
public class DemoController { @RequestMapping("/redis/{key}")
@Cacheable(value = "redis",key="#key",cacheManager=CacheConfig.CacheManagerName.REDIS_CACHE_MANAGER)
public String cacheRedisTest(@PathVariable("key") String key) {
log.info("redis,key={}", key);
return key;
} @RequestMapping("/ehcache/{key}")
@Cacheable(value = "oKongCache",key="#key",cacheManager=CacheConfig.CacheManagerName.EHCACHE_CACHE_MAANGER)
public String cacheEhcacheTest(@PathVariable("key") String key) {
log.info("ehcache,key={}", key);
return key;
} @RequestMapping("/default/{key}")
@Cacheable(value = "default",key="#key")
public String cacheDefaultTest(@PathVariable("key") String key) {
log.info("default,key={}", key);
return key;
}
}

3.配置application文件,加入相关配置。

# REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0 # ehcache配置地址
spring.cache.ehcache.config=ehcache.xml

配置ehcache.xml文件,设置cacheName

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd">
<!--timeToIdleSeconds 当缓存闲置n秒后销毁 -->
<!--timeToLiveSeconds 当缓存存活n秒后销毁 -->
<!-- 缓存配置
name:缓存名称。
maxElementsInMemory:缓存最大个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。 diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
maxElementsOnDisk:硬盘最大缓存个数。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk
store persists between restarts of the Virtual Machine. The default value
is false.
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是
LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。 -->
<!-- 磁盘缓存位置 -->
<diskStore path="java.io.tmpdir" />
<!-- 默认缓存 -->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap" />
</defaultCache> <!-- 指定cache,即对应cacheName的值 -->
<cache name="oKongCache"
eternal="false"
timeToIdleSeconds="2400"
timeToLiveSeconds="2400"
maxEntriesLocalHeap="10000"
maxEntriesLocalDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU">
</cache>
</ehcache>

关于其属性参数,大家可自行百度下,使用的不多呀,(┬_┬)

4.启动应用。

依次访问:

  1. http://127.0.0.1:8080/redis/okong
  2. http://127.0.0.1:8080/ehcache/okong
  3. http://127.0.0.1:8080/default/okong

可以看看redis中已经存在相关记录了

之后多访问几次,查看控制台,是没有输出的。

参考资料

  1. https://docs.spring.io/spring-boot/docs/1.5.15.RELEASE/reference/htmlsingle/#boot-features-caching-provider

总结

本章节主要介绍了多cacheManager的灵活切换,以便实现更加灵活的缓存使用,可以根据具体的业务需求,进行差异化操作。关于ehcache的使用,现在用的不多了,所以相关配置参数,可以自行搜索下了。

最后

目前互联网上很多大佬都有SpringBoot系列教程,如有雷同,请多多包涵了。原创不易,码字不易,还希望大家多多支持。若文中有所错误之处,还望提出,谢谢。

老生常谈

  • 个人QQ:499452441
  • 微信公众号:lqdevOps

个人博客:http://blog.lqdev.cn

完整示例:https://github.com/xie19900123/spring-boot-learning/tree/master/chapter-36

原文地址:https://blog.lqdev.cn/2019/03/08/springboot/chapter-thirty-six/

SpringBoot | 第三十六章:集成多CacheManager的更多相关文章

  1. Gradle 1.12用户指南翻译——第三十六章. Sonar Runner 插件

    本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  2. “全栈2019”Java第三十六章:类

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  3. SpringBoot | 第三十五章:Mybatis的集成和使用

    前言 最近收到公众号留言说,单纯的Mybatis的集成和使用.前面在第九章:Mybatis-plus的集成和使用介绍了基于mybatis-plus的集成和使用.后者也只是对mybatis进行了功能增强 ...

  4. SpringBoot | 第三十二章:事件的发布和监听

    前言 今天去官网查看spring boot资料时,在特性中看见了系统的事件及监听章节.想想,spring的事件应该是在3.x版本就发布的功能了,并越来越完善,其为bean和bean之间的消息通信提供了 ...

  5. “全栈2019”Java多线程第三十六章:如何设置线程的等待截止时间

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 下一章 "全栈2019"J ...

  6. SpringBoot | 第三十四章:CXF构建WebService服务

    前言 上一章节,讲解了如何使用Spring-WS构建WebService服务.其实,创建WebService的方式有很多的,今天来看看如何使用apache cxf来构建及调用WebService服务. ...

  7. 【WPF学习】第三十六章 样式基础

    前面三章介绍了WPF资源系统,使用资源可在一个地方定义对象而在整个标记中重用他们.尽管可使用资源存储各种对象,但使用资源最常见的原因之一是通过他们的保存样式. 样式是可应用于元素的属性值集合.WPF样 ...

  8. 第三十六章 Linux常用性能检测的指令

    作为一个Linux运维人员,介绍下常用的性能检测指令! 一.uptime 命令返回的信息: 19:08:17              //系统当前时间 up 127 days,  3:00     ...

  9. 第三十六章 metrics(4)- metrics-graphite

    将metrics report给graphite(carbon-relay) 一.代码 1.pom.xml <!-- metrics-graphite --> <dependency ...

随机推荐

  1. leetcode 加一

    给定一个非负整数组成的非空数组,在该数的基础上加一,返回一个新的数组. 最高位数字存放在数组的首位, 数组中每个元素只存储一个数字. 你可以假设除了整数 0 之外,这个整数不会以零开头. 示例 1: ...

  2. django drf viewsets和routers

    1.定义VIew from django.shortcuts import render from rest_framework.views import APIView from rest_fram ...

  3. centos7 安装git

    centos7下git的安装和配置   git的安装: yum 源仓库里的 Git 版本更新不及时,最新版本的 Git 是 1.8.3.1,但是官方最新版本已经到了 2.9.2.想要安装最新版本的的 ...

  4. windows 多个人同时远程同一台电脑

    windows  多个人同时远程同一台电脑 第一步:(内外远程) 参考内网多个人同时远程一台电脑: http://www.cnblogs.com/zlp520/p/7688984.html 第二步:( ...

  5. .net 抽象类(abstract)和接口(interface)区别

    1.抽象类    (1) 抽象方法只作声明,而不包含实现,可以看成是没有实现体的虚方法    (2) 抽象类不能被实例化    (3) 抽象类可以但不是必须有抽象属性和抽象方法,但是一旦有了抽象方法, ...

  6. asp.net(mvc) 框架

    1.NFine mvc+ef 2.Grove orm架构 3.NHibernate orm 4.NBear 5.petshop 6.Membership 7.Brnshop 网上商城 8.cms快速开 ...

  7. [HAOI2008] 排名系统

    题目链接:戳我 要注意因为数可能会对应很多人,但是输出的时候要按照添加的顺序输出.所以我们不能将相同值的节点合并,用set维护.就算值相同也只能新开节点. 然后就没有什么了...懒得写哈希表..直接上 ...

  8. Android学习之 adb被占用解决办法

    1.adb被占用解决办法 方法一:(1)查看5037端口哪个进程在用          netstat -a -o 5037 (2)查看上面进程是哪个执行文件在占用          tasklist ...

  9. luoguP2479 [SDOI2010]捉迷藏

    https://www.luogu.org/problemnew/show/P2479 据说可以用线段树做但是我不会,只能写一个 KD-Tree 了 对于每个点求出距离它最远的点和最近的点的距离,然后 ...

  10. [转] 以普通用户启动的Vim如何保存需要root权限的文件

    [转] 以普通用户启动的Vim如何保存需要root权限的文件 在Linux上工作的朋友很可能遇到过这样一种情况,当你用Vim编辑完一个文件时,运行:wq保存退出,突然蹦出一个错误: E45: 'rea ...