写点什么

8 分钟带你学会 SpringBoot 整合 Redis 来实现缓存技术

  • 2022-11-15
    北京
  • 本文字数:13090 字

    阅读完需:约 43 分钟

8分钟带你学会SpringBoot整合Redis来实现缓存技术

1.概述

随着互联网技术的发展,对技术要求也越来越高,所以在当期情况下项目的开发中对数据访问的效率也有了很高的要求,所以在项目开发中缓存技术使用的也越来越多,因为它可以极大的提高系统的访问速度,关于缓存的框架也种类繁多,比如 Redis、Ehchahe、JBoss Cache、Voldemort、Cacheonix 等等,今天主要介绍的是使用现在非常流行的 NoSQL 数据库(Redis)来实现我们的缓存需求

2.SpringBoot 简介

Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot 致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。


主要特点:


1.创建独立的 Spring 应用程序


2.嵌入的 Tomcat,无需部署 WAR 文件


3.简化 Maven 配置


4.自动配置 Spring


5.提供生产就绪型功能,如指标,健康检查和外部配置


6.绝对没有代码生成和对 XML 没有要求配置

3.Redis 简介

Redis 是一个开源(BSD 许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件,Redis 的优势包括它的速度、支持丰富的数据类型、操作原子性,以及它的通用性。

4.下面就是 SpringBoot 整合 Redis 具体实现步骤

4.1 springBoot 整合 redis 的实现步骤


  • 步骤一: 导入相关依赖


在 Maven 的 pom.xml 文件中加入 Redis 包.配置 redis 依赖


<!—配置 redis 依赖-->
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>2.1.5.RELEASE</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.6.2</version> </dependency>//依赖解释​Spring-data-redis提供了在Spring应用中通过简单的配置访问redis服务,RedisTemplate对reids底层开发包(Jedis, JRedis, and RJC,lettuce)进行了高度封装,封装了 RedisTemplate 对象来进行对Redis的各种操作、异常处理及序列化,支持发布订阅,并对Spring 3.1 cache进行了实现,它支持所有的Redis原生的 API。
复制代码


  • 步骤二: 配置全局配置文件.配置连接 redis 的连接参数


    server:      port: 80    spring:      cache:        type: redis      redis:          # redis数据库索引(默认为0),我们使用索引为3的数据库,避免和其他数据库冲突          #database: 3          # redis服务器地址(默认为loaclhost)          host: localhost          # redis端口(默认为6379)          port: 6379          # redis访问密码(默认为空)          #password: pwd123          # redis连接超时时间(单位毫秒)          timeout: 3000          # redis连接池配置          pool:            # 最大可用连接数(默认为8,负数表示无限)            max-active: 8            # 最大空闲连接数(默认为8,负数表示无限)            max-idle: 4            # 最小空闲连接数(默认为0,该值只有为正数才有用)            min-idle: 0            # 从连接池中获取连接最大等待时间(默认为-1,单位为毫秒,负数表示无限)            max-wait: -1
复制代码


  • 步骤三: RedisTemplate 模板类对象可以配置,也可以不用配置

  • 回顾 redis 存储的数据类型,五种值的数据类型,最终以字符串类型的数据存到 redis 里面.

  • 问题: 向 redis 存储 javaBean, 集合数据等等其他数据.

  • 解决方案:

  • 可以将 javaBean, 集合数据等等其他数据转成字符串类型的数据,再存储到 redis 里面.

  • 具体实现:

  • springBoot 整合 redis 时,默认使用的 JDK 序列化类,将对象数据转成 string 类型数据.(可以不用配置 RedisTemplate 模板类)

  • 当然可以手动配置 RedisTemplate 模板类,可以指定序列化类,比如:性能好的 JacksonJsonRedisSerializer

  • 可以不用配置, SpringBoot 会扫描到该 RedisTemplate 会初始化这个 bean,存到 spring 容器中.

  • 可以配置 RedisTemplate(固定代码)


        @Configuration        public class RedisConfig {            /**             * 设置Redis序列化方式,默认使用的JDKSerializer的序列化方式,效率低,这里我们使用 jackson             *             * @param factory             * @return             */            @Bean            public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {                //创建模板类对象                RedisTemplate<String, Object> template = new RedisTemplate<>();                //创建String序列化类                RedisSerializer<String> redisSerializer = new StringRedisSerializer();                //指定使用jackson工具负责具体的序列化操作                Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);                //创建jackson的核心api的 ObjectMapper ,将bean,list,map,数组等等转成字符串                ObjectMapper om = new ObjectMapper();                //使用objectmapper设置bean的属性,修饰符                om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);                //设置默认类型                om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);                //设置将本地时间转成字符串                om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);                //将核心api objectmapper设置给jackson                jackson2JsonRedisSerializer.setObjectMapper(om);                //通过工厂得到连接对象                template.setConnectionFactory(factory);                //设置key序列化方式: 转成字符串                template.setKeySerializer(redisSerializer);                //设置value序列化: 字符串                template.setValueSerializer(jackson2JsonRedisSerializer);                //value hashmap序列化                template.setHashValueSerializer(jackson2JsonRedisSerializer);                return template;            }                      //获取缓存管理器对象:管理缓存            @Bean            public CacheManager cacheManager(RedisConnectionFactory factory) {                RedisSerializer<String> redisSerializer = new StringRedisSerializer();                Jackson2JsonRedisSerializer 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);                // 配置序列化(解决乱码的问题),过期时间600秒                RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()                        .entryTtl(Duration.ofSeconds(600))                        .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))                        .disableCachingNullValues();                RedisCacheManager cacheManager = RedisCacheManager.builder(factory)                        .cacheDefaults(config)                        .build();                return cacheManager;            }
}
复制代码


-   步骤三: 可以测试
在测试类里面,注入@RedisTemplate操作redis数据库.
复制代码


        package com.user;
import com.system.SpringApplicationConfig11; import com.system.user.domain.User; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.ListOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.StringUtils;
import java.util.List; import java.util.Set;
@RunWith(SpringRunner.class) @SpringBootTest(classes = {SpringApplicationConfig11.class}) public class DemoRedisTemplate { @Autowired private RedisTemplate<String, Object> redisTemplate; @Test// hash类型: key-value(key-value)就是map public void testX9(){ redisTemplate.boundHashOps("myhash").put("username","jack"); redisTemplate.boundHashOps("myhash").put("password","1111"); Set<Object> myhash = redisTemplate.boundHashOps("myhash").keys(); System.out.println("获取map的所有key:"); for (Object key : myhash) { System.out.print(key+","); } System.out.println(); List<Object> myhash1 = redisTemplate.boundHashOps("myhash").values(); System.out.println("获取map的所有value"); for (Object value : myhash1) { System.out.print(value+","); } System.out.println(); } @Test//sortedset类型: redisTemplate public void testX8(){ redisTemplate.boundZSetOps("myset2withscore").add("jack",10.0); redisTemplate.boundZSetOps("myset2withscore").add("rose",9.0); redisTemplate.boundZSetOps("myset2withscore").add("ship",20.0); Set<Object> myset2withscore = redisTemplate.boundZSetOps("myset2withscore").range(0, -1); for (Object o : myset2withscore) { System.out.println(o); } } @Test//set类型: redisTemplate public void testX7(){ redisTemplate.boundSetOps("myset1noscore").add("jack"); redisTemplate.boundSetOps("myset1noscore").add("jack"); redisTemplate.boundSetOps("myset1noscore").add("rose"); Set<Object> myset1noscore = redisTemplate.boundSetOps("myset1noscore").members(); for (Object o : myset1noscore) { System.out.println(myset1noscore); }
} @Test//list类型的数据: ListOperations public void testX5(){ Boolean flag = redisTemplate.hasKey("mylist");//判断缓存数据库是否有这个key System.out.println("mylist:"+flag); Boolean flag2 = redisTemplate.hasKey("username");//判断缓存数据库是否有这个key System.out.println("username:"+flag2);
} @Test//list类型的数据: RedisTemplate public void testX6(){ redisTemplate.boundListOps("mylist").leftPush("jack"); redisTemplate.boundListOps("mylist").leftPush("rose"); redisTemplate.boundListOps("mylist").rightPush("ship"); List<Object> mylist = redisTemplate.boundListOps("mylist").range(0, -1); for (Object o : mylist) { System.out.println(o); }
} @Test//list类型的数据: ListOperations public void testX4(){ redisTemplate.opsForList().leftPush("mylist","jack"); Object mylist = redisTemplate.opsForList().leftPop("mylist"); System.out.println(mylist); //ListOperations<String, Object> operation= redisTemplate.opsForList();
}
@Test//操作string public void testX(){ redisTemplate.opsForValue().set("username","jack");//底层用的就是jedis Object username = redisTemplate.opsForValue().get("username"); System.out.println(username); } @Test//操作string public void testX2(){ redisTemplate.boundValueOps("username").set("rose");//向缓存中设置数据 Object username = redisTemplate.boundValueOps("username").get(); System.out.println(username); } @Test//操作string public void testX3(){ User user2 = new User(10L,"大马猴","dmh@sina.com","666","12"); //底层: 用指定的Jackson2JsonRedisSerializer的objectMapper将user转成json,设置到缓存中 redisTemplate.boundValueOps("user").set(user2);
}

}
复制代码


RedisTemplate 核心 api:


RedisTemplate 概述:


RedisTemplate 是 Spring Boot 访问 Redis 的核心组件,底层通过 RedisConnectionFactory 对多种 Redis 驱动进行集成,上层通过 XXXOperations 提供丰富的 [API],操作 redis 数据库.


问题:


但美中不足的是,需要针对应用场景,使用不同的 RedisSerializer 对 RedisTemplate 进行定制,那有没有一种方法能够自动完成 RedisTemplate 的定义呢?


解决方案:


自己配置 RedisTemplate,手动指定序列化类.



  • 连接池自动管理,提供了一个高度封装的“RedisTemplate”类(会自动初始化到容器中,可以直接使用)。

  • RedisTemplate 默认使用的 jdk 的序列化

  • 当然也可以指定 RedisTemplate 的序列化工具

  • 针对 jedis 客户端中大量 api 进行了归类封装,将同一类型操作封装为 operation 操作接口。

  • 操作不同的数据类型,获取不同的 operations 操作对象.

  • Jedis 的 api:

  • ValueOperations:操作的 string 类型的数据, 简单 K-V 操作,底层就是 set 方法

  • SetOperations:set 类型数据操作.

  • ZSetOperations:zset 类型数据操作

  • HashOperations:针对 map 类型的数据操作

  • ListOperations:针对 list 类型的数据操作

  • 提供了对 key 的“bound”(绑定)便捷化操作 API,可以通过 bound 封装指定的 key,然后进行一系列的操作而无须“显式”的再次指定 Key,即 BoundKeyOperations 将事务操作封装,由容器控制。

  • BoundValueOperations

  • 底层封装的就是 ValueOperations:操作的 string 类型的数据, 简单 K-V 操作,底层就是 set 方法

  • BoundSetOperations

  • 底层封装的就是 SetOperations:set 类型数据操作.

  • BoundListOperations

  • 底层封装的就是 ZSetOperations:zset 类型数据操作

  • BoundSetOperations

  • 底层封装的就是 ZSetOperations:zset 类型数据操作

  • BoundHashOperations

  • 底层封装的就是 HashOperations:针对 map 类型的数据操作

  • 针对数据的“序列化/反序列化”,提供了多种可选择策略(RedisSerializer)

  • JdkSerializationRedisSerializer:POJO 对象的存取场景,使用 JDK 本身序列化机制,将 pojo 类通过 ObjectInputStream/ObjectOutputStream 进行序列化操作,最终 redis-server 中将存储字节序列。是目前最常用的序列化策略。

  • 缺点: 性能差,不能对本地日期类型进行转换.


  • StringRedisSerializer:Key 或者 value 为字符串的场景,根据指定的 charset 对数据的字节序列编码成 string,是“new String(bytes, charset)”和“string.getBytes(charset)”的直接封装。是最轻量级和高效的策略。

  • JacksonJsonRedisSerializer:jackson-json 工具提供了 javabean 与 json 之间的转换能力,可以将 pojo 实例序列化成 json 格式存储在 redis 中,也可以将 json 格式的数据转换成 pojo 实例。因为 jackson 工具在序列化和反序列化时,需要明确指定 Class 类型,因此此策略封装起来稍微复杂。【需要 jackson-mapper-asl 工具支持】

  • OxmSerializer:提供了将 javabean 与 xml 之间的转换能力,目前可用的三方支持包括 jaxb,apache-xmlbeans;redis 存储的数据将是 xml 工具。不过使用此策略,编程将会有些难度,而且效率最低;不建议使用。【需要 spring-oxm 模块的支持】


如果你的数据需要被第三方工具解析,那么数据应该使用 StringRedisSerializer 而不是 JdkSerializationRedisSerializer。


注意小结:


如果向缓存中添加 javaBean 对象时,一定要让 javaBean 实现 Serializable 接口


4.2 Service 层应用缓存之 springBoot 整合缓存


  • 为什么要 springBoot 整合缓存 Cache.

  • 通过 Cache 保存数据, 提高数据的响应效率.

  • 实现本质:

  • 第一步: 第一次需要从关系型数据库里面把数据查询查询出来, 设置到缓存中

  • 第二步: 下次再查询数据时,不在查询关系型数据库中,而是从缓存中取出数据

  • 存在问题:

  • 比如: 第一次从 mysql 中查询了 100 条数据,存到缓存中有 100 条数据

  • 比如: 下次再查询之前,删除了一条数据,mysql 剩余 99 条数据, 缓存中依然了 100 条数据

  • 解决方案:

  • 如果关系型数据库中的数据发生了变化,需要同步更新缓存数据(让关系型数据库的数据与 redis 缓存的数据同步)

  • 在企业中解决方案:

  • 解决方案一: 通过手写缓存代码,实现缓存数据的存取以及缓存数据的同步更新(代码冗余)

  • 解决方案二: 通过 springBoot 整合缓存,实现缓存数据的自动存储,以及自动同步更新.


