springboot中使用redis由浅入深解析

正文

很多时候,我们会在springboot中配置redis,但是就那么几个配置就配好了,没办法知道为什么,这里就详细的讲解一下
这里假设已经成功创建了一个springboot项目。

redis连接工厂类

第一步,需要加上springboot的redis jar包

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

然后我们写一个配置类,创建了一个redis连接的工厂的spring bean。(Redis连接工厂会生成到Redis数据库服务器的连接)

@Configuration
public class RedisConfig {
  @Bean
  public RedisConnectionFactory redisCF(){
    //如果什么参数都不设置,默认连接本地6379端口
    JedisConnectionFactory factory = new JedisConnectionFactory();
    factory.setPort(6379);
    factory.setHostName("localhost");
    return factory;
  }
}

单元测试,看看这个工厂方法的使用

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class RedisTest {

  @Autowired
  RedisConnectionFactory factory;

  @Test
  public void testRedis(){
    //得到一个连接
    RedisConnection conn = factory.getConnection();
    conn.set("hello".getBytes(), "world".getBytes());
    System.out.println(new String(conn.get("hello".getBytes())));
  }

}

输出结果 :world,说明已经成功获取到连接,并且往redis获取添加数据,

template(模版)

但是我们发现每次添加的key和value都是byte数组类型(使用很麻烦),于是spring为我们带来了redis template(模版)

Spring Data Redis提供了两个模板:
  RedisTemplate
  StringRedisTemplate

首先我们先创建一个RedisTemplate模板类,类型的key是String类型,value是Object类型(如果key和value都是String类型,建议使用StringRedisTemplate)

  @Bean
  public RedisTemplate redisTemplate(RedisConnectionFactory factory){
    //创建一个模板类
    RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
    //将刚才的redis连接工厂设置到模板类中
    template.setConnectionFactory(factory);
    return template;
  }

单元测试

@Autowired
  RedisTemplate<String, Object> template;

  @Test
  public void testRedisTemplate(){
    template.opsForValue().set("key1", "value1");
    System.out.println(template.opsForValue().get("key1"));
  }

得到结果输出value1,是不是很方便了呢。

如果是操作集合呢,也很方便的哈。

  @Test
  public void testRedisTemplateList(){

    Pruduct prud = new Pruduct(1, "洗发水", "100ml");
    Pruduct prud2 = new Pruduct(2, "洗面奶", "200ml");
    //依次从尾部添加元素
    template.opsForList().rightPush("pruduct", prud);
    template.opsForList().rightPush("pruduct", prud);
    //查询索引0到商品总数-1索引(也就是查出所有的商品)
    List<Object> prodList = template.opsForList().range("pruduct", 0,template.opsForList().size("pruduct")-1);
    for(Object obj:prodList){
      System.out.println((Pruduct)obj);
    }
    System.out.println("产品数量:"+template.opsForList().size("pruduct"));

  }

key和value序列化

当保存一条数据的时候,key和value都要被序列化成json数据,取出来的时候被序列化成对象,key和value都会使用序列化器进行序列化,spring data redis提供多个序列化器

GenericToStringSerializer:使用Spring转换服务进行序列化;
JacksonJsonRedisSerializer:使用Jackson 1,将对象序列化为JSON;
Jackson2JsonRedisSerializer:使用Jackson 2,将对象序列化为JSON;
JdkSerializationRedisSerializer:使用Java序列化;
OxmSerializer:使用Spring O/X映射的编排器和解排器(marshaler和unmarshaler)实现序列化,用于XML序列化;
StringRedisSerializer:序列化String类型的key和value。

RedisTemplate会默认使用JdkSerializationRedisSerializer,这意味着key和value都会通过Java进行序列化。StringRedisTemplate默认会使用StringRedisSerializer

例如,假设当使用RedisTemplate的时候,我们希望将Product类型的value序列化为JSON,而key是String类型。RedisTemplate的setKeySerializer()和setValueSerializer()方法就需要如下所示:

