spring boot redis 整合使用cache

739 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第17天,点击查看活动详情

前言

一般在我们的开发中通常会把redis当做缓存来使用,我们会手动对需要缓存的对象进行操作,在redis中读写。久而久之,我们会自己封装一个redis的工具类来作为我们数据对象和redis中间的桥梁。但是这样有一个缺点,就是缓存的读写和我们的业务代码耦合程度太高了,功能模块之间的界限不清晰。

spring 在解耦方面提供了AOP切面这一概念。使用注解进行标记很好地达到了解耦的目的。spring cache模块提供了整合redis的方法,方便地将redis缓存和业务代码分离,但又能保证redis缓存地读写。

搭建环境

首先我们需要引入依赖。

<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>

这里一个是redis模块,另外一个是cache缓存模块。最后一个是lettuce的连接池依赖。 然后修改我们地application.yml配置文件。

spring:
  application:
    name: REDIS
  data:
    redis:
      repositories:
        enabled: false
  cache:
    type: redis
  redis:
    host: 127.0.0.1
    password: 7rWBBtyi2H3Cc9ld
    port: 6379
    lettuce:
      pool:
        max-wait: -1ms
    timeout: 20000

这里首先定义了缓存的类型是redis。然后配置了redis的服务地址以及端口和密码。我提供的是最简单的配置。

spring cache提供了抽象的缓存方法接口,我们可以根据使用不同的缓存载体来选择它对应的缓存实现。下面我们需要创建一个redis缓存的配置类。

@EnableCaching
@Configuration
public class RedisConfiguration extends CachingConfigurerSupport {


    @Bean(name = "selfKeyGenerator")
    public KeyGenerator sassKeyGenerator(){
        final String prefix = "self";
        final String sp = ":";
        return (target, method, params) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(prefix).append(sp);
            sb.append(target.getClass().getSimpleName())
                    .append(sp);
            sb.append(method.getName());
            for (Object param:params){
                sb.append(sp);
                sb.append(param);
            }
            return sb.toString();
        };
    }

    @Bean
    public CacheManager cacheManager(LettuceConnectionFactory connectionFactory){
        RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair
                .fromSerializer(new GenericJackson2JsonRedisSerializer());
        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration
                .defaultCacheConfig()
                .serializeValuesWith(pair)
                .entryTtl(Duration.ofMinutes(10));
        return RedisCacheManager
                .builder(RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory))
                .cacheDefaults(defaultCacheConfig).build();

    }


    /**
     * @description 将redisTemplate注入容器
     * @author zhou
     * @create 2021/4/20 17:30
     * @param
     * @return org.springframework.data.redis.core.RedisTemplate<java.lang.String,java.lang.Object>
     **/
    @Bean
    public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory connectionFactory){
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        this.initRedisTemplate(redisTemplate,connectionFactory);
        return redisTemplate;
    }

    /**
     * @description 初始化redis序列化方式,不开启事务
     * @author zhou
     * @create 2021/4/20 17:29
     * @param
     * @return void
     **/
    private void initRedisTemplate(RedisTemplate<String,Object> redisTemplate,LettuceConnectionFactory connectionFactory){
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        // 开启事务
        //redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.setConnectionFactory(connectionFactory);
    }

首先我们需要一个cacheManager,这个是我们的缓存管理器。我们选择redisCacheManager作为它的实现。这里声明了缓存的序列化方式,所有缓存管理器中缓存的过期时间为10分钟。别的配置默认,那么spring就会读取配置文件里面的参数了。

第二个是KeyGenerator,这个是定义key的生成前缀,方便和别的缓存做区分。

最后要注意在类上加@EnableCaching来激活缓存的配置。

方法验证

这里我们数据层使用jpa来做验证。我们先对应写好增删改查的几个方法

@Slf4j
@Service
@CacheConfig(keyGenerator = "selfKeyGenerator",cacheNames = "book")
public class BookServiceImpl implements BookService {

    @Autowired
    private BookRepository bookRepository;

    @Override
    @CachePut(key = "#bookPO.bookId")
    public BookPO add(BookPO bookPO) {
        bookPO.setCreateDate(LocalDateTime.now());
        bookPO.setUpdateDate(LocalDateTime.now());
        bookRepository.save(bookPO);
        return bookPO;
    }

    @Override
    @CachePut(key = "#id",unless = "null == #result")
    @Transactional(rollbackFor = Exception.class)
    public BookVO update(Long id, BookPO bookPO) {
        bookPO.setBookId(id);
        int i = bookRepository.updateBook(bookPO);
        if (0 == i){
            return null;
        }
        BookVO bookVO = new BookVO();
        bookVO.setBookId(id);
        bookVO.setBookName(bookPO.getBookName());
        return bookVO;
    }

    @Override
    @Cacheable(key = "#id",unless = "null == #result")
    public BookVO get(Long id) {
        BookPO one = bookRepository.getOne(id);
        if (null == one){
            return null;
        }
        BookVO bookVO = new BookVO();
        bookVO.setBookId(one.getBookId());
        bookVO.setBookName(one.getBookName());
        return bookVO;
    }

    @Override
    @CacheEvict(key = "#id")
    public void delete(Long id) {
        bookRepository.deleteById(id);
    }

先来看@CacheConfig 这个注解主要是在类上声明类级别缓存的一些配置,比如, 可以选择这个类中缓存的key的生成器是哪个,以及这个类中缓存的名称是什么。

然后是@CachePut,这个我加在了新增的方法上,目的是当我添加一条记录时,增加一个对应的缓存。这个注解官方的接释是在不干扰方法执行的情况下更新缓存时,可以使用该注释,也就是只要执行了该方法就会添加一个缓存,那么往往是添加在新增方法上用于添加缓存。

@Cacheable这个注解我放在了查询的方法上,目的是,当调用方法时,这个注解会判断,是否命中缓存,如果命中,则返回缓存;如果没有命中缓存,方法执行结束后,添加一份缓存。可以认为程序首先会查询缓存,如果缓存中存在需要的数据,那么直接返回;如果不存在那么会查询数据库,然后把方法的返回值缓存下来。

@CacheEvict这个注解时用于清除缓存,当中有可选参数allEntries=true,如果为true为清空当前cacheName下所有的缓存。一般我们不会这么做,所以都是指定缓存的id来清除。可以看到通常使用sprign表达式来读取方法入参。

以上就是spring boot redis 的简单整合了。