具体的整合步骤:


  • 缓存常用的注解

  • @Cacheable: 先从缓存中查询数据,如果缓存中没有数据,这时从关系型数据库查询数据,设置到缓存中.

  • @CachePut: 先从关系型数据库中查询数据,清空缓存中的数据,把返回的数据再次设置到缓存中

  • ​ 实现缓存的同步更新.

  • 注意细节:

  • @CachePut 通常操作的添加方法和更新方法

  • @CachePut 要求缓存的 key 要保持一致.

  • 当执行添加一条数据和修改一条数据时,使用 @CachePut 注解,key 使用 id

  • 实现缓存的同步更新

  • 第一步: 添加 id=13L 的数据, 设置到缓存中.

  • 第二步: 修改 id=13L 的数据, 比如: 修改 username=jack, 缓存会自动同步更新为 jack.

  • @CachePut 和 @Cacheable 一般不建议放在一块使用.

  • @Cacheable 和 @CachePut 一块使用时,不会实现缓存的同步更新

  • 解决 @Cacheable 和 @CachePut 实现缓存的同步更新,

  • 第一步: 执行查询操作,设置 10 条数据到缓存中

  • 第二步: 执行添加一条数据操作, 设置 1 条数据到缓存中,手动更新缓存,实现缓存的同步, 最终缓存了 11 条数据

  • 解决方案:

  • 修改添加方法,将添加方法的返回值与查询方法的返回值保持一致

  • 如果不一致,会出现类型转换异常.



        1.  在添加方法中,执行完添加后,再次查询数据库,最终实现缓存的同步
复制代码



