java redis 命名空间_Redis在SpringBoot中的实践_御风游的博客-程序员宅基地

技术标签: java redis 命名空间  

Redis,目前非常流行的内存数据库,其广泛应用于Web场景的缓存技术下。本文简要介绍在SpringBoot下的Redis的实践应用

配置Redis

1. 添加Redis依赖

在pom.xml中添加Redis依赖

org.springframework.boot

spring-boot-starter-data-redis

2.0.4.RELEASE

org.apache.commons

commons-pool2

2.6.1

2. 添加Redis服务器配置

在application.properties配置文件中添加Redis服务器参数配置。

# Redis 服务器配置

# Redis 数据库索引

spring.redis.database=0

# Redis 服务器地址

spring.redis.host=127.0.0.1

# Redis 服务器连端口

spring.redis.port=6379

# Redis 服务器连接密码

spring.redis.password=123456

3. 添加Redis客户端配置

Spring Boot从2.X版本开始引入新的客户端——Lettuce。较之前的Jedis相比而言,Lettuce是一个基于Netty的线程安全的Redis客户端。故,我们这里推荐使用Lettuce,并在application.properties配置文件中对Lettuce客户端进行配置

[Note]:配置文件中的Redis客户端配置可以不写,则Spring Boot(2.X及以上版本)会默认使用Lettuce客户端

当我们在配置文件显式地配置Redis客户端时,需要添加commons-pool2连接池依赖

# Redis客户端Lettuce配置

# 连接池最大连接数(使用负值表示没有限制) 默认 8

spring.redis.lettuce.pool.max-active=8

# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1

spring.redis.lettuce.pool.max-wait=-1

# 连接池中的最大空闲连接 默认 8

spring.redis.lettuce.pool.max-idle=8

# 连接池中的最小空闲连接 默认 0

spring.redis.lettuce.pool.min-idle=0

4. 编写Java Config类自定义RedisTemplate Bean用于直接操作Reids,而不使用SpringBoot默认提供的,故该Bean实例名称必须要为redisTemplate,这样才可以避免Spring生成默认的RedisTemplate实例。对于Redis中的Key使用String序列化,而对于Value则使用更通用的Json序列化,而不是默认的JDK序列化

自定义Spring Cache的缓存管理器CacheManager,配置其使用redis数据库中间件,这样我们即可利用Spring Cache的注解快速操作缓存的查询和删除,而不是直接去使用redisTemplate来访问redis数据库

[Note]:值得一提的是,这里虽然为了方便将RedisTemplate的配置类和CacheManager的配置类写在了一起,但是显然两者是两个相对独立的东西,Spring Cache是对各种缓存中间件(包含但不止于Redis)的抽象,通过注解统一缓存的操作方式,而Redis则是一种具体的缓存实现方案

需要在配置类上添加@EnableCaching注解,以使能Spring Cache

@Slf4j

@Configuration

@EnableCaching // 使能Spring Cache缓存 public class RedisConfig extends CachingConfigurerSupport{

// Key 过期时间: 1day = 86400s private Duration timeToLive = Duration.ofDays(1);

// Spring Cache 配置类 @Bean(name="cacheManager")

public CacheManager cacheManager(RedisConnectionFactory factory){

RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()

.entryTtl( timeToLive ) // 设置缓存的过期时间 .computePrefixWith(cacheName -> cacheName + ":") // 无该行代码,则Spring Cache 默认使用::用作命名空间的分隔符 .serializeKeysWith( RedisSerializationContext.SerializationPair.fromSerializer( getKeySerializer() ) ) // 设置Key序列化器 .serializeValuesWith( RedisSerializationContext.SerializationPair.fromSerializer( getValueSerializer() ) ) // 设置Value序列化器 .disableCachingNullValues();

RedisCacheManager redisCacheManager = RedisCacheManager.builder(factory)

.cacheDefaults( redisCacheConfiguration )

.build();

log.info(" 自定义Spring Cache Manager配置完成 ... ");

return redisCacheManager;

}

// Redis 配置类 // 自定义的RedisTemplate的Bean名称必须为 redisTemplate。当方法名不为 redisTemplate时,可通过name显示指定bean名称,@Bean(name="redisTemplate") @Bean(name = "redisTemplate")

public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {

RedisTemplate template = new RedisTemplate<>();

template.setConnectionFactory(redisConnectionFactory);

// 设置String Key序列化器 template.setKeySerializer( getKeySerializer() );

template.setValueSerializer( getValueSerializer() );

// 设置Hash Key序列化器 template.setHashKeySerializer( getKeySerializer() );

template.setHashValueSerializer( getValueSerializer() );

log.info("自定义RedisTemplate配置完成 ... ");

return template;

}

