springcache是java的缓存框架,它是一种抽象,一种规范,开发者不能直接使用他,必须用他的一些实现,比如redis,ehcache。两个的关系就好比jdbc与mysql驱动的关系。springboot为搜springcache提供了自动化配置方案,只需要引入依赖即可。
Cache接口下Spring提供了各种xxxCache的实现,如RedisCache,EhCacheCache ,ConcurrentMapCache等;
一、Cache与CacheManger
针对不同的缓存技术,需要实现不同的cacheManager,Spring定义了如下的cacheManger实现。
常规的SpringBoot已经为我们自动配置了EhCache、Collection、Guava、ConcurrentMap等缓存,默认使用ConcurrentMapCacheManager。SpringBoot的application.properties配置文件,使用spring.cache前缀的属性进行配置。
2、缓存依赖
spring-boot-starter-cache 为基础依赖,其他依赖根据使用不同的缓存技术选择加入,默认情况下使用 ConcurrentMapCache不需要引用任何依赖,这里使用 RedisCacheManager,则只需要引入redis的"spring-boot-starter-data-redis"依赖即可,Redis使用模式使用pool2连接池,再需要引用org.apache.commons的依赖"commons-pool2"
<!--spring cache,当使用的是redis最为缓存,则该依赖页不需要-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--redis 连接池依赖包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
3、application.yml配置
spring:
application:
name: cache_demo
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://ip:3306/itcast?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
redis:
database: 0 # Redis数据库索引(默认为0)
timeout: 60000 # 连接超时时间(毫秒)
password: 123456
host: ip
port: 6379
lettuce:
shutdown-timeout: 100 # 毫秒
pool:
min-idle: 5 # 连接池中的最小空闲连接
max-active: 5000 # 连接池最大连接数(使用负值表示没有限制)
max-idle: 10 # 连接池中的最大空闲连接
max-wait: 1000 # 连接池最大阻塞等待时间(使用负值表示没有限制):毫秒
cache:
redis:
time-to-live: 1800000 # 缓存默认有效时长,以毫秒为单位
type: redis #指定使用redis作为缓存
4、缓存注解
下面是缓存公用接口注释,使用与任何缓存技术
- 1,@EnableCaching:在启动类注解@EnableCaching开启缓存
@SpringBootApplication
@EnableCaching //开启缓存
public class DemoApplication{
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
- 2、@Cacheable
配置了getById函数的返回值将被加入缓存。同时在查询时,会先从缓存中获取,若不存在才会执行方法。
该注解主要有下面几个参数:
- value、cacheNames:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存存储的集合名。由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必须有的value属性,也成为非必需项了
- key:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:@Cacheable(key = “#p0”):使用函数第一个参数作为缓存的key值
- condition:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:@Cacheable(key = “#p0”, condition = “#p0.length() < 3”),表示只有当第一个参数的长度小于3的时候才会被缓存
- unless:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断。
- keyGenerator:用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现org.springframework.cache.interceptor.KeyGenerator接口,并使用该参数来指定。需要注意的是:该参数与key是互斥的
- cacheManager:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用
- cacheResolver:用于指定使用那个缓存解析器,非必需。需通过org.springframework.cache.interceptor.CacheResolver接口来实现自己的缓存解析器,并用该参数指定。
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
@Autowired
private UserService userService;
/**
* Cacheable:触发缓存填充的操作,检查缓存是否存在,存在则取缓存数据,直接返回,不存在则查数据库写入缓存
* value:缓存的名称,每个缓存名称下可以有多个key
* key:缓存的key
* condition:设置缓存的判断条件,当返回值不为 null 时才存缓存
* unless:与condition相反,满足条件则不缓存
* @param id
* @return
*/
@Cacheable(value = "userCache", key = "#id", unless = "#result == null")
@GetMapping("/{id}")
public User getById(@PathVariable Long id){
User user = userService.getById(id);
return user;
}
}
- 3、@CachePut
主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用 。简单来说就是用户更新缓存数据。但需要注意的是该注解的value 和 key 必须与要更新的缓存相同,也就是与@Cacheable 相同。示例:
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
@Autowired
private UserService userService;
/**
* CachePut将方法返回值放入缓存
* value:缓存的名称,每个缓存名称下可以有多个key
* key:缓存的key
* @param user
* @return
*/
@CachePut(value = "userCache", key = "#result.id")
@PostMapping
public User save(@RequestBody User user){
userService.save(user);
return user;
}
}
- 4、@CacheEvict
配置于函数上,通常用在删除方法上,用来从缓存中移除相应数据。除了同@Cacheable一样的参数之外,它还有下面两个参数:
allEntries:非必需,默认为false。当为true时,会移除所有数据。如:@CachEvict(value=”testcache”,allEntries=true)
beforeInvocation:非必需,默认为false,会在调用方法之后移除数据。当为true时,会在调用方法之前移除数据。 如:@CachEvict(value=”testcache”,beforeInvocation=true)
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
@Autowired
private UserService userService;
/**
* CacheEvict:清理指定缓存
* value:缓存的名称,每个缓存名称下可以有多个key
* key:缓存的key
* @param id
*/
@CacheEvict(value = "userCache", key = "#id")
//@CacheEvict(value = "userCache", key = "#p0")
//@CacheEvict(value = "userCache", key = "#root.args[0]")
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id){
userService.removeById(id);
}
}
- 5、@CacheConfig
统一配置本类的缓存注解的属性,在类上面统一定义缓存的名字,方法上面就不用标注了,当标记在一个类上时则表示该类所有的方法都是支持缓存的
@RestController
@RequestMapping("/user")
@Slf4j
@CacheConfig(cacheNames = {"userCache"})
public class UserController {
@Autowired
private UserService userService;
/**
* CacheEvict:清理指定缓存
* value:缓存的名称,每个缓存名称下可以有多个key
* key:缓存的key
* @param id
*/
@CacheEvict(key = "#id")
//@CacheEvict(value = "userCache", key = "#p0")
//@CacheEvict(value = "userCache", key = "#root.args[0]")
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id){
userService.removeById(id);
}
}
5、SpEL上下文数据
Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档:
注意:
1.当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。 如
@Cacheable(key = "targetClass + methodName +#p0")
2、使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。 如:
@Cacheable(value="users", key="#id")
@Cacheable(value="users", key="#p0")
3、SpEL提供了多种运算符
6、自定义序列化
测试过程中发现写入缓存的数据为乱码,可以通过自定义装配来
- 装配序列化类型
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
// 配置redisTemplate
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());//key序列化
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());//value序列化
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
- 装配过期时间
/**
* 通过RedisCacheManager配置过期时间
*
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1)); // 设置缓存有效期一小时
return RedisCacheManager
.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
.cacheDefaults(redisCacheConfiguration).build();
}
- 一个比较完整的装配类 demo
/**
* 自定义缓存配置文件,继承 CachingConfigurerSupport
* Created by huanl on 2017/8/22.
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport{
public RedisConfig() {
super();
}
/**
* 指定使用哪一种缓存
* @param redisTemplate
* @return
*/
@Bean
public CacheManager cacheManager(RedisTemplate<?,?> redisTemplate) {
RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
return rcm;
}
/**
* 指定默认的key生成方式
* @return
*/
@Override
public KeyGenerator keyGenerator() {
KeyGenerator keyGenerator = new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... objects) {
StringBuilder sb = new StringBuilder();
sb.append(o.getClass().getName());
sb.append(method.getName());
for (Object obj : objects) {
sb.append(obj.toString());
}
return sb.toString();
}
};
return keyGenerator;
}
@Override
public CacheResolver cacheResolver() {
return super.cacheResolver();
}
@Override
public CacheErrorHandler errorHandler() {
return super.errorHandler();
}
/**
* redis 序列化策略 ,通常情况下key值采用String序列化策略
* StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。StringRedisSerializer
* RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。JdkSerializationRedisSerializer
* @param factory
* @return
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory){
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
// // 使用Jackson2JsonRedisSerialize 替换默认序列化
// Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
// ObjectMapper om = new ObjectMapper();
// om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
// jackson2JsonRedisSerializer.setObjectMapper(om);
//
//
// //设置value的序列化方式
// redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// //设置key的序列化方式
// redisTemplate.setKeySerializer(new StringRedisSerializer());
// redisTemplate.setHashKeySerializer(new StringRedisSerializer());
// redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
//使用fastJson作为默认的序列化方式
GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
redisTemplate.setDefaultSerializer(genericFastJsonRedisSerializer);
redisTemplate.setValueSerializer(genericFastJsonRedisSerializer);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(genericFastJsonRedisSerializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
/**
* 转换返回的object为json
* @return
*/
@Bean
public HttpMessageConverters fastJsonHttpMessageConverters(){
// 1、需要先定义一个converter 转换器
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
// 2、添加fastJson 的配置信息,比如:是否要格式化返回的json数据
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
// 3、在convert 中添加配置信息
fastConverter.setFastJsonConfig(fastJsonConfig);
// 4、将convert 添加到converters当中
HttpMessageConverter<?> converter = fastConverter;
return new HttpMessageConverters(converter);
}
}
出自:https://www.cnblogs.com/ejiyuan/p/11014765.html
参考文档:https://blog.csdn.net/qq_26820793/article/details/125139773