启动类在加上注解 @EnableCaching
开启缓存注解,主要适用于整个接口返回结果需要缓存的场景,其他情况,由于业务场景比较复杂,也就是单独某段代码需要缓存,使用redis的客户端会更加灵活。@Cacheable 的cacheNames可以理解为缓存key的前缀或者一级目录(redis可视化工具下)。
引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--这边用的是Redis缓存,所以加上这个依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
@Configuration
@Slf4j
public class ActivityRedisConfig extends CachingConfigurerSupport {
@Bean
@SuppressWarnings(value = {"unchecked", "rawtypes"})
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
/**
* 使用入参的所有参数作为缓存的key
* @return
*/
@Bean
public KeyGenerator activityKeyGenerator() {
return (target, method, params) -> {
System.out.println(params);
String key = "";
try {
// params[0] 为@RequestBody接收的对象,这里只针对这种情况做处理,
// 如果接口还有其他接收参数的形式,比如@pathvariable,
//@PathVariable结合@RequestParam等请自行测试再添加相关逻辑代码,这里就没去验证了
key = getKey(params[0]);
} catch (IllegalAccessException e) {
log.info("缓存key生成失败", e);
}
return key;
};
}
/**
* 自定义缓存管理器
*/
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
// 设置@cacheable 序列化方式,方便可视化工具查看
config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer)).entryTtl(Duration.ofMinutes(5));
Set<String> cacheNames = new HashSet<>();
cacheNames.add("测试");
cacheNames.add("distributor");
ConcurrentHashMap<String, RedisCacheConfiguration> configMap = new ConcurrentHashMap<>();
configMap.put("测试", config.entryTtl(Duration.ofMinutes(1L)));
configMap.put("distributor", config.entryTtl(Duration.ofMinutes(50L)));
//需要先初始化缓存名称,再初始化其它的配置。
RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(config)
.initialCacheNames(cacheNames).withInitialCacheConfigurations(configMap).build();
return cacheManager;
}
public String getKey(Object object) throws IllegalAccessException {
String s = null;
Field[] fields = object.getClass().getDeclaredFields();
s = getField(fields, object, s);
Field[] fieldsSuper = object.getClass().getSuperclass().getDeclaredFields();
s = getField(fieldsSuper, object, s);
return s;
}
public String getField(Field[] fields, Object object, String s) throws IllegalAccessException {
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
field.setAccessible(true);
if (field.getName().equals("serialVersionUID")) continue;
String ss = field.getName() + "_" + String.valueOf(field.get(object));
s = s == null ? ss : s + "_" + ss;
}
return s;
}
}
方式一:
@PostMapping("users/info")
@Cacheable(cacheNames = "测试", key = "#user.id", unless = "!#result?.result")
@CapCode
public ResultMessage info(@RequestBody User user) {
System.out.println("controller[url=users/login]");
return ResultMessage.success(user);
}
方式二:自定义keyGenerator
@PostMapping("users/info")
@Cacheable(cacheNames = "测试", keyGenerator = "activityKeyGenerator", unless = "!#result?.result")
@CapCode
public ResultMessage info(@RequestBody User user) {
System.out.println("controller[url=users/login]");
return ResultMessage.success(user);
}
其中unless的使用非常重要,决定什么情况下使用缓存,一般是接口查询成功才使用缓存,失败了就不需要缓存,具体使用看文章底部参考文章,另外说一点unless处理boolean类型时,表达式返回结果是false才缓存,上文接口处理成功,unless = "!#result?.result"。
返回封装如下:
public class ResultMessage<T> implements Serializable {
/**
*
*/
private static final long serialVersionUID = 3443815263986524969L;
public ResultMessage() {
}
/**
* @param result
* @param code
* @param msg
* @param data
*/
public ResultMessage(Boolean result, int code, String msg, T data) {
this.result = result;
this.code = code;
this.msg = msg;
this.data = data;
}
/**
* 结果,true 成功,false 失败
*/
private Boolean result = true;
/**
* 编码
*/
private int code;
/**
* 提示信息
*/
private String msg;
/**
* 业务数据
*/
private T data;
/**
* 成功
*
* @param msg
* @param data
* @return
*/
public static <TT> ResultMessage<TT> success(String msg, TT data) {
return new ResultMessage<TT>(Boolean.TRUE, Constants.SUCCESS_CODE, msg, data);
}
/**
* 成功
*
* @param data
* @return
*/
public static <TT> ResultMessage<TT> success(TT data) {
return new ResultMessage<TT>(Boolean.TRUE, Constants.SUCCESS_CODE, Constants.RET_SUCCESS_MESSAGE, data);
}
/**
* 成功
*
* @param msg
* @return
*/
public static <TT> ResultMessage<TT> success(String msg) {
return new ResultMessage<TT>(Boolean.TRUE, Constants.SUCCESS_CODE, msg, null);
}
/**
* 失败
*
* @param msg
* @param data
* @return
*/
public static <TT> ResultMessage<TT> fail(String msg, TT data) {
return new ResultMessage<TT>(Boolean.FALSE, Constants.FAIL_CODE, msg, data);
}
/**
* 失败
*
* @param data
* @return
*/
public static <TT> ResultMessage<TT> fail(TT data) {
return new ResultMessage<TT>(Boolean.FALSE, Constants.FAIL_CODE, Constants.RET_FAIL_MESSAGE, data);
}
/**
* 失败
*
* @param msg
* @return
*/
public static <TT> ResultMessage<TT> fail(String msg) {
return new ResultMessage<TT>(Boolean.FALSE, Constants.FAIL_CODE, msg, null);
}
/**
* 异常
*
* @param msg
* @param data
* @return
*/
public static <TT> ResultMessage<TT> error(String msg, TT data) {
return new ResultMessage<TT>(Boolean.FALSE, Constants.ERROR_CODE, msg, data);
}
/**
* 异常
*
* @param data
* @return
*/
public static <TT> ResultMessage<TT> error(TT data) {
return new ResultMessage<TT>(Boolean.FALSE, Constants.ERROR_CODE, Constants.RET_ERROR_MESSAGE, data);
}
/**
* 异常
*
* @param msg
* @return
*/
public static <TT> ResultMessage<TT> error(String msg) {
return new ResultMessage<TT>(Boolean.FALSE, Constants.ERROR_CODE, msg, null);
}
/**
* 返回结果
*
* @param result
* @param code
* @param msg
* @param data
* @return
*/
public static <TT> ResultMessage<TT> result(Boolean result, int code, String msg, TT data) {
return new ResultMessage<TT>(result, code, msg, data);
}
public Boolean getResult() {
return result;
}
public void setResult(Boolean result) {
this.result = result;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString() {
return "ResultMessage{" +
"result=" + result +
", code=" + code +
", msg='" + msg + '\'' +
", data=" + data +
'}';
}
}
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> {
@SuppressWarnings("unused")
private ObjectMapper objectMapper = new ObjectMapper();
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
static {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
}
public FastJson2JsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return JSON.parseObject(str, clazz);
}
public void setObjectMapper(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "'objectMapper' must not be null");
this.objectMapper = objectMapper;
}
protected JavaType getJavaType(Class<?> clazz) {
return TypeFactory.defaultInstance().constructType(clazz);
}
}
参考: