前言

传送门:

spring boot redis官方,目前版本 2.1x,配置和1.x有一定区别。https://spring.io/projects/spring-data-redis 阿里云redis开发规范https://yq.aliyun.com/articles/531067 spring boot.2x 集成redis–自定义注解实现过期时间 redis注解上直接配置过期时间和自动刷新时间最新版本特性: @Cacheable(value = “people#120#90”, key = “#person.id”) springboot与缓存—使用、原理、整合redis

redis配置首先引入依赖

spring boot 项目中引入redis相关依赖,这里顺便将session保存到redis中。

<!-- Spring Boot Data Cache 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>
配置redis参数
##  redis config
spring.redis.host=
spring.redis.port=6379
spring.redis.password=
# 最大空闲连接数
spring.redis.jedis.pool.max-active=8
# 最小空闲连接数
spring.redis.jedis.pool.max-idle=8
# 等待可用连接的最大时间,负数为不限制

spring.redis.jedis.pool.max-wait=-1
# 最大活跃连接数,负数为不限制
spring.redis.jedis.pool.min-idle=1
# 数据库连接超时时间,2.0 中该参数的类型为Duration,这里在配置的时候需要指明单位  1.x可以将此参数配置10000 单位是ms
# 连接池配置,2.0中直接使用jedis或者lettuce配置连接池
spring.redis.timeout=60s
spring.redis.database=0
设置redis配置文件
package com.csii.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.lang.reflect.Method;
import java.time.Duration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * redis缓存配置
 *
 * @author
 * @version V1.0
 * @date 
 */
@Configuration
public class RedisCacheConfig extends CachingConfigurerSupport {
    private final static Logger logger = LoggerFactory.getLogger(RedisCacheConfig.class);

    /**
     * 自定义redis key值生成策略
     * @return
     */
    @Bean
    @Override
    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();
            }
        };
    }
// data-redis 1.x形式
//    @Bean
//    public CacheManager cacheManager(RedisTemplate redisTemplate) {
//        RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
//        return redisCacheManager;
//    }
//
//

    /**
     * spring-data-redis  版本不同,方法也不一样 上面是1.5  下面是2.0
     * @param connectionFactory
     * @return
     */

   /* @Bean
    public CacheManager cacheManager(RedisTemplate redisTemplate) {
        RedisCacheManager manager = new RedisCacheManager(redisTemplate);
        manager.setUsePrefix(true);
        RedisCachePrefix cachePrefix = new RedisPrefix("prefix");
        manager.setCachePrefix(cachePrefix);
        // 整体缓存过期时间
        manager.setDefaultExpiration(3600L);
        // 设置缓存过期时间。key和缓存过期时间,单位秒
        Map<String, Long> expiresMap = new HashMap<>();
        expiresMap.put("user", 1000L);
        manager.setExpires(expiresMap);
        return manager;
    }*/


    /**
     * 自定义CacheManager  配置全局缓存时间  data-redis2.x配置
     */
//    @Bean
//    public CacheManager cacheManager(RedisTemplate redisTemplate) {
//        //全局redis缓存过期时间
//        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration
//                .defaultCacheConfig()
//                .entryTtl(Duration.ofHours(1));
//        RedisCacheWriter redisCacheWriter = RedisCacheWriter
//                .nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory());
//        RedisCacheManager redisCacheManager = new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
//        return redisCacheManager;
//    }

	/**
     *  data-redis 2.x配置  支持配置对应key缓存时间
     * @param factory
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();  // 生成一个默认配置,通过config对象即可对缓存进行自定义配置
        config = config.entryTtl(Duration.ofMinutes(30))     // 设置缓存的默认过期时间,也是使用Duration设置
                .disableCachingNullValues();     // 不缓存空值

        // 设置一个初始化的缓存空间set集合
        Set<String> cacheNames =  new HashSet<>();
        cacheNames.add("device");
        cacheNames.add("project_type");

        // 对每个缓存空间应用不同的配置
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
        configMap.put("device", config);
        configMap.put("project_type", config.entryTtl(Duration.ofHours(1)));
        // 使用自定义的缓存配置初始化一个cacheManager
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .initialCacheNames(cacheNames)
                // 注意这两句的调用顺序,一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
                .withInitialCacheConfigurations(configMap)
                .build();
        return cacheManager;
    }

    /**
     * @Description: 防止redis入库序列化乱码的问题
     * @return     返回类型
     * @date 
     */
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //key序列化
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //value序列化
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class));
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}
开启缓存配置