@CachePut(cacheNames = "listA" ,key="'aa'") public List<User> addUser2(User user){ //1.先添加 userMapper.insert(user); //2.再查询 List<User> users = userMapper.selectAll(); return users; }
复制代码


1.  @CacheEvict: 直接清空缓存中的数据
复制代码


  • 步骤一: 导入依赖


      <dependency>                    <groupId>org.springframework.boot</groupId>                    <artifactId>spring-boot-starter-cache</artifactId>     </dependency>
复制代码


  • 步骤二: 在引导类上面使用


    @SpringBootApplication    @MapperScan("com.dao")    @EnableCaching//开启缓存支持    public class SpringApplicationConfig {        public static void main(String[] args){            //自动扫描包, 自动装配,启动tomcat插件            SpringApplication.run(SpringApplicationConfig.class);        }    }
复制代码


  • 步骤三: 使用缓存注解


@Servicepublic class TestService {
@Autowiredprivate PersonRepo personRepo;
/**
* @Cacheable 应用到读取数据的方法上,先从缓存中读取,如果没有再从 DB 获取数据,然后把数据添加到缓存中
* unless 表示条件表达式成立的话不放入缓存
*/
@Cacheable(value = "user", key = "#root.targetClass + #username", unless = "#result eq null" , condition="") public Person getPersonByName(String username) { Person person = personRepo.getPersonByName(username); return person; }
/**

* @CachePut 应用到写数据的方法上,如新增方法,调用方法时会自动把相应的数据放入缓存,同步更新缓存为什么没有更新缓存因为CachePut是方法运行完成之后然后再往缓存中放入数据,所以使用以下的方式进行操作这个是以方法的参数ID进行缓存操作,当然还可以使用下面这种方式进行操作。拿出返回值的id。在这里需要注意的一点是Cacheable的key是不能用#result取出对应的返回值的。*/
@CachePut(value = "user", key = "#person.id", unless = "#person eq null") public Person savePerson(Person person) {
return personRepo.savePerson(person);
}
/**
* @CacheEvict 应用到删除或者修改数据的方法上,调用方法时会将指定key的缓存数据清空, 再次重新查询数据库
*/
@CacheEvict(value = "user", key = "#root.targetClass + #username", condition = "#result eq true")public boolean removePersonByName(String username) { return personRepo.removePersonByName(username) > 0; }
public boolean isExistPersonName(Person person) { return personRepo.existPersonName(person) > 0;
}
复制代码


  • 总结:

  • @Cacheable 源码实现


      @Cacheable(cacheNames = "listA",key = "'aa'")        public List<User> findUsers(){            List<User> list = userMapper.selectAll();            return list;        }    //源码(手动整合缓存的话,一般使用Ehcache)    //1.通过缓存管理器得到指定名称的缓存            Cache cache = cacheManager.getCache("listA");            //2.通过指定名称: 获取保存数据的缓存区域            Cache.ValueWrapper cacheValue = cache.get("aa");            if(cacheValue!=null){                //3.根据key从缓存中查询数据                //3.1 判断缓存中有没有数据                //3.2 缓存中有数据,说明不是第一次访问                Object o = cacheValue.get();                System.out.println(o);                System.out.println("将从缓存中拿出的数据响应到前台页面");
}else{ //3.3 缓存中没有数据,说明是第一次访问,查询mysql的数据,存到缓存 List<User> users = userMapper.selectAll(); //3.4 将users集合转成json,存到缓存中 String json =""; //将json存到缓存里面
}
复制代码


  • 总结

  • @Cacheable 属性说明:


cacheNames/value :用来指定缓存组件的名字 >> - key :缓存数据时使用的 key,可以用它来指定。默认是使用方法参数的值。(这个 key 你可以使用 spEL 表达式来编写) >> - keyGenerator :key 的生成器。 key 和 keyGenerator 二选一使用, 需要自定义 bean 生成 key >> - cacheManager :可以用来指定缓存管理器。从哪个缓存管理器里面获取缓存。 >> - condition :可以用来指定符合条件的情况下才缓存 >> - unless :否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。当然你也可以获取到结果进行判断。(通过 #result 获取方法结果) >> - sync :是否使用异步模式。 >> >>


缓存注解源码


// @since 3.1  可以标注在方法上、类上  下同@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface Cacheable {//缓存查询 作用: 先查询缓存,没有再查询关系数据库存到缓存  // 缓存名称  可以写多个~  @AliasFor("cacheNames")  String[] value() default {};  @AliasFor("value")  String[] cacheNames() default {};
// 支持写SpEL,切可以使用#root String key() default ""; // Mutually exclusive:它和key属性互相排斥。请只使用一个 String keyGenerator() default "";
String cacheManager() default ""; String cacheResolver() default "";
// SpEL,可以使用#root。 只有true时,才会作用在这个方法上 String condition() default ""; // 可以写SpEL #root,并且可以使用#result拿到方法返回值~~~ String unless() default ""; // true:表示强制同步执行。(若多个线程试图为**同一个键**加载值,以同步的方式来进行目标方法的调用) // 同步的好处是:后一个线程会读取到前一个缓存的缓存数据,不用再查库了~~~ // 默认是false,不开启同步one by one的 // @since 4.3 注意是sync而不是Async // 它的解析依赖于Spring4.3提供的Cache.get(Object key, Callable<T> valueLoader);方法 boolean sync() default false;}

@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface CachePut {//缓存更新: 先查询数据库,然后更新缓存
@AliasFor("cacheNames") String[] value() default {}; @AliasFor("value") String[] cacheNames() default {};
// 注意:它和上面区别是。此处key它还能使用#result String key() default ""; String keyGenerator() default "";
String cacheManager() default ""; String cacheResolver() default "";
String condition() default ""; String unless() default "";}
Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface CacheEvict {//缓存清除
@AliasFor("cacheNames") String[] value() default {}; @AliasFor("value") String[] cacheNames() default {};
// 它也能使用#result String key() default ""; String keyGenerator() default "";
String cacheManager() default ""; String cacheResolver() default ""; String condition() default "";
// 是否把上面cacheNames指定的所有的缓存都清除掉,默认false boolean allEntries() default false; // 是否让清理缓存动作在目标方法之前执行,默认是false(在目标方法之后执行) // 注意:若在之后执行的话,目标方法一旦抛出异常了,那缓存就清理不掉了~~~~ boolean beforeInvocation() default false;
}
复制代码


  1. spEL 编写 key 或者 condition 或者 unless 属性,可以使用下面的对象

  2. 注意: key 属性里面,可以使用下面图片的对象, condition 属性不能全部使用下面图片的对象, unless 也一样

  3. @Cacheable 的 condition 属性能使用的 SpEL 语言只有 #root 和获取参数类的 SpEL表达式,不能使用返回结果的 #result 。所以 condition = "#result != null" 会导致所有对象都不进入缓存,每次操作都要经过数据库。

  4. @Cacheable 的 unless 属性可以使用 #result 表达式。 效果: 缓存如果有符合要求的缓存数据则直接返回,没有则去数据库查数据,查到了就返回并且存在缓存一份,没查到就不存缓存。



4.3 数据访问资源类


@Component@Path("personMgr")public class PersonMgrResource {
@Autowiredprivate PersonService personService;
@GET@Path("getPersonByName")@Produces(MediaType.APPLICATION_JSON)public JsonResp getPersonByName(@QueryParam("username") String username) { Person person = personService.getPersonByName(username);
return JsonResp.success(person);
}
@POST@Path("removePersonByName")@Produces(MediaType.APPLICATION_JSON)public JsonResp removePersonByName(@QueryParam("username") String username) { if(personService.removePersonByName(username)) {
return JsonResp.success();
}
return JsonResp.fail("系统错误!");
}
@POST@Path("savePerson")@Produces(MediaType.APPLICATION_JSON)public JsonResp savePerson(Person person) {
if (personService.isExistPersonName(person)) { return JsonResp.fail("用户名已存在!");
}
if (personService.savePerson(person).getId() > 0) { return JsonResp.success(); }
return JsonResp.fail("系统错误!");
}
}
复制代码

5.通过 postman 工具来测试缓存是否生效

第一次访问查找用户:


第一次通过用户名称来查找用户可以看到是从库中查询的数据,我们可以通过 RedisClient 工具来查看数据已放入了缓存


第二次查找用户:发现服务端并未打印任何数据库查询日志,可以知道第二次查询是从缓存中查询得到的数据。


总结


本文介绍如何通过 SpringBoot 来一步步集成 Redis 缓存,关于 Redis 的使用它不仅可以用作缓存,还可以用来构建队列系统,Pub/Sub 实时消息系统,分布式系统的的计数器应用,关于 Redis 更多的介绍,请前往官网查阅。

6.内容总结

  • NOSQL(理解)

  • Redis(掌握)

  • SpringBoot 整合 Redis(掌握)

  • SpringBoot 整合 Jedis(理解,并掌握)

  • SpringBoot 整合缓存(掌握)

  • @Cacheable

  • @CachePut

  • @CacheEvict

  • 作业(额外)

  • 需求: index.html 异步请求,在页面显示城市列表(使用缓存技术)



  • 帮助到你的话就点个关注吧~


用户头像

还未添加个人签名 2022-10-21 加入

还未添加个人简介

评论

发布
暂无评论
8分钟带你学会SpringBoot整合Redis来实现缓存技术_redis_好程序员IT教育_InfoQ写作社区