一.基础配置和初步使用cacheable
1.首先准备工作,启动mysql数据库和redis服务(根据个人情况),配置springboot项目。
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://127.0.0.1:3306/salong
type: com.alibaba.druid.pool.DruidDataSourceC3P0Adapter
driver-class-name: com.mysql.jdbc.Driver
redis:
port: 6379
host: localhost
password: 123456
database: 5
server:
port: 8090
2.在springboot启动类上加上 @EnableCaching 注解,开启缓存。
3.模拟写一个controller,代码如下:
package com.salong.aspect.test;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
@RestController
@RequestMapping("/aspect")
public class index {
@RequestMapping("cache")
//其中cacheNames必须指定,用于redis的key构造,key用于区分不同的key
//如果a=1,b=2,那么redis会生成一个user3::12的key,value为此方法返回的值的json格式
//修改参数a或b,cache会认定为新的cache,会继续往redis加新的缓存
//condition为满足条件时候才缓存,这里满足a不为空并且长度大于3,才缓存
//unless为满足条件时候不缓存,这里只要b为空就不缓存
@Cacheable(cacheNames = "user3",key = "#a+''+#b",condition = "#a!=null && #a.length()>3" ,unless = "#b==null")
public String cache(String a,String b) {
String cache = service.getCache();
return cache;
}
}
class service {
public static String getCache(){
//这里模拟数据库来的数据,每调用一次,控制台打印一次service被调用
String s=""+new Date();
System.out.println("service被调用");
return s;
}
}
3.访问localhost:8090/aspect/cache?a=1&b=2,会调用service,第二次如果参数不变,那么将不再调用service,会去调用redis的user3::12这个key。
查看redis,也的确找得到这个key。
二. 设置过期时间
经过第一步的@Cacheable注解,我们已经可以在redis里面存缓存信息了,那么问题来了,每个key都是永久存在的,后期redis的缓存管理是个很大的问题,所以要对key设置过期时间的,而且对于不同的业务,最好是不同的缓存时间也是不同的,所以这一步处理不同的key设置过期时间。
1.首先建议先把第一步的springboot启动类加的@EnableCaching 注解删除掉,单独配置一个配置类,来设置过期时间。
package com.salong.myself.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
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.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author Salong
* @date 2021/3/18 18:55
* @Email:salong0503@aliyun.com
*/
@Configuration
@EnableCaching
public class RedisCacheConfig {
/**
* 自定义缓存管理器
* 在下面configMap中配置springCache每个模块的过期时间
*/
private Duration timeToLive = Duration.ZERO;
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance ,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题)
ConcurrentHashMap<String, RedisCacheConfiguration> configMap = new ConcurrentHashMap<>();
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(timeToLive)
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.disableCachingNullValues();
//user模块的key有效期30秒
configMap.put("user", config.entryTtl(Duration.ofSeconds(30L)));
//user2模块key永久有效
configMap.put("user2", config);
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.withInitialCacheConfigurations(configMap)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
这样配置,只要使用了@Cacheable注解并且cacheNames是user1的,存入redis缓存中30秒将会被清理掉,而user2的则永久不会被清理。
三.Cacheable,CachePut和CacheEvit的区别
在CRUD中,Cacheable用于查操作的缓存数据,CachePut用于更新缓存信息,而CacheEvit删除缓存,用于删除操作,删除数据库数据的同时删除缓存。
Cacheable不保证方法体能调用,即第一次使用参数访问时候,访问的方法体访问数据库,并且缓存,第二次使用相同参数的时候,将不再执行方法体的代码,直接通过Cacheable去缓存拿数据。
而CachePut每次都会执行方法体,对数据库进行操作的时候,同时更新缓存信息。更新后的数据,Cacheable也是可以访问到的。
四.CachePut注解的使用
待续