这里吧@EnableCaching@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600 * 24 配置在main主类上,也可以配置 在configuration的类,只要项目启动后可以扫到注解进行初始化

/**
 *
 */
@SpringBootApplication
@MapperScan("com.csii.dao")
@EnableCaching
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600 * 24)
public class AppCsiiServerApplication {

    private static Logger logger = LoggerFactory.getLogger(AppCsiiServerApplication.class);
    public static ConfigurableApplicationContext context;
    public static void main(String[] args) {
        context=SpringApplication.run(AppCsiiServerApplication.class, args);
    }

}
redis集成Spring原理是什么?

Spring缓存抽象模块通过CacheManager来创建、管理实际缓存组件,当SpringBoot应用程序引入spring-boot-starter-data-redi依赖后吗,容器中将注册的是CacheManager实例RedisCacheManager对象,RedisCacheManager来负责创建RedisCache作为缓存管理组件,由RedisCache操作redis服务器实现缓存数据操作。实际测试发现默认注入的RedisCacheManager操作缓存用的是RedisTemplate<Object, Object>,因此我们需要自定义cacheManager,替换掉默认的序列化器。

@Configuration作用

@Configuration底层是含有@Component ,所以@Configuration 具有和 @Component 的作用。

@Configuration可理解为用spring的时候xml里面的标签。

注:

配置类必须以类的形式提供(不能是工厂方法返回的实例),允许通过生成子类在运行时增强(cglib 动态代理)。 配置类不能是 final 类(没法动态代理)。 配置注解通常为了通过 @Bean 注解生成 Spring 容器管理的类。 配置类必须是非本地的(即不能在方法中声明,不能是 private)。 任何嵌套配置类都必须声明为static。

@Bean方法不能创建进一步的配置类(也就是返回的bean如果带有@Configuration,也不会被特殊处理,只会作为普通的 bean)。

@EnableCaching作用是什么?

@EnableCaching注解是spring framework中的注解驱动的缓存管理功能。自spring版本3.1起加入了该注解。 当你在配置类(@Configuration)上使用@EnableCaching注解时,会触发一个post processor,这会扫描每一个spring bean,查看是否已经存在注解对应的缓存。如果找到了,就会自动创建一个代理拦截方法调用,使用缓存的bean执行处

因此我们只要在使用的地方注入即可:

@Autowired StringRedisTemplate stringRedisTemplate; //操作 k-v 字符串 @Autowired RedisTemplate redisTemplate; //k- v 都是对象 StringRedisTemplate 提供opsForValue用来操作key-value,如上面的示例,另外还提供了一系列opsForHash()、opsForList()、opsForSet()、opsForZSet()等方法用来操作不同结构类型的数据

redis注解使用案例
package com.sl.cache.service;
import com.sl.cache.entity.Product;
import com.sl.cache.mapper.ProductMapper;
import org.springframework.beans.factory.annotation.Autowired;
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.cache.annotation.Caching;
import org.springframework.stereotype.Service;

@Service
@CacheConfig(cacheNames = "product")
public class ProductService {
    @Autowired
    private ProductMapper productMapper;

    @Cacheable(cacheNames = "product1",key = "#root.methodName+'['+#id+']'")
    //@Cacheable(cacheNames = {"product1","product2"})// 默认key为参数,多个参数SimpleKey [arg1,arg2]
    //@Cacheable(cacheNames = "product",key = "#root.methodName+'['+#id+']'")
    //@Cacheable(cacheNames = "product",keyGenerator = "myKeyGenerator")
    //@Cacheable(cacheNames = "product",key = "#root.methodName+'['+#id+']'",condition="#a0>10",unless = "#a0==11") //或者condition="#id>10")
    public Product getProductById(Long id){
       Product product =productMapper.getProductById(id);
       System.out.println(product);
       return product;
    }

