springboot整合spring Cache(redis)

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: 前面有介绍过spring整合redis和redis的一些注意点,在实际开发中,spring cache方便简化的缓存操作的大部分内容。通过注解的方式实现缓存。

前言



20190816160911631.png


前面有介绍过spring整合redis和redis的一些注意点,在实际开发中,spring cache方便简化的缓存操作的大部分内容。通过注解的方式实现缓存。

阅读前推荐先阅读:redis缓存介绍。和springboot整合redis


缓存抽象的核心是将缓存应用于Java方法,从而根据缓存中可用的信息减少执行次数。也就是说,每次调用目标方法时,抽象都会应用一种缓存行为,该行为检查该方法是否已针对给定参数执行。


  • 如果已执行,则返回缓存的结果,而不必执行实际方法。
  • 如果该方法尚未执行,则执行该方法,并将结果缓存并返回给用户,以便下次调用该方法时,返回缓存的结果。
  • 这样,对于给定的一组参数,昂贵的方法(无论是CPU还是IO)只能执行一次,并且重用结果而不必再次实际执行该方法。


在缓存框架出现之前,要么公司自己写缓存框架造轮子,要么在每个service中手动修改调用redis进行存储或者更新(删除).一旦业务逻辑代码有变,将造成不小的维护负担。并且代码量也很大,使得项目看起来很臃肿!


而spring cache的出现用于简化对缓存的操作,你只需要配置缓存的cacheManager,然后按照一些简单的序列化配置,在service层即可使用注解的形式对项目逻辑无侵入的调用缓存。


方法介绍



@Cacheable:


触发缓存填充。即:如果有缓存缓存中直接拿数据(就不走service了),如果没缓存那么就执行service。将service中的值传到缓存中(执行service)。

  • 多个名称:缓存可能有一个名字或者多个名字,那么你可以@Cacheable("books")或者@Cacheable({"books", "isbns"})表示。
  • 自定义key:大家对同一个接口进行查询,可能有多个参数,那么你可以自定义key。类似@Cacheable(value = "getuser",key = "#username")这样写法,这个规则和mybatis注解很像,但也有点区别也可以自行拼凑。
  • 条件缓存:有些数据你若不想所有都想走缓存,当然也可以设置条件缓存,比如你若想让两个字的姓名缓存,那么你可以@Cacheable(value = "getuser",key = "#username",condition="#username.length() < 3")这样写法
  • 上锁@Cacheable(value = "getuser",key = "#username",sync = true)对有些数据,你可能不想让他并发执行,那么可以假设sync关键字。当然,如果是分布式或者多台服务器需要考虑分布式锁实现。


@Cacheable(value = "getalluser")
    public  List<user> getalluser()
    {
        /*
        假设数据从数据库查出啦。有这么两条
         */
        user user1=new user("bigsai","12345","man");
        user user2=new user("给我star","谢谢","man");
        List<user>list=new ArrayList<>();
        list.add(user1);
        list.add(user2);
        return list;
    }
    //获取user  如果没有缓存将执行log
    @Cacheable(value = "getuser",key = "#username")
    public user getuserbyname(String username)
    {
        //假设该数据从mysql查询结果如下
        user user=new user(username,"123","women");
        logger.info("执行方法cacheable,从mysql查询");
        return user;
    }


@CacheEvict:


触发缓存逐出。即删除缓存。执行之后将删除缓存

  • 用法和前面相似@CacheEvict(value = "getuser",key = "#username")
  • 但是若想删除getuser下所有缓存目录,那么加个参数@CacheEvict(value = "getuser",key = "#username",allEntries = true)其中allEntries就是删除所有的意思。当需要清除整个缓存区域时,此选项会派上用场。而不是逐出每个条目。


@CachePut:


更新缓存而不会干扰方法执行。即:不管有没有缓存。每次都执行方法,将方法得到的数据更新到缓存之中。起到update的功能。

  • 需要注意的是,在更新过程中可能有并发的存在。你可以设置锁.参考Cacheable用法


@Caching:


