Spring Boot(八)集成Spring Cache 和 Redis

时间:2023-01-18 13:00:28

在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实现代码:

Spring Boot(八)集成Spring Cache 和 Redis

因此我们只要在使用的地方注入即可:

@Autowired
StringRedisTemplate stringRedisTemplate; //操作 k-v 字符串
@Autowired
RedisTemplate redisTemplate; //k- v 都是对象

StringRedisTemplate 提供opsForValue用来操作key-value,如上面的示例,另外还提供了一系列opsForHash()、opsForList()、opsForSet()、opsForZSet()等方法用来操作不同结构类型的数据。

运行上面的测试方法,查看redis客户端:

Spring Boot(八)集成Spring Cache 和 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的更多相关文章

  1. Spring Boot中集成Spring Security 专题

    check to see if spring security is applied that the appropriate resources are permitted: @Configurat ...

  2. spring-boot-starter-security Spring Boot中集成Spring Security

    spring security是springboot支持的权限控制系统. security.basic.authorize-mode 要使用权限控制模式. security.basic.enabled ...

  3. Spring Boot 中集成 Redis 作为数据缓存

    只添加注解:@Cacheable,不配置key时,redis 中默认存的 key 是:users::SimpleKey [](1.redis-cli 中,通过命令:keys * 查看:2.key:缓存 ...

  4. Spring Boot&lpar;八&rpar;:RabbitMQ详解

    Spring Boot(八):RabbitMQ详解 RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用. 消息中间件在互联网公司的使用中越来越多 ...

  5. Spring Boot快速集成kaptcha生成验证码

    Kaptcha是一个非常实用的验证码生成工具,可以通过配置生成多样化的验证码,以图片的形式显示,从而无法进行复制粘贴:下面将详细介绍下Spring Boot快速集成kaptcha生成验证码的过程. 本 ...

  6. Spring Boot日志集成实战

    Spring Boot日志框架 Spring Boot支持Java Util Logging,Log4j2,Lockback作为日志框架,如果你使用starters启动器,Spring Boot将使用 ...

  7. Spring Boot日志集成

    Spring Boot日志框架 Spring Boot支持Java Util Logging,Log4j2,Lockback作为日志框架,如果你使用starters启动器,Spring Boot将使用 ...

  8. Spring Boot:集成Druid数据源

    综合概述 数据库连接池负责分配.管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个:释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据 ...

  9. shiro 和 spring boot 的集成

    1 添加依赖 使用 shiro-spring-boot-web-starter 在 spring boot 中集成 shiro 只需要再添加一个依赖 <dependency> <gr ...

  10. &lbrack;转帖&rsqb;spring boot项目集成jacoco

    小试牛刀:spring boot项目集成jacoco 2019-03-28 20:14:36 zyq23333 阅读数 509   版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议, ...

随机推荐

  1. Ubuntu 14&period;04开发环境初始化

    安装fcitx, fcitx-googlepinyin, 移除默认键盘快捷键. 英文版不要安装系统推荐的语言更新,会使浏览器以及其他的应用的字体变成bitmap. 安装nvidia驱动 安装vim,设 ...

  2. statspack系列7

    原文:http://jonathanlewis.wordpress.com/2006/12/27/analysing-statspack-7/ 作者:Jonathan Lewis 这是一段Oracle ...

  3. linux 下进程状态及进程控制

    系统状态检测及进程控制1,/proc 是系统的一个窗户,可以透视内核2,建议将hosts里localhost,locahost.locadomain 解析为127.0.0.1 把系统域名解决为局域网的 ...

  4. mybatis实战教程三:多对多关联

    MyBatis3.0 添加了association和collection标签专门用于对多个相关实体类数据进行级联查询,但仍不支持多个相关实体类数据的级联保存和级联删除操作 一.创建student.te ...

  5. CSRF重放共计详解

    CSRF 背景与介绍 CSRF(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安全隐患之一.其他安全隐患,比如 ...

  6. windows phone 基础

    一.安装 建议安装Windows 7环境,XP中不能运行模拟器,Vista系统支持,但不解释.系统安装完后,直接去微软网站在线安装即可,非常方便,美中不足的是如果你的网速不快,那可能要折磨你半天,快得 ...

  7. C&plus;&plus; 语言中的重载、内联、缺省参数、隐式转换等机制展现了很多优点

    C++ 语言中的重载.内联.缺省参数.隐式转换等机制展现了很多优点,但是这些 优点的背后都隐藏着一些隐患.正如人们的饮食,少食和暴食都不可取,应当恰到好处. 我们要辨证地看待 C++的新机制,应该恰如 ...

  8. MySQL模糊查询&lpar;like&rpar;时区分大小写

    问题说明:通过上面的语句,你会发现MySQL的like查询是不区分大小写的,因为我的失误,把Joe写成了joe才发现了这个东东吧.但是,有时候,我们需要区分大小写的是,该怎么办呢?解决方法如下: 方法 ...

  9. mvc中使用uploadify批量上传的应用

    网上找了很多资料都没有发现一个好用.可以用的uploadify批量上传的应用,这里通过官方和自己的一些项目需要整理了一个出来. 希望能帮助到需要的人. 效果图:

  10. maven学习(三)maven仓库

    官网图片: maven仓库: 仓库通常配置在settings.xml文件中,此处注意settings.xml的优先级:用户 > 全局,广义上maven的仓库一共包含两种: 1.本地仓库,默认在& ...