传送门:
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
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 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
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);
}
}
Spring缓存抽象模块通过CacheManager来创建、管理实际缓存组件,当SpringBoot应用程序引入spring-boot-starter-data-redi依赖后吗,容器中将注册的是CacheManager实例RedisCacheManager对象,RedisCacheManager来负责创建RedisCache作为缓存管理组件,由RedisCache操作redis服务器实现缓存数据操作。实际测试发现默认注入的RedisCacheManager操作缓存用的是RedisTemplate<Object, Object>,因此我们需要自定义cacheManager,替换掉默认的序列化器。
@Configuration底层是含有@Component ,所以@Configuration 具有和 @Component 的作用。
@Configuration可理解为用spring的时候xml里面的标签。
注:
配置类必须以类的形式提供(不能是工厂方法返回的实例),允许通过生成子类在运行时增强(cglib 动态代理)。 配置类不能是 final 类(没法动态代理)。 配置注解通常为了通过 @Bean 注解生成 Spring 容器管理的类。 配置类必须是非本地的(即不能在方法中声明,不能是 private)。 任何嵌套配置类都必须声明为static。
@Bean方法不能创建进一步的配置类(也就是返回的bean如果带有@Configuration,也不会被特殊处理,只会作为普通的 bean)。
@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()等方法用来操作不同结构类型的数据
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;
}
}
相关步骤参考: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;
}
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对象 | 当前被调用的方法 | |
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拿到参数 | |
result | 执行上下文 | 方法执行后的返回值(仅当方法执行之后的判断有效,如‘unless’,'cache evict’的beforeInvocation=false) | #result |
@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:作用是主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实查询
方法的调用
主要参数说明:
参数配置和@Cacheable一样。
@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)。
通过注解的属性值可以看出来,这个注解将其他注解方式融合在一起了,我们可以根据需求来自定义注解,并将前面三个注解应用在一起
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"
该注解是可以将缓存分类,它是类级别的注解方式。我们可以这么使用它。 这样的话,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。这个问题坑了我很久