一,介绍

SpringBoot 的缓存可以使用本地缓存,也可以整合第三方缓存,比如:redis、guava、ehcahe、jcache等等。
接下来主要介绍SpringBoot的缓存整合Redis的使用。

注:下面介绍的方式主要是通过注解来进行缓存的使用,当然,你还可以使用RedisTemplate来操作Reids缓存,这里不做介绍。

  • 在pom.xml中添加以下依赖
<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

二,本地缓存

2.1,概念

Java Caching定义了五个核心接口,分别是CachingProvider、CachingManager、Cache、Entry、Expiry

  • CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个CachingProvider。
  • CacheManager定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。
  • Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。
  • Entry是一个存储在Cache中的key-value对。
  • Expiry每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。

2.2,几个重要的缓存注解

@EnableCaching

开启基于注解的缓存;
可以加在主运行类上,也可以加在配置类上;

@EnableCaching
@SpringBootApplication
public class DelespringApplication {

    public static void main(String[] args) {
        SpringApplication.run(DelespringApplication.class, args);
    }

}
@EnableCaching
@Configuration
public class RedisConfig{
    
}

@Cacheable

主要针对方法配置,能够根据方法的请求参数对其进行缓存

@Cacheable(value = "cach1")
@RequestMapping("list")
public List<Student> fun(Integer id,Integer age) {
    List<Student> list = studentMapper.selectList(null);
    return list;
}

常用参数:

  • value/cacheNames

缓存的名称,也是缓存的命名空间

  • key
    缓存的键,可以为空,如果指定要按照 SpEL 表达式编写;
    默认的key
  • 单参数:cacheNames::arg
  • 无参数: cacheNames::SimpleKey [], 后面使用 SimpleKey []来补齐
  • 多参数: cacheNames::SimpleKey [arg1, arg2…]
  • 非基础对象:cacheNames::obj.toString()

使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。 如:
@Cacheable(value="users", key="#id")
@Cacheable(value="users", key="#p0")
SpEL上下文数据

名称位置描述示例
methodNameroot对象当前被调用的方法名#root.methodname
methodroot对象当前被调用的方法#root.method.name
targetroot对象当前被调用的目标对象实例#root.target
targetClassroot对象当前被调用的目标对象的类#root.targetClass
argsroot对象当前被调用的方法的参数列表#root.args[0]
cachesroot对象当前方法调用使用的缓存列表#root.caches[0].name
Argument Name执行上下文当前被调用的方法的参数,如findArtisan(Artisan artisan),可以通过#artsian.id获得参数#artsian.id
result执行上下文方法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false)#result

SpEL提供了多种运算符

关系<,>,<=,>=,==,!=,lt,gt,le,ge,eq,ne
类型运算符
算术+,- ,* ,/,%,^
逻辑&&,!,and,or,not,between,instanceof
条件?: (ternary),?: (elvis)
正则表达式matches
其他类型?.,?[…],![…],^[…],$[…]
  • keyGenerator
    缓存键的生成器
@Configuration
public class MyConfig extends CachingConfigurerSupport {
    @Autowired
    private RedisConnectionFactory factory;

    @Bean
    public KeyGenerator keyGenerator(){
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                String name = method.getName();
                return name;
            }
        };
    }
}
@RestController
public class MyController {
    @Autowired
    private StudentMapper studentMapper;

    @Cacheable(value = "cach1",keyGenerator = "keyGenerator")
    @RequestMapping("list")
    public List<Student> fun(Integer id,Integer age) {
        List<Student> list = studentMapper.selectList(null);
        return list;
    }
}
  • cacheManager
    缓存管理器,用法同上。
  • condition
    缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存/清除缓存 例如:@Cacheable(value=”testcache”,condition=”#userName.length()>2”)
  • unless
    否定缓存。当条件结果为TRUE时,就不会缓存。@Cacheable(value=”testcache”,unless=”#userName.length()>2”)

@CachePut

保证方法被调用,又希望结果被缓存。与@Cacheable区别在于是否每次都调用方法,常用于更新
常用参数和 @Cacheable 大致一样

@CacheEvict

清空缓存
常用参数:

  • allEntries
    是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 例如:@CachEvict(value=”testcache”,allEntries=true)
  • beforeInvocation
    是否在方法执行前就清空,缺省为 false,如果指定为 true,

@CacheConfig

该注解可以统一设置缓存的名称,它是类级别的注解方式。
注意:如果同时使用了 @CacheConfig 和 @Cacheable ,则将优先使用 @Cacheable

@CacheConfig(cacheNames = "renqiang")
@RestController
public class MyController {
    @Autowired
    private StudentMapper studentMapper;

    // 优先使用此缓存名称
    @Cacheable(value = "qiang")
    @RequestMapping("list")
    public List<Student> fun(Integer id,Integer age) {
        List<Student> list = studentMapper.selectList(null);
        return list;
    }
}

@Caching

使用以上注解的时候,每一个注解都需要一个方法,如果要实现一个方法同时满足多个缓存注解的时候,就可以使用此注解。
从源码可以看出来,此注解是 @Cacheable,@CachePut,@CacheEvict 的结合体

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {

	Cacheable[] cacheable() default {};

	CachePut[] put() default {};

	CacheEvict[] evict() default {};

}

例子:

@RestController
public class MyController {
    @Autowired
    private StudentMapper studentMapper;

    @Caching(
            cacheable = {@Cacheable(value = "ren")},
            put = {
                    @CachePut(value = "ren1",key = "#id"),
                    @CachePut(value = "ren2",key = "#age")
            }
    )
    @RequestMapping("list")
    public List<Student> fun(Integer id,Integer age) {
        List<Student> list = studentMapper.selectList(null);
        return list;
    }
}

三,相关配置

1,自定义 key

第一种方法,继承CachingConfigurerSupport类,重写 KeyGenerator 方法

@Configuration
public class MyConfig extends CachingConfigurerSupport {

    @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();
            }
        };
    }
}

第二种方法,实现KeyGenerator接口

/**
 * @Author: Esyrg
 * @Date: 2022/6/11 23:15
 */
@Configuration
public class MyConfig implements KeyGenerator {

    @Override
    public Object generate(Object target, Method method, Object... params) {
        return null;
    }
}

使用

/**
 * @Author: Esyrg
 * @Date: 2022/6/11 22:25
 */
@RestController
public class MyController {
    @Autowired
    private StudentMapper studentMapper;

    @Cacheable(value = "cah1",keyGenerator = "myConfig")
    @RequestMapping("fun1")
    public List fun1(){
        List<Student> list = studentMapper.selectList(null);
        return list;
    }
}

2,自定义缓存过期时间

第一种方法,继承CachingConfigurerSupport类,重写 cacheManager 方法

/**
 * @Author: Esyrg
 * @Date: 2022/8/22 21:41
 */
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
    @Autowired
    private RedisConnectionFactory factory;

    @Override
    public CacheManager cacheManager() {
        RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
        RedisCacheConfiguration configuration1 = configuration
                .entryTtl(Duration.ofSeconds(20)) // 设置过期事件为20秒
                .disableCachingNullValues() // 不缓存空值
//                .disableKeyPrefix() // 不使用缓存前缀
                .prefixCacheNameWith("ren"); // 自定义缓存前缀

        RedisCacheManager build = RedisCacheManager.builder(factory)
                .cacheDefaults(configuration1)
                .build();
        return build;
    }
}

第二种方法,配置类

/**
 * @Author: Esyrg
 * @Date: 2022/8/22 21:41
 */
@Configuration
public class RedisConfig {
    @Autowired
    private RedisConnectionFactory factory;

    @Bean
    public CacheManager cacheManager() {
        RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
        RedisCacheConfiguration configuration1 = configuration.entryTtl(Duration.ofSeconds(20))
                .disableCachingNullValues();
        // 设置一个初始化的缓存空间set集合
        Set<String> cacheNames = new HashSet<>();
        cacheNames.add("cah1");
        cacheNames.add("cah2");
        // 对每个缓存空间应用不同的配置
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
        configMap.put("cah1", configuration1); // 使用默认的缓存时间
        configMap.put("cah2", configuration.entryTtl(Duration.ofSeconds(10))); // cah2 缓存空间的缓存时间
        RedisCacheManager build = RedisCacheManager
                .builder(factory)
                .initialCacheNames(cacheNames) // 注意这两句的调用顺序,一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
                .withInitialCacheConfigurations(configMap)
                .build();
        return build;
    }
}

3,自定义序列化方式

/**
 * @Author: Esyrg
 * @Date: 2022/8/22 21:41
 */
@Configuration
public class RedisConfig {
    @Autowired
    private RedisConnectionFactory factory;

    @Bean
    public CacheManager cacheManager() {
        //初始化一个RedisCacheWriter
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(factory);
        Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        RedisSerializationContext.SerializationPair serializationPair = RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer);
        // RedisCacheConfiguration默认是使用StringRedisSerializer序列化key,JdkSerializationRedisSerializer序列化value
        // 设置 key 的序列化方式为 jackson
        RedisCacheConfiguration configuration1 = RedisCacheConfiguration.defaultCacheConfig().serializeKeysWith(serializationPair);
        // 设置 value 的序列化方式为 jackson
        RedisCacheConfiguration configuration2 = configuration1.serializeValuesWith(serializationPair);
        // 设置过期时间为 20 秒
        RedisCacheConfiguration configuration3 = configuration2.entryTtl(Duration.ofSeconds(20));

        RedisCacheManager manager = new RedisCacheManager(redisCacheWriter, configuration3);
        return manager;

    }
}
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