@Bean
  public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
    // 创建一个模板类
    RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
    // 将刚才的redis连接工厂设置到模板类中
    template.setConnectionFactory(factory);
    // 设置key的序列化器
    template.setKeySerializer(new StringRedisSerializer());
    // 设置value的序列化器
    //使用Jackson 2,将对象序列化为JSON
    Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
    //json转对象类,不设置默认的会将json转成hashmap
    ObjectMapper om = new ObjectMapper();
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    jackson2JsonRedisSerializer.setObjectMapper(om);
    template.setValueSerializer(jackson2JsonRedisSerializer);

    return template;
  }

到这里,大家肯定会对springboot使用redis有了简单的了解。

springboot缓存某个方法

申明缓存管理器

在某些时候,我们可能有这样的需求,用户登录的时候,我们会从数据库中读取用户所有的权限,部门等信息。而且每次刷新页面都需要判断该用户有没有这个权限,如果不停的从数据库中读并且计算,是非常耗性能的,所以我们这个时候就要用到了springboot为我们带来的缓存管理器

首先在我们的RedisConfig这个类上加上@EnableCaching这个注解。

这个注解会被spring发现,并且会创建一个切面(aspect) 并触发Spring缓存注解的切点(pointcut) 。 根据所使用的注解以及缓存的状态, 这个切面会从缓存中获取数据, 将数据添加到缓存之中或者从缓存中移除某个值。

接下来我们需要申明一个缓存管理器的bean,这个作用就是@EnableCaching这个切面在新增缓存或者删除缓存的时候会调用这个缓存管理器的方法

/**
   * 申明缓存管理器,会创建一个切面(aspect)并触发Spring缓存注解的切点(pointcut)
   * 根据类或者方法所使用的注解以及缓存的状态,这个切面会从缓存中获取数据,将数据添加到缓存之中或者从缓存中移除某个值

   * @return
   */
  @Bean
  public RedisCacheManager cacheManager(RedisTemplate redisTemplate) {
    return new RedisCacheManager(redisTemplate);
  }

当然,缓存管理器除了RedisCacheManager还有一些其他的。例如

  • SimpleCacheManager
  • NoOpCacheManager
  • ConcurrentMapCacheManager
  • CompositeCacheManager
  • EhCacheCacheManager

ConcurrentMapCacheManager,这个简单的缓存管理器使用java.util.concurrent.ConcurrentHashMap作为其缓存存储。它非常简单,因此对于开发、测试或基础的应用来讲,这是一个很不错的选择.

添加缓存

接下来我们在controller层的方法内加上注解,然后启动我们的项目。

@RequestMapping("/getPrud")
  @Cacheable("prudCache")
  public Pruduct getPrud(@RequestParam(required=true)String id){
    System.out.println("如果第二次没有走到这里说明缓存被添加了");
    return pruductDao.getPrud(Integer.parseInt(id));
  }

发现打印的这段话只被打印一次,说明在走到这个方法的时候触发了一个切面,并且查找返回缓存中的数据。

当然@Cacheable注解也可以放到这个dao层的方法里面,但是这里会报一个错,Integer无法转成String,因为我们dao层方法的参数类型是int,而RedisTemplate的key类型是String,这里是要注意的。

打开redis的客户端发现redis对应的key就是我们的参数1,这个时候就会出问题,比如说我在其他要缓存的方法的参数也是1,就会重复。后面我们会将自定义这个key的值。

除了@Cacheable添加缓存外,springboot还为我们带了了其他几个注解

删除缓存

在delete的时候用@CacheEvict清楚这条缓存。

  @RequestMapping("/deletePrud")
  @CacheEvict("pruddeleteCache")
  public String deletePrud(@RequestParam(required=true)String id){
    return "SUCCESS";
  }

@CachePut将这个方法的返回值放到缓存,如果我们放一个Pruduct对象,他会将这个对象作为key,这显然不是我们想要的。这个时候就需要自定义我们的key。

自定义key

@Cacheable和@CachePut都有一个名为key属性,这个属性能够替换默认的key,它是通过一个表达式(Spel表达式,spring提供的,很简单)计算得到的。