    @CachePut(value="product",key = "#result.productId",condition = "#result!=null")
    public  Product updateProduct(Product product){
        int count = productMapper.updateProduct(product);
        System.out.println("影响行数:"+count);
        if(count>0){
            return product;
        }else{
            return null;
        }
    }

    //@CacheEvict(value="product",key="#id")
    //@CacheEvict(value="product",allEntries = true) //清楚所有缓存
    @CacheEvict(value="product",allEntries = true,beforeInvocation = true) //清楚所有缓存
    public boolean deleteProductById(Long id) {
        productMapper.deleteProductById(id);
        return true;
    }

    //含有CachePut注解,所以执行这个方法时一定会查询数据库,及时有cacheable注解
    @Caching(
            cacheable = {@Cacheable(value="product",key="#productName")},
            put = {
                    @CachePut(value="product",key="#result.productId"),
                    @CachePut(value="product",key="#result.productName")
            }
    )
    public  Product getProductByName(String productName){

        Product product =productMapper.getProductByName(productName);

         return product;
    }
}
Spring Boot缓存实战 Redis 设置有效时间和自动刷新缓存方式一:直接扩展注解的Value值

相关步骤参考:https://www.jianshu.com/p/275cb42080d9

参考配置如下:

@Override
@Cacheable(value = "people#${select.cache.timeout:1800}#${select.cache.refresh:600}", key = "#person.id", sync = true)
public Person findOne(Person person, String a, String[] b, List<Long> c) {
    Person p = personRepository.findOne(person.getId());
    System.out.println("为id、key为:" + p.getId() + "数据做了缓存");
    System.out.println(redisTemplate);
    return p;
}
方式一:通过CancheManager配置(业务量不大推荐)

spring-data-redis 1.x版本配置

/*
     * spring-data-redis  1.5X版本
     * @param connectionFactory
     * @return
     */

   @Bean
    public CacheManager cacheManager(RedisTemplate redisTemplate) {
        RedisCacheManager manager = new RedisCacheManager(redisTemplate);
        manager.setUsePrefix(true);
        RedisCachePrefix cachePrefix = new RedisPrefix("prefix");
        manager.setCachePrefix(cachePrefix);
        // 整体缓存过期时间
        manager.setDefaultExpiration(3600L);
        // 设置缓存过期时间。key和缓存过期时间,单位秒
        Map<String, Long> expiresMap = new HashMap<>();
        expiresMap.put("user", 1000L);
        manager.setExpires(expiresMap);
        return manager;
    }

2.x版本配置