重新组合要应用于方法的多个缓存操作。

  • 有时,需要指定相同类型(例如@CacheEvict或 @CachePut)的多个注释- 例如,因为不同高速缓存之间的条件或键表达式不同。@Caching允许多个嵌套 @Cacheable,@CachePut和@CacheEvict注解相同的方法来使用。以下示例使用两个@CacheEvict注释:@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") })
  • 此操作可能是一个方法操作更新、删除不同的缓存。具体使用需要考虑情景。


@CacheConfig:


在类级别共享一些常见的缓存相关设置。

  • 此方法针对同一个类中一些共同操作,简便操作和代码量。
  • 到目前为止,我们已经看到缓存操作提供了许多自定义选项,您可以为每个操作设置这些选项。但是,如果某些自定义选项适用于该类的所有操作,则它们可能会很繁琐。例如,指定用于类的每个高速缓存操作的高速缓存的名称可以由单个类级定义替换。这是@CacheConfig 发挥作用的地方。以下示例用于@CacheConfig设置缓存的名称:


@CacheConfig("books") 
public class BookRepositoryImpl implements BookRepository {
    @Cacheable
    public Book findBook(ISBN isbn) {...}
}


具体整合



step1:创建springboot工程,省略部分截图

step2:添加maven依赖


   <dependency>
     <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-data-redis</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-cache</artifactId>
     </dependency>
     <dependency>
         <groupId>org.apache.commons</groupId>
         <artifactId>commons-pool2</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
    </dependency>


step3: 配置application.properties


spring.redis.host=127.0.0.1
spring.redis.password=
spring.redis.port= 6379
spring.redis.timeout=10000
spring.cache.type=redis
logging.level.com.redisCache=debug
spring.redis.database=3
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0


step4: config目录下建立配置文件(缓存注入对象配置,以及序列化相关配置,和前文redis差不多)

  • 这里面需要注意的是里面有关于redis的一些配置。在项目中防止多个项目公用一个redis可以在redis前面加个前缀(通常是项目名)。
  • 过期时间一定要设置,并且过期策略还需要根据项目需求具体踩坑设置。


package com.redisCache.config;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.cache.CacheManager;
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.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.io.Serializable;
import java.time.Duration;
import java.util.*;
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfiguration {
    @Bean
    public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Serializable> template = new RedisTemplate<>();
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
    @Bean(name = "cacheManager")
    @Primary
    public CacheManager cacheManager( RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
               // .entryTtl(Duration.ofSeconds(50))设置过期时间
                .disableCachingNullValues()
                .computePrefixWith(cacheName -> "rediscache".concat(":").concat(cacheName).concat(":"))//rediscache可以改成你的项目名
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        return RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(cacheConfiguration)
                .build();
    }
}


step5:创建pojo对象和controller

pojo:


package com.redisCache.pojo;
import java.io.Serializable;
public class user  implements Serializable {
    private String name;
    private  String password;
    private  String sex;
    public user(String name,String password,String sex)
    {
        this.name=name;
        this.password=password;
        this.sex=sex;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
}


service:


package com.redisCache.service;
import com.redisCache.pojo.user;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class redisService {
    Logger logger= LoggerFactory.getLogger(redisService.class);
    @Cacheable(value = "getalluser")
    public  List<user> getalluser()
    {
        /*
        假设数据从数据库查出啦。有这么两条
         */
        user user1=new user("bigsai","12345","man");
        user user2=new user("给我star","谢谢","man");
        List<user>list=new ArrayList<>();
        list.add(user1);
        list.add(user2);
        return list;
    }
    //获取user  如果没有缓存将执行log
    @Cacheable(value = "getuser",key = "#username")
    public user getuserbyname(String username)
    {
        //假设该数据从mysql查询结果如下
        user user=new user(username,"123","women");
        logger.info("执行方法cacheable,从mysql查询");
        return user;
    }
     //更新user。每次都执行
    @CachePut(value = "getuser",key = "#username")
    public user updateuser(String username,String password)
    {
        //假设更新用户账号密码re
        user user=new user(username,"123","women");
        user.setPassword(password);
        logger.info("执行方法cacheput,再数据库更新返回");
        return user;
    }
    //删除缓存,其中condition可加可不加,本句意思是只有当姓名为bigsai
    @CacheEvict(value = "getuser",key = "#username",allEntries = true)
    public String deleteuser(String username)
    {
         return "移除成功";
    }
}


controller:


package com.redisCache.controller;
import com.redisCache.pojo.user;
import com.redisCache.service.redisService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class redisController {
    private  final static Logger log= LoggerFactory.getLogger(redisController.class);
    @Autowired(required = false)
    redisService redisService;
    @GetMapping("getalluser")
    public List<user>getalluser()
    {
        return redisService.getalluser();
    }
    @GetMapping("getuser/{username}")
    public user getuser(@PathVariable String username)
    {
        return redisService.getuserbyname(username);
    }
    @GetMapping("updateuser/{username}/{password}")
    public user updateuser(@PathVariable String username,@PathVariable String password)
    {
        return redisService.updateuser(username,password);
    }
    @GetMapping("deleteuser/{username}")
    public String deleteuser(@PathVariable String username)
    {
        return  redisService.deleteuser(username);
    }
}


step6:在启动类加上允许缓存注解


20190816162415395.png


测试与总结



getalluser:


2019081616312563.png


getuser:


20190816163223206.png


update:更新上一条


20190816163335732.png


delete:整个移除(前面讲到allentries参数作用,不妨自己测试)


20190816164008381.png


