springboot整合spring Cache(redis)

13 篇文章 3 订阅
4 篇文章 0 订阅

前言

在这里插入图片描述
前面有介绍过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:在启动类加上允许缓存注解
在这里插入图片描述

测试与总结

getalluser:
在这里插入图片描述
getuser:
在这里插入图片描述
update:更新上一条
在这里插入图片描述
delete:整个移除(前面讲到allentries参数作用,不妨自己测试)
在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员bigsai

喝杯咖啡压压惊!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值