例如下面的就是将返回对象的id当作key来存储(但是Pruduct的id是int类型,所以需要将数字转化成String类型)

  @RequestMapping("/savePrud")
  @CachePut(value="prudsaveCache",key="#result.id +''")
  public Pruduct savePrud(Pruduct prud){
    return prud;
  }

另外除了#result是代表函数的返回值,spring还为我们带来了其他的一些元数据

 条件化缓存

通过为方法添加Spring的缓存注解,Spring就会围绕着这个方法创建一个缓存切面。但是,在有些场景下我们可能希望将缓存功能关闭。

@Cacheable和@CachePut提供了两个属性用以实现条件化缓存:unless和condition,这两个属性都接受一个SpEL表达式。如果unless属性的SpEL表达式计算结
果为true,那么缓存方法返回的数据就不会放到缓存中。与之类似,如果condition属性的SpEL表达式计算结果为false,那么对于这个方法缓存就会被禁用掉

表面上来看,unless和condition属性做的是相同的事情。但是,这里有一点细微的差别。

unless属性只能阻止将对象放进缓存,但是在这个方法调用的时候,依然会去缓存中进行查找,如果找到了匹配的值,就会返回找到的值。

与之不同,如果condition的表达式计算结果为false,那么在这个方法调用的过程中,缓存是被禁用的。就是说,不会去缓存进行查找,同时返回值也不会放进缓存中。

  @RequestMapping("/getPrud2")
  @CachePut(value ="prudCache",unless="#result.desc.contains('nocache')")
  public Pruduct getPrud2(@RequestParam(required=true)String id){
    System.out.println("如果走到这里说明,说明缓存没有生效!");
    Pruduct p = new Pruduct(Integer.parseInt(id), "name_nocache"+id, "nocache");
    return p;
  }

上面的代码中,如果返回的对象desc中包含nocache字符串,则不进行缓存。

总结demo:

将类名方法名和pruduct的id作为key

@RequestMapping("/getPrud3")
  @Cacheable(value ="prudCache",key="#root.targetClass.getName() + #root.methodName + #id")
  public Pruduct getPrud3(@RequestParam(required=true)String id){
    System.out.println("如果第二次没有走到这里说明缓存被添加了");
    return pruductDao.getPrud(Integer.parseInt(id));
  }

最后注意

#result 方法返回值不能用在@Cacheable上,只能用在@CachePut

springboot配置升级简单化

当然上面的配置只是为了了解原理的哈,实际上我们使用会更简单点。我们重写了RedisConfig

@Configuration
@EnableCaching//开启缓存
public class RedisConfig extends CachingConfigurerSupport {

  @Bean
  public KeyGenerator keyGenerator() {
    return new KeyGenerator() {
      @Override
      public Object generate(Object target, Method method, Object... params) {
        StringBuilder sb = new StringBuilder();
        sb.append(target.getClass().getName());
        sb.append(method.getName());
        for (Object obj : params) {
          sb.append(obj.toString());
        }
        return sb.toString();
      }
    };
  }
  /**
   * 申明缓存管理器,会创建一个切面(aspect)并触发Spring缓存注解的切点(pointcut)
   * 根据类或者方法所使用的注解以及缓存的状态,这个切面会从缓存中获取数据,将数据添加到缓存之中或者从缓存中移除某个值

   * @return
   */
  @Bean
  public RedisCacheManager cacheManager(RedisTemplate redisTemplate) {
    return new RedisCacheManager(redisTemplate);
  }

  @Bean
  @Primary
  public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
    // 创建一个模板类
    RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
    // 将刚才的redis连接工厂设置到模板类中
    template.setConnectionFactory(factory);
    // 设置key的序列化器
    template.setKeySerializer(new StringRedisSerializer());
    // 设置value的序列化器
    //使用Jackson 2,将对象序列化为JSON
    Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
    //json转对象类,不设置默认的会将json转成hashmap
    ObjectMapper om = new ObjectMapper();
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    jackson2JsonRedisSerializer.setObjectMapper(om);
    template.setValueSerializer(jackson2JsonRedisSerializer);