  • 可以看得到缓存的一些操作正常完成
  • 上文只是一个简单的整合过程,具体使用还需要自己踩坑才行。而缓存的深度远远不是这么简单,还需要自己挖掘。
  • 项目完整github地址。不吝啬star的大哥求个star
  • 最后,欢迎关注个人公众号bigsai 关注后回复 java精选资料一份!




相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
8天前
|
缓存 前端开发 Java
【Spring】——SpringBoot项目创建
SpringBoot项目创建,SpringBootApplication启动类,target文件,web服务器,tomcat,访问服务器
|
2月前
|
监控 Java 数据库连接
详解Spring Batch:在Spring Boot中实现高效批处理
详解Spring Batch:在Spring Boot中实现高效批处理
213 12
|
30天前
|
存储 NoSQL Java
使用lock4j-redis-template-spring-boot-starter实现redis分布式锁
通过使用 `lock4j-redis-template-spring-boot-starter`,我们可以轻松实现 Redis 分布式锁,从而解决分布式系统中多个实例并发访问共享资源的问题。合理配置和使用分布式锁,可以有效提高系统的稳定性和数据的一致性。希望本文对你在实际项目中使用 Redis 分布式锁有所帮助。
96 5
|
2月前
|
安全 Java 测试技术
详解Spring Profiles:在Spring Boot中实现环境配置管理
详解Spring Profiles:在Spring Boot中实现环境配置管理
92 10
|
1月前
|
负载均衡 Java 开发者
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
124 5
|
2月前
|
消息中间件 NoSQL Java
Spring Boot整合Redis
通过Spring Boot整合Redis,可以显著提升应用的性能和响应速度。在本文中,我们详细介绍了如何配置和使用Redis,包括基本的CRUD操作和具有过期时间的值设置方法。希望本文能帮助你在实际项目中高效地整合和使用Redis。
67 2
|
3月前
|
缓存 NoSQL Java
Spring Boot与Redis:整合与实战
【10月更文挑战第15天】本文介绍了如何在Spring Boot项目中整合Redis,通过一个电商商品推荐系统的案例,详细展示了从添加依赖、配置连接信息到创建配置类的具体步骤。实战部分演示了如何利用Redis缓存提高系统响应速度,减少数据库访问压力,从而提升用户体验。
167 2
|
2月前
|
JavaScript NoSQL Java
CC-ADMIN后台简介一个基于 Spring Boot 2.1.3 、SpringBootMybatis plus、JWT、Shiro、Redis、Vue quasar 的前后端分离的后台管理系统
CC-ADMIN后台简介一个基于 Spring Boot 2.1.3 、SpringBootMybatis plus、JWT、Shiro、Redis、Vue quasar 的前后端分离的后台管理系统
53 0
|
存储 SQL 消息中间件
springboot整合redis
redis是一个支持key-value的数据库,数据全部在内存中处理,在在一定时间间隔中将数据固化到磁盘。因为是内存操作,所以速度特别快。(这里我们主要介绍redis作为缓存使用)
212 0
springboot整合redis
|
存储 缓存 NoSQL
SpringBoot整合Redis
SpringBoot整合Redis
400 0
SpringBoot整合Redis