Spring Boot(八)集成Spring Cache 和 Redis
在Spring Boot中添加spring-boot-starter-data-redis依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
在application.properties中指定redis服务器IP、端口和密码、连接数等:
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口 使用默认端口6379可以省略配置
#spring.redis.port=6379
# Redis服务器连接密码(默认为空)
#spring.redis.password=
# 连接池最大连接数(如果配置<=0,则没有限制 )
spring.redis.jedis.pool.max-active=8
使用StringRedisTemplate 和RedisTemplate
StringRedisTemplate是Spring Boot内置的操作Redis的API实现类,另外还有一个API实现类是RedisTemplate。StringRedisTemplate的API假定所有的数据类型化都是字符类型,即key和value都是字符串类型,对于常见额SpringBoot应用系统,使用字符串操作也已经足够了,而且能方便的通过客户端管理工具管理。StringRedisTemplate继承RedisTemplate,与RedisTemplate不同的是重新设置了序列化策略,使用StringRedisSerialier类来序列化key-value,包括List、Hash、Set等数据结构。
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootCacheApplicationTests { @Autowired
StringRedisTemplate stringRedisTemplate; //操作 k-v 字符串 @Autowired
RedisTemplate redisTemplate; //k- v 都是对象 /**
* redis 常见
* String(字符串) List(列表) Set(集合) Hash(散列) ZSet(有序集合)
*/ @Test
public void test1() {
stringRedisTemplate.opsForValue().append("StringKey", "字符串数值");
String value = stringRedisTemplate.opsForValue().get("StringKey");
System.out.println(value);
}
@Test
public void test2() { Product product = productMapper.getProductById(4L);
redisTemplate.opsForValue().set("produxtid4",product); }
}
spring-boot-autoconfigure-2.0.4.RELEASE.jar包中RedisAutoConfiguration.java已经自动声明了两个redis操作bean:
RedisAutoConfiguration.java实现代码:

因此我们只要在使用的地方注入即可:
@Autowired
StringRedisTemplate stringRedisTemplate; //操作 k-v 字符串
@Autowired
RedisTemplate redisTemplate; //k- v 都是对象
StringRedisTemplate 提供opsForValue用来操作key-value,如上面的示例,另外还提供了一系列opsForHash()、opsForList()、opsForSet()、opsForZSet()等方法用来操作不同结构类型的数据。
运行上面的测试方法,查看redis客户端:

可以发现,出现了一些无法识别的字符,查看RedisTemplate源码可这个是由于默认使用了JDK的序列化机制,而StringRedisTemplate没有出乱码是因为它修改了序列化器
StringRedisTemplate实现:
public class StringRedisTemplate extends RedisTemplate<String, String> {
/**
* Constructs a new <code>StringRedisTemplate</code> instance. {@link #setConnectionFactory(RedisConnectionFactory)}
* and {@link #afterPropertiesSet()} still need to be called.
*/
public StringRedisTemplate() {
RedisSerializer<String> stringSerializer = new StringRedisSerializer();
setKeySerializer(stringSerializer);
setValueSerializer(stringSerializer);
setHashKeySerializer(stringSerializer);
setHashValueSerializer(stringSerializer);
}
/**
* Constructs a new <code>StringRedisTemplate</code> instance ready to be used.
*
* @param connectionFactory connection factory for creating new connections
*/
public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
this();
setConnectionFactory(connectionFactory);
afterPropertiesSet();
}
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
return new DefaultStringRedisConnection(connection);
}
}
RedisTemplate实现,截取默认序列化器相关源码:
@Override
public void afterPropertiesSet() { super.afterPropertiesSet(); boolean defaultUsed = false; if (defaultSerializer == null) {
//默认序列化器使用JdkSerializationRedisSerializer
defaultSerializer = new JdkSerializationRedisSerializer(
classLoader != null ? classLoader : this.getClass().getClassLoader());
} if (enableDefaultSerializer) { if (keySerializer == null) {
keySerializer = defaultSerializer;
defaultUsed = true;
}
if (valueSerializer == null) {
valueSerializer = defaultSerializer;
defaultUsed = true;
}
if (hashKeySerializer == null) {
hashKeySerializer = defaultSerializer;
defaultUsed = true;
}
if (hashValueSerializer == null) {
hashValueSerializer = defaultSerializer;
defaultUsed = true;
}
} if (enableDefaultSerializer && defaultUsed) {
Assert.notNull(defaultSerializer, "default serializer null and not all serializers initialized");
} if (scriptExecutor == null) {
this.scriptExecutor = new DefaultScriptExecutor<>(this);
} initialized = true;
}
既然RedisTemplate的默认序列化器不是很方便在redis管理工具中查看,我们可以自己定义一个RedisTemplate实例,修改默认的序列化器。
实现方式如下,定义一个配置类,重新注入一个RedisTemplate操作bean:
@Configuration
public class MyRedisConfig { @Bean(name = "redisTemplate")
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){ RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
//参照StringRedisTemplate内部实现指定序列化器
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(keySerializer());
redisTemplate.setHashKeySerializer(keySerializer());
redisTemplate.setValueSerializer(valueSerializer());
redisTemplate.setHashValueSerializer(valueSerializer()); return redisTemplate;
} private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
}
//使用Jackson序列化器
private RedisSerializer<Object> valueSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
}
重新运行上面的测试代码,可以发现redis客户端中已经可以正常的显示json格式数据了。
SpringBoot集成redis + spring cache
Spring Cache集成redis的运行原理:
Spring缓存抽象模块通过CacheManager来创建、管理实际缓存组件,当SpringBoot应用程序引入spring-boot-starter-data-redi依赖后吗,容器中将注册的是CacheManager实例RedisCacheManager对象,RedisCacheManager来负责创建RedisCache作为缓存管理组件,由RedisCache操作redis服务器实现缓存数据操作。实际测试发现默认注入的RedisCacheManager操作缓存用的是RedisTemplate<Object, Object>,因此我们需要自定义cacheManager,替换掉默认的序列化器。
实现代码:
添加mybatis和redis依赖:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
添加mapper映射:
@Mapper
public interface ProductMapper {
@Select("select * from tb_product where product_id=#{id}")
Product getProductById(Long id); @Update("update tb_product set product_name=#{productName},product_desc=#{productDesc} WHERE product_id=#{productId}")
int updateProduct(Product product); @Delete("delete from tb_product where product_id=#{id}")
void deleteProductById(Long id); @Select("select * from tb_product where product_name=#{productName}")
Product getProductByName(String productName);
}
Service:
package com.sl.cache.service;
import com.sl.cache.entity.Product;
import com.sl.cache.mapper.ProductMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service; @Service
@CacheConfig(cacheNames = "product")
public class ProductService {
@Autowired
private ProductMapper productMapper; @Cacheable(cacheNames = "product1",key = "#root.methodName+'['+#id+']'")
//@Cacheable(cacheNames = {"product1","product2"})// 默认key为参数,多个参数SimpleKey [arg1,arg2]
//@Cacheable(cacheNames = "product",key = "#root.methodName+'['+#id+']'")
//@Cacheable(cacheNames = "product",keyGenerator = "myKeyGenerator")
//@Cacheable(cacheNames = "product",key = "#root.methodName+'['+#id+']'",condition="#a0>10",unless = "#a0==11") //或者condition="#id>10")
public Product getProductById(Long id){
Product product =productMapper.getProductById(id);
System.out.println(product);
return product;
} @CachePut(value="product",key = "#result.productId",condition = "#result!=null")
public Product updateProduct(Product product){
int count = productMapper.updateProduct(product);
System.out.println("影响行数:"+count);
if(count>0){
return product;
}else{
return null;
}
} //@CacheEvict(value="product",key="#id")
//@CacheEvict(value="product",allEntries = true) //清楚所有缓存
@CacheEvict(value="product",allEntries = true,beforeInvocation = true) //清楚所有缓存
public boolean deleteProductById(Long id) {
productMapper.deleteProductById(id);
return true;
} //含有CachePut注解,所以执行这个方法时一定会查询数据库,及时有cacheable注解
@Caching(
cacheable = {@Cacheable(value="product",key="#productName")},
put = {
@CachePut(value="product",key="#result.productId"),
@CachePut(value="product",key="#result.productName")
}
)
public Product getProductByName(String productName){ Product product =productMapper.getProductByName(productName); return product;
}
}
Controller:
package com.sl.cache.controller;
import com.sl.cache.entity.Product;
import com.sl.cache.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; @RestController
public class ProductController { @Autowired
private ProductService productService; @GetMapping("/product/{id}")
public Product getProduct(@PathVariable("id") Long id) { Product product = productService.getProductById(id);
return product;
} //prooduct?productid=1&productName= &
@GetMapping("/product")
public Product updateProduct(Product product) {
productService.updateProduct(product);
return product;
} @GetMapping("/delproduct")
public String delProduct(@RequestParam(value="id") Long id) { productService.deleteProductById(id);
return "ok";
} @GetMapping("/product/name/{productName}")
public Product getEmpByLastName(@PathVariable("productName") String productName){
return productService.getProductByName(productName);
}
}
自定义cacheManager实现:
package com.sl.cache.config;
import com.sl.cache.entity.Product;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.cache.config.CacheManagementConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer; import java.net.UnknownHostException;
import java.time.Duration; @Configuration
public class MyRedisConfig { @Bean(name = "redisTemplate")
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){ RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(keySerializer());
redisTemplate.setHashKeySerializer(keySerializer());
redisTemplate.setValueSerializer(valueSerializer());
redisTemplate.setHashValueSerializer(valueSerializer());
return redisTemplate;
} @Primary
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory){
//缓存配置对象
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig(); redisCacheConfiguration = redisCacheConfiguration.entryTtl(Duration.ofMinutes(30L)) //设置缓存的默认超时时间:30分钟
.disableCachingNullValues() //如果是空值,不缓存
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer())) //设置key序列化器
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer((valueSerializer()))); //设置value序列化器 return RedisCacheManager
.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
.cacheDefaults(redisCacheConfiguration).build();
}
private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
} private RedisSerializer<Object> valueSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
}
启用缓存,添加mybatis Mapper映射扫描:
@MapperScan("com.sl.cache.mapper")
@SpringBootApplication
@EnableCaching
public class SpringbootCacheApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootCacheApplication.class, args);
}
}
Spring Boot(八)集成Spring Cache 和 Redis的更多相关文章
- Spring Boot中集成Spring Security 专题
check to see if spring security is applied that the appropriate resources are permitted: @Configurat ...
- spring-boot-starter-security Spring Boot中集成Spring Security
spring security是springboot支持的权限控制系统. security.basic.authorize-mode 要使用权限控制模式. security.basic.enabled ...
- Spring Boot 中集成 Redis 作为数据缓存
只添加注解:@Cacheable,不配置key时,redis 中默认存的 key 是:users::SimpleKey [](1.redis-cli 中,通过命令:keys * 查看:2.key:缓存 ...
- Spring Boot(八):RabbitMQ详解
Spring Boot(八):RabbitMQ详解 RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用. 消息中间件在互联网公司的使用中越来越多 ...
- Spring Boot快速集成kaptcha生成验证码
Kaptcha是一个非常实用的验证码生成工具,可以通过配置生成多样化的验证码,以图片的形式显示,从而无法进行复制粘贴:下面将详细介绍下Spring Boot快速集成kaptcha生成验证码的过程. 本 ...
- Spring Boot日志集成实战
Spring Boot日志框架 Spring Boot支持Java Util Logging,Log4j2,Lockback作为日志框架,如果你使用starters启动器,Spring Boot将使用 ...
- Spring Boot日志集成
Spring Boot日志框架 Spring Boot支持Java Util Logging,Log4j2,Lockback作为日志框架,如果你使用starters启动器,Spring Boot将使用 ...
- Spring Boot:集成Druid数据源
综合概述 数据库连接池负责分配.管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个:释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据 ...
- shiro 和 spring boot 的集成
1 添加依赖 使用 shiro-spring-boot-web-starter 在 spring boot 中集成 shiro 只需要再添加一个依赖 <dependency> <gr ...
- [转帖]spring boot项目集成jacoco
小试牛刀:spring boot项目集成jacoco 2019-03-28 20:14:36 zyq23333 阅读数 509 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议, ...
随机推荐
- 【七】ab压测
[任务7]ab压测 安装ab压测软件 命令:yum -y install httpd-tools 进行压力测试: 执行命令:ab -c 20 -n 5000 http://192.168.159.30 ...
- Hadoop生态新增列式存储系统Kudu
Hadoop生态系统发展到现在,存储层主要由HDFS和HBase两个系统把持着,一直没有太大突破.在追求高吞吐的批处理场景下,我们选用HDFS,在追求低延迟,有随机读写需求的场景下,我们选用H ...
- STM32 HAL库学习系列第2篇 GPIO配置
GPIO 库函数 基本就是使用以下几个函数 GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); void H ...
- Python学习:13.Python正则表达式
一.正则表达式简介 正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配. Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式. 就其本质 ...
- 《C语言程序设计基础1》第二学期第一周学习总结
**<C语言程序设计基础1>第二学期第一周学习总结 一. 本周学习内容总结 一维数组,了解了一维数组的定义(定义一个数组,需要明确数组变量名,数组元素的类型和数组大小,即数组中元素的数量) ...
- JAVA乐观锁实现-CAS
是什么 全称compare and swap,一个CPU原子指令,在硬件层面实现的机制,体现了乐观锁的思想. JVM用C语言封装了汇编调用.Java的基础库中有很多类就是基于JNI调用C接口实现了多线 ...
- 4.26-python学习笔记(变量及命名、字符串、格式化字符串print,format,%...)
参考书目:<Learn Python The Hard Way> cars=100 print('there are ',cars,'cars available.') ##练习5 更多变 ...
- 20155332 2016-2017-2《Java程序设计》第1周学习总结
# 20155332 2016-2017-2<Java程序设计>第1周学习总结 教材学习内容总结 课本提问 1.Java平台概论:为什么需要Java程序设计语言? 2.Java平台概论:版 ...
- OpenCV参考手册之Mat类详解
OpenCV参考手册之Mat类详解(一) OpenCV参考手册之Mat类详解(二) OpenCV参考手册之Mat类详解(三)
- Zabbix学习之路(八)之自动化监控网络发现和主动注册
1.网络发现 分两步走:创建发现规则(rule)和执行的动作(Action)(1)创建发现规则"Configuration"-->"Create discover ...