    return template;
  }

}

然后在resources下的application.properties下配置

# 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

大家发现我们并没有注册RedisConnectionFactory,那是因为spring默认帮我们读取application.properties文件并且注册了一个factorybean

keyGenerator方法帮我们注册了一个key的生成规则,就不用我们写spel表达式了,根据反射的原理读取类名+方法名+参数。但是我们有时候还是需要结合spel的。

然后在controller上加上@Cacheable("cachename"),之后就可以在redis看到保存了并且key的值是keyGenerator生成的名字

本文代码github地址

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • springboot整合redis进行数据操作(推荐)

    redis是一种常见的nosql,日常开发中,我们使用它的频率比较高,因为它的多种数据接口,很多场景中我们都可以用到,并且redis对分布式这块做的非常好. springboot整合redis比较简单,并且使用redistemplate可以让我们更加方便的对数据进行操作. 1.添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starte

  • SpringBoot利用redis集成消息队列的方法

    一.pom文件依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 二.创建消息接收者 变量.方法及构造函数进行标注,完成自动装配的工作. 通过 @Autowired的使用来消除 set ,get方法. @Autowired pub

  • 详解springboot中redis的使用和分布式session共享问题

    对于分布式使用Nginx+Tomcat实现负载均衡,最常用的均衡算法有IP_Hash.轮训.根据权重.随机等.不管对于哪一种负载均衡算法,由于Nginx对不同的请求分发到某一个Tomcat,Tomcat在运行的时候分别是不同的容器里,因此会出现session不同步或者丢失的问题. 实际上实现Session共享的方案很多,其中一种常用的就是使用Tomcat.Jetty等服务器提供的Session共享功能,将Session的内容统一存储在一个数据库(如MySQL)或缓存(如Redis)中. 本文旨在

  • SpringBoot远程访问redis服务器问题剖析

    使用了SpringBoot的项目,在远程连接Redis服务器时,会遇倒一些小问题,这里剖析一下. 1.首先,要在pom文件中添加redis依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> </dependency> 2.接着要在项目的配置文件中添加对redis服务

  • 详解SpringBoot集成Redis来实现缓存技术方案

    概述 在我们的日常项目开发过程中缓存是无处不在的,因为它可以极大的提高系统的访问速度,关于缓存的框架也种类繁多,今天主要介绍的是使用现在非常流行的NoSQL数据库(Redis)来实现我们的缓存需求. Redis简介 Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件,Redis 的优势包括它的速度.支持丰富的数据类型.操作原子性,以及它的通用性. 案例整合 本案例是在之前一篇SpringBoot + Mybatis + RESTful的基础上来集

  • 详解springboot配置多个redis连接

    一.springboot nosql 简介 Spring Data提供其他项目,用来帮你使用各种各样的NoSQL技术,包括MongoDB, Neo4J, Elasticsearch, Solr, Redis,Gemfire, Couchbase和Cassandra.Spring Boot为Redis, MongoDB, Elasticsearch, Solr和Gemfire提供自动配置.你可以充分利用其他项目,但你需要自己配置它们. 1.1.Redis Redis是一个缓存,消息中间件及具有丰富

  • springboot整合spring-data-redis遇到的坑

    描述 使用springboot整合redis,使用默认的序列化配置,然后使用redis-client去查询时查询不到相应的key. 使用工具发现,key的前面多了\xAC\xED\x00\x05t\x00!这样一个串. 而且value也是不能直观可见的. 问题所在 使用springdataredis,默认情况下是使用org.springframework.data.redis.serializer.JdkSerializationRedisSerializer这个类来做序列化. org.spri

  • springboot中使用redis由浅入深解析

    正文 很多时候,我们会在springboot中配置redis,但是就那么几个配置就配好了,没办法知道为什么,这里就详细的讲解一下 这里假设已经成功创建了一个springboot项目. redis连接工厂类 第一步,需要加上springboot的redis jar包 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redi

  • springboot中使用redis的方法代码详解

    特别说明: 本文针对的是新版 spring boot 2.1.3,其 spring data 依赖为 spring-boot-starter-data-redis,且其默认连接池为 lettuce ​redis 作为一个高性能的内存数据库,如果不会用就太落伍了,之前在 node.js 中用过 redis,本篇记录如何将 redis 集成到 spring boot 中.提供 redis 操作类,和注解使用 redis 两种方式.主要内容如下: •docker 安装 redis •springboo

  • 在SpringBoot中添加Redis及配置方法

    在实际的开发中,会有这样的场景.有一个微服务需要提供一个查询的服务,但是需要查询的数据库表的数据量十分庞大,查询所需要的时间很长. 此时就可以考虑在项目中加入缓存. 引入依赖 在maven项目中引入如下依赖.并且需要在本地安装redis. <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifac

  • SpringBoot中使用redis做分布式锁的方法

    一.模拟问题 最近在公司遇到一个问题,挂号系统是做的集群,比如启动了两个相同的服务,病人挂号的时候可能会出现同号的情况,比如两个病人挂出来的号都是上午2号.这就出现了问题,由于是集群部署的,所以单纯在代码中的方法中加锁是不能解决这种情况的.下面我将模拟这种情况,用redis做分布式锁来解决这个问题. 1.新建挂号明细表 2.在idea上新建项目 下图是创建好的项目结构,上面那个parent项目是其他项目不用管它,和新建的没有关系 3.开始创建controller,service,dao(mapp

  • SpringBoot中使用Redis的完整实例

    一.在SpringBoot中使用Redis的一套军体拳 1.导包 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>2.2.0.RELEASE</version> </dependency> 2.导入工具类 packag

  • java、spring、springboot中整合Redis的详细讲解

    java整合Redis 1.引入依赖或者导入jar包 <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> 2.代码实现 public class JedisTest { public static void main(String[]

  • SpringBoot中使用Redis作为全局锁示例过程

    目录 一.模拟没有锁情况下的资源竞争 二.使用redis加锁 微服务的项目中,一个服务我们启动多份,在不同的进程中.这些服务是无状态的,而由数据存储容器(mysql/redis/es)进行状态数据的持久化.这就会导致资源竞争,出现多线程的问题. 一.模拟没有锁情况下的资源竞争 public class CommonConsumerService { //库存个数 static int goodsCount = 900; //卖出个数 static int saleCount = 0; publi

  • springboot中使用redis并且执行调试lua脚本

    目录 原因: 1.创建一个基本的web项目 2.配置redis 3.测试redis 的lua脚本 4.技术点 5.调试方式 1.进入服务关闭关闭正在运行的服务器 2.从命令行启动redis 3.在lua脚本中增加打印 4.运行代码 6.总结 今天有个项目需要使用redis,并且有使用脚本的需求.但是因为之前没有写过,所以还有一点点不熟悉,今天记录一下. 原因: 原子操作,redis会将整个脚本作为一个整体执行,中间不会被其他命令插入. 1.创建一个基本的web项目 文件 ->新建 -> 项目,

  • 使用SpringBoot中整合Redis

    目录 SpringBoot中整合Redis SpringBoot整合Redis改不了database问题 SpringBoot中整合Redis 本次,我们以IDEA + SpringBoot作为 Java中整合Redis的使用 的测试环境,如果对创建SpringBoot项目有不清楚的地方,可以参考这篇文章:使用Idea创建我的第一个SpringBoot项目 首先,我们需要导入Redis的maven依赖 <!-- Redis的maven依赖包 -->         <dependency

  • SpringBoot中使用Redis Stream实现消息监听示例

    目录 Demo环境 仓库地址 POM依赖 配置监听消息类 监听俩个stream的实现 [问题补充]确认完消息删除消息 [问题补充]自动初始化stream的key和group问题-最新更新-2021年12月4日 Demo环境 JDK8 Maven3.6.3 springboot2.4.3 redis_version:6.2.1 仓库地址 https://gitee.com/hlovez/redismq.git. POM依赖 <?xml version="1.0" encoding=

随机推荐