// key 采用String序列化器 private RedisSerializer getKeySerializer() {

return new StringRedisSerializer();

}

// value 采用Json序列化器 private RedisSerializer getValueSerializer() {

return new GenericJackson2JsonRedisSerializer();

}

}

缓存操作

直接使用RedisTemplate操作缓存

1. 封装RedisTemplate操作

将redisTemplate常用方法封装到RedisService中,便于我们后面使用

/*** Redis 操作类*/

@Service

public class RedisService {

@Autowired

private RedisTemplate redisTemplate;

/*----------------------- key 基本操作 ----------------------------*/

/*** 判断 key 是否存在* @param key* @return*/

public Boolean existKey(String key) {

return redisTemplate.hasKey(key);

}

/*** 删除 key* @param key*/

public Boolean deleteKey(String key) {

return redisTemplate.delete(key);

}

/*** 获取 key-value 的 数据类型* @param key* @return*/

public DataType typeKey(String key) {

return redisTemplate.type(key);

}

/*** 设置key的过期时间* @param key* @param timeout* @param unit* @return*/

public Boolean setExpire(String key, long timeout, TimeUnit unit{

return redisTemplate.expire(key, timeout, unit);

}

/*** 获取 key 的剩余过期时间* @param key* @param unit* @return*/

public Long getExpire(String key, TimeUnit unit){

return redisTemplate.getExpire(key, unit);

}

/*** 获取 key 的剩余过期时间* @param key* @return*/

public Long getExpire(String key) {

return redisTemplate.getExpire(key);

}

/*** 移除 key 的过期时间,该 key 将永久存在* @param key* @return*/

public Boolean persistExpire(String key) {

return redisTemplate.persist(key);

}

/*** 将 key 移动至给定index编号的数据库中* @param key* @param dbIndex* @return*/

public Boolean move(String key, int dbIndex) {

return redisTemplate.move(key, dbIndex);

}

/*----------------------- String key 操作 ----------------------------*/

/*** 设置String key的值* @param key* @param value*/

public void setStringKey(String key, Object value) {

redisTemplate.opsForValue().set(key, value);

}

/*** 获取String key的值* @param key* @return*/

public Object getStringKey(String key) {

return redisTemplate.opsForValue().get(key);

}

/*----------------------- Hash key 操作 ----------------------------*/

/*** 设置Hash Key中指定字段的值* @param key* @param field* @param value*/

public void setHashKey(String key, String field, Object value) {

redisTemplate.opsForHash().put(key, field, value);

}

/*** 获取Hash Key中指定字段的值* @param key* @param field* @return*/

public Object getHashKey(String key, String field) {

return redisTemplate.opsForHash().get(key, field);

}

/*** 获取Hash Key全部字段的值* @param key* @return*/

public Map getHashKeyAll(String key) {

return redisTemplate.opsForHash().entries(key);

}

/*** 判断Hash Key中指定字段是否存在* @param key* @param field* @return*/

public Boolean existField(String key, String field) {

return redisTemplate.opsForHash().hasKey(key, field);

}

/*** 根据方法名和Map参数生成Hash Key的filed字段值* @param methodName* @param map* @return*/

public String getField(String methodName, Map map){

StringBuilder sb = new StringBuilder(methodName);

if (!MapUtil.isEmpty(map)) {

sb.append(JSONUtil.parseObj(map));

}

return sb.toString();

}

}

2. 编写测试用例

这里分别提供了一个String Key的写、读缓存测试用例,验证我们的Redis缓存服务是否设置成功

@Controller

@ResponseBody

@RequestMapping(value = "testRedis")

public class TestRedisController {

@Autowired

private RedisService redisService;

/*------------------------- Test String Key -------------------------------*/

@GetMapping("/testSetStringKey/{id}")

public String testSetStringKey(@PathVariable Integer id) {

Student stu = new Student();

stu.setId(id);

stu.setSex("女:" + id);

stu.setUsername("Amy");

// 写入缓存 redisService.setStringKey("testStringKey-" + id, stu);

return "缓存写入成功";

}

@GetMapping("/testGetStringKey/{id}")

public Student testGetStringKey(@PathVariable Integer id) {

// 读取缓存 Student stu = (Student)redisService.getStringKey("testStringKey-" + id);

return stu;

}

}

3. 测试

在Redis控制台中执行 keys * 命令,查看Redis中当前存在Key

向 http://localhost:8080/testRedis/testSetStringKey/1234 发送请求以实现向Redis中写入缓存

看看Redis数据库是否发生了变化,从下图可以看到,新加入了一个名为testStringKey-1234的String Key,其值为Student对象Json序列化后的字符串,此时,说明缓存写入成功

向 http://localhost:8080/testRedis/testGetStringKey/1234 发送请求以实现从Redis中读取相应的缓存数据,下图结果表明缓存读取成功

通过Sping Cache注解操作缓存

@Cacheable注解

该注解可以标记在方法、类上,表明该方法是支持缓存的。当调用该方法时,Spring Cache会首先检查该方法对应的缓存。如果缓存中该Key存在,则直接将缓存Key中的Value作为方法的结果返回,而无需进入并执行方法;如果缓存中无指定Key,则进入并执行方法,在将返回值返回的同时将其存入缓存中,以便下次调用该方法时,直接从缓存中获取方法的执行结果,而无需再进入方法执行

可用属性:value: 缓存名称。在Redis中其实际上即为命名空间。默认使用::作为命名空间的分隔符,上文的RedisConfig配置类的cacheManager()方法中,自定义:作为Redis的命名空间

key: 缓存Key名。自定义缓存Key名中可以使用“#参数”方式引用形参中的值

unless: 方法返回值不存入缓存的条件, 使用#result引用方法的返回值

测试代码如下:

@Controller

@ResponseBody

@RequestMapping("testSpringCache")

public class TestSpringCacheController {

private static final String cacheName = "SpringCacheTest";

@RequestMapping(value = "/testCacheable")

@Cacheable(value = cacheName, key = "#name+'-'+#age", unless = "#result == null")

public User testCacheable(@RequestParam String name, @RequestParam Integer age) {

User user = new User();

user.setName(name);

user.setAge(age);

return user;

}

}

Redis数据库中的情况

我们向 http://localhost:8080/testSpringCache/testCacheable?name=Aaron&age=18 发送请求后,再查看此时Redis数据库中的情况,即可看到多了一个Key为SpringCacheTest:Aaron-18的缓存,如上所述,将SpringCacheTest作为命名空间的名称,使用:作为命名空间的分隔符,而该缓存的过期时间TTL也为上文配置的RedisConfig.timeToLive值

@CacheEvict注解

该注解可以标记在方法、类上,表明调用该方法是即清除指定缓存。当调用该方法时,Spring Cache即会清除指定的缓存

可用属性:value: 指定欲清除key所在的缓存名称(命名空间名)

key : 指定欲清除的缓存Key名 - allEntries: 是否清除指定命名空间下的所有key的缓存,默认为false。当其为true时,将忽略key属性配置指定key

beforeInvocation: 是否在调用方法前去清除指定缓存。当其为true时,将会在方法执行前清除缓存;当其为false时,将会在方法成功执行后清除缓存,如果方法未成功执行(抛出异常)将无法清除缓存

测试代码如下:

@Controller

@ResponseBody

@RequestMapping("testSpringCache")

public class TestSpringCacheController {

private static final String cacheName = "SpringCacheTest";

@RequestMapping(value = "/testOneCacheEvict")

@CacheEvict(value = cacheName, key="'Tony'",beforeInvocation = true)

public void testOneCacheEvict() {

System.out.println("清除cacheNamem[SpringCacheTest]命名空间下的key[Tony]缓存");

return;

}

@RequestMapping(value = "/testAllCacheEvict")

@CacheEvict(value = cacheName, allEntries = true,beforeInvocation = true)

public void testAllCacheEvict() {

System.out.println("清除cacheNamem[SpringCacheTest]命名空间下的所有缓存");

return;

}

}

Redis数据库中的情况:

我们向 http://localhost:8080/testSpringCache/testOneCacheEvict 发送请求后,再查看此时Redis数据库中的情况,即可发现命名空间SpringCacheTest下key为Tony的缓存已经被成功清除

我们再向 http://localhost:8080/testSpringCache/testAllCacheEvict 发送请求,再查看此时Redis数据库中的情况,即可发现命名空间SpringCacheTest下所有key的缓存全部被清除完毕

[Note]: allEntries为true时,只能清除命名空间value下所有key的缓存,并不会清除缓存名恰好为命名空间value的缓存,如上图所示,调用testAllCacheEvict()方法后,SpringCacheTest缓存依然保留,不会被清除。欲清除该缓存,可通过redisTemplate操作

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_29955355/article/details/114204315

智能推荐

UE4攀爬系统(一)_十点半的末班车的博客-程序员宅基地_ue4攀爬

UE4攀爬系统参考教程:https://www.bilibili.com/video/BV1yb4y1o7BY?share_source=copy_webUE4攀爬系统最近突然看到一个UP主在做一个攀爬系统的教程,我觉得看完了调试了一下还阔以,就打算更新一个系列,去讲一下大概这个Up主的思路大概是什么样子的。希望能帮助到那些看视频学习只能学到操作但是不太清楚原理是什么样的UE4同好们。首先直接看人物蓝图的tick。今天就先看find climbing rotation 这个函数。看名字应该都猜的到该

Jmeter简单使用介绍_andy585846的博客-程序员宅基地_jmeter介绍与使用

=================================  1.简介Jmeter 是Apache组织开发的基于Java的压力测试工具,开源、多系统,是一款很好的HTTP测试工具。本文介绍Jmeter的基本工程配置流程,并可以使用GUI界面进行基础的测试工作。Jmeter是由Java开发的,所以在运行之前需要安装Jre。  2. 基本测试  2.1 添加线程组

(补充)常用端口大全_weixin_30567225的博客-程序员宅基地

端口大全及端口关闭方法(详细)一 、端口大全 端口:0 服务:Reserved 说明:通常用于分析操作系统。这一方法能够工作是因为在一些系统中“0”是无效端口,当你试图使用通常的闭合端口连接它时将产生不同的结果。一种典型的扫描,使用IP地址为0.0.0.0,设置ACK位并在以太网层广播。 端口:1 服务:tcpmux 说明:这显示有人在寻找SGI Irix机器。...

2017.7.24 mdns移植到407_taoluster的博客-程序员宅基地_lwip mdns

一、 (1)明确近期工作任务; 博世项目(移植库函数后以太网相关功能确认(MQTT、modbus tcp)、新增部分代码标准化及模块化、MQTT(加密及非加密)与tls modbus tcp同时运行是否存在异常、关于加密签名确认) (2) 理解: 近期主要学习了解MQTT 与Modbus tcp 相关知识,在没有TLS加密情况下先跑起来,观察相关现象。 了解 加密相关知识。 二、具体工作

Android Studio导入外部数据库(使用Sqlite expert pro创建外部数据库)_K_C_of的博客-程序员宅基地_android studio创建数据库

在各个博客网站查询了很多方法,虽然能够导入,但是表是空的,个人判断是外部数据库类型(如后缀为db,sql)的原因,改为sqlite类型的数据库就可以正常导入所有外部数据库的信息了,以下是导入的几个步骤:方法一是使用虚拟机的目录的upload方法直接导入,具体步骤去查其他博主的吧,这里主要讲使用代码来写入外部的数据库:![在这里插入图片描述](https://img-blog.csdnimg.cn/d2a9251f3d5a429798a6d246cb757cf3.png)方法二,代码导入: 1.

随便推点

Python程序运行中防止意外Ctrl+C按键致程序终止_str999_cn的博客-程序员宅基地_python 按键退出程序

Python网络爬虫往往需要连续运行很长时间。期间,运行过程中会随时输出一些状态信息,供分析查看。在此过程中,有时会选择所需信息,然后习惯性地点击Ctrl+C进行复制操作,啊哦~~~~ 不妙!对于运行中的Python来说,这也会导致发出退出运行的信号。于是我们就看到如下的画面:KeyboardInterrupt异常!怎么办?如果不想因为一个粗心大意的按键而导致程序中途退出,势必需要对退

centos7.5部署kubernetes 1.12.2记录_西丽小帅的博客-程序员宅基地

环境准备部署环境规划如下节点 IP 备注 k8s-master 10.0.0.251 master节点 k8s-node1 10.0.0.252 node节点1 k8s-node2 10.0.0.253 node节点2 部署前准备1.设置各个节点主机名称和配置域名解析注意:各个节点的主机名...

JAVA的Scanner简介_小佐编程的博客-程序员宅基地

Scannner是基于正则表达式的文本扫描器,可以从文件、输入流、字符串中解析出几本的类型值和字符串值。Scanner使用空白(空格、Tab空白、回车)作为多个输入项之间的分隔符,设置分隔符使用useDelimiter(String pattern)。用于扫描的两个方法:1、hasNextXxx():判断是否有下一个输入项,Xxx可以是Int、Long等代表基本数据类型的字符串。如果只

Spring的jdbcTemplate查询执行原生sql_weixin_30640291的博客-程序员宅基地

  在spring与hibernate整合时进行数据库检索,执行原生sql:  public AppointmentEvaluateVo searchMyfeedbackDetail(String accountId, String fbId) { String sql = "select ae.id as fbId ,ae.app_id as appI...

fastDFS和nginx环境搭建和java上传图片demo_海澜百丈冰的博客-程序员宅基地

第一步:把fastDFS都上传到linux系统。第二步:安装FastDFS之前,先安装libevent工具包。yum -y install libevent第三步:安装libfastcommonV1.0.7工具包。1、解压缩2、./make.sh3、./make.shinstall4、把/usr/lib64/libfastcommon.so文件向/usr/lib/下复制

推荐文章

热门文章

相关标签