/**
     *  data-redis 2.x配置  支持配置对应key缓存时间
     * @param factory
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();  // 生成一个默认配置,通过config对象即可对缓存进行自定义配置
        config = config.entryTtl(Duration.ofMinutes(30))     // 设置缓存的默认过期时间,也是使用Duration设置
                .disableCachingNullValues();     // 不缓存空值

        // 设置一个初始化的缓存空间set集合
        Set<String> cacheNames =  new HashSet<>();
        cacheNames.add("device");
        cacheNames.add("project_type");

        // 对每个缓存空间应用不同的配置
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
        configMap.put("device", config);
        configMap.put("project_type", config.entryTtl(Duration.ofHours(1)));
        // 使用自定义的缓存配置初始化一个cacheManager
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .initialCacheNames(cacheNames)
                // 注意这两句的调用顺序,一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
                .withInitialCacheConfigurations(configMap)
                .build();
        return cacheManager;
    }
常用缓存注解使用

注解类型

作用

应用

@Cacheable

触发缓存入口

查询数据缓存

@CacheEvict

触发移除缓

缓存移除

@CacahePut

更新缓存

用于插入和更新数据操作

@Caching

将多种缓存操作分组

支持更多缓存操作

@CacheConfig

类级别的缓存注解,允许共享缓存名称

class级别配置

使用案例集锦:

@Cacheable("product")
@Cacheable(value = {"product","order"}, key = "#root.targetClass+'-'+#id")
@Cacheable(value = "product", key = "#root.targetClass+'-'+#id")
自定义cacheManager
@Cacheable(value = "product", key = "#root.targetClass+'-'+#id” cacheManager="cacheManager")
@CachePut
应用到写数据的方法上,如新增/修改方法
@CachePut(value = "product", key = "#root.targetClass+'-'+#product.id")
@CacheEvict 
即应用到移除数据的方法上,如删除方法
@CacheEvict(value = "product", key = "#root.targetClass+'-'+#id")
提供的SpEL上下文数据

Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档:

名字

位置

描述

示例

methodName

root对象

当前被调用的方法名

#root.methodName

method

root对象

当前被调用的方法

#root.method.name

target

root对象

当前被调用的目标对象

#root.target

targetClass

root对象

当前被调用的目标对象类

#root.targetClass

args root对象

当前被调用的方法的参数列表

#root.args[0]

caches root对象

当前方法调用使用的缓存列表

(如@Cacheable(value={“cache1”, “cache2”})),则有两个cache

#root.caches[0].name

argument name

执行上下文

当前被调用的方法的参数,如findById(Long id),我们可以通过#id拿到参数

#user.id

result

执行上下文

方法执行后的返回值(仅当方法执行之后的判断有效,如‘unless’,'cache evict’的beforeInvocation=false)

#result

@Cacheable

@Cacheable:作用是主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,已经缓存就不再调用注解修饰的方法,适用于查询接口

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.cache.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
    @AliasFor("cacheNames")
    String[] value() default {};

    @AliasFor("value")
    String[] cacheNames() default {};

    String key() default "";

    String keyGenerator() default "";

    String cacheManager() default "";

    String cacheResolver() default "";

    String condition() default "";

    String unless() default "";

    boolean sync() default false;
}

主要参数说明:

1、value :缓存的名称,在 spring 配置文件中定义,必须指定至少一个,

例如:@Cacheable(value=”mycache”) 或者 @Cacheable(value={”cache1”,”cache2”}。

2、key :缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合,

例如:@Cacheable(value=”testcache”,key=”#userName”)。

3、condition :缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存, 例如:@Cacheable(value=”testcache”,condition=”#userName.length()>2。 4、keyGenerator 配置key自动生成器 例如: @Cacheable(value=“cat”, keyGenerator = “accountKeyGenerator”)

@CachePut

@CachePut:作用是主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实查询

方法的调用

主要参数说明:

参数配置和@Cacheable一样。

@CacheEvict

@CacheEvict:作用是主要针对方法配置,能够根据一定的条件对缓存进行清空

在这里插入代码片

主要参数说明: 1、value 和 cacheName, key 和 condition 参数配置和@Cacheable一样。 2、allEntries :是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存, 例如:

@CachEvict(value=”testcache”,allEntries=true)。

3、beforeInvocation :是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存,

例如

@CachEvict(value=”testcache”,beforeInvocation=true)。
@Caching

通过注解的属性值可以看出来,这个注解将其他注解方式融合在一起了,我们可以根据需求来自定义注解,并将前面三个注解应用在一起

public @interface Caching {
    Cacheable[] cacheable() default {};
 
    CachePut[] put() default {};
 
    CacheEvict[] evict() default {};
}

使用:

@Caching(
  put = {
           @CachePut(value = "user", key = "\"user_\" + #user.id"),
           @CachePut(value = "user", key = "#user.name"),
           @CachePut(value = "user", key = "#user.account")
   }
    )
    public User saveUserByCaching(User user){
        userMapper.insert(user);
        return user;
    }

通过这种方式可以添加多个key

127.0.0.1:6379> keys *
1) "user~keys"
2) "dsjkf"
3) "dkjd"
4) "user_3"
@CacheConfig 类级别的缓存注解,允许共享缓存名称

该注解是可以将缓存分类,它是类级别的注解方式。我们可以这么使用它。 这样的话,UseCacheRedisService的所有缓存注解例如@Cacheable的value值就都为user。

@CacheConfig(cacheNames = "user")
@Service
public class UseCacheRedisService {}

在redis的缓存中显示如下

127.0.0.1:6379> keys *

“user~keys”

“user_1” 127.0.0.1:6379> get user~keys (error) WRONGTYPE Operation against a key holding the wrong kind of value 127.0.0.1:6379> type user~keys zset 127.0.0.1:6379> zrange user~keys 0 10

“user_1” 我们注意到,生成的user~keys,它是一个zset类型的key,如果使用get会报WRONGTYPE Operation against a key holding the wrong kind of value。这个问题坑了我很久