SpringBoot 缓存之 @Cacheable使用示例

启动类在加上注解 @EnableCaching 开启缓存注解,主要适用于整个接口返回结果需要缓存的场景,其他情况,由于业务场景比较复杂,也就是单独某段代码需要缓存,使用redis的客户端会更加灵活。@Cacheable 的cacheNames可以理解为缓存key的前缀或者一级目录(redis可视化工具下)。

引入依赖:

        
            org.springframework.boot
            spring-boot-starter-cache
        
        
        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        

@Configuration
@Slf4j
public class ActivityRedisConfig extends CachingConfigurerSupport {

    @Bean
    @SuppressWarnings(value = {"unchecked", "rawtypes"})
    public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate 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 cacheNames = new HashSet<>();
        cacheNames.add("测试");
        cacheNames.add("distributor");
        ConcurrentHashMap 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 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  ResultMessage success(String msg, TT data) {
        return new ResultMessage(Boolean.TRUE, Constants.SUCCESS_CODE, msg, data);
    }

    /**
     * 成功
     *
     * @param data
     * @return
     */
    public static  ResultMessage success(TT data) {
        return new ResultMessage(Boolean.TRUE, Constants.SUCCESS_CODE, Constants.RET_SUCCESS_MESSAGE, data);
    }

    /**
     * 成功
     *
     * @param msg
     * @return
     */
    public static  ResultMessage success(String msg) {
        return new ResultMessage(Boolean.TRUE, Constants.SUCCESS_CODE, msg, null);
    }

    /**
     * 失败
     *
     * @param msg
     * @param data
     * @return
     */
    public static  ResultMessage fail(String msg, TT data) {
        return new ResultMessage(Boolean.FALSE, Constants.FAIL_CODE, msg, data);
    }

    /**
     * 失败
     *
     * @param data
     * @return
     */
    public static  ResultMessage fail(TT data) {
        return new ResultMessage(Boolean.FALSE, Constants.FAIL_CODE, Constants.RET_FAIL_MESSAGE, data);
    }

    /**
     * 失败
     *
     * @param msg
     * @return
     */
    public static  ResultMessage fail(String msg) {
        return new ResultMessage(Boolean.FALSE, Constants.FAIL_CODE, msg, null);
    }

    /**
     * 异常
     *
     * @param msg
     * @param data
     * @return
     */
    public static  ResultMessage error(String msg, TT data) {
        return new ResultMessage(Boolean.FALSE, Constants.ERROR_CODE, msg, data);
    }

    /**
     * 异常
     *
     * @param data
     * @return
     */
    public static  ResultMessage error(TT data) {
        return new ResultMessage(Boolean.FALSE, Constants.ERROR_CODE, Constants.RET_ERROR_MESSAGE, data);
    }

    /**
     * 异常
     *
     * @param msg
     * @return
     */
    public static  ResultMessage error(String msg) {
        return new ResultMessage(Boolean.FALSE, Constants.ERROR_CODE, msg, null);
    }

    /**
     * 返回结果
     *
     * @param result
     * @param code
     * @param msg
     * @param data
     * @return
     */
    public static  ResultMessage result(Boolean result, int code, String msg, TT data) {
        return new ResultMessage(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 implements RedisSerializer {
    @SuppressWarnings("unused")
    private ObjectMapper objectMapper = new ObjectMapper();

    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

    private Class clazz;

    static {
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
    }

    public FastJson2JsonRedisSerializer(Class 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);
    }
}

参考:

redis@Cacheable注解unless用法

Springboot 之 @Cacheable 各种序列化及有效时间设置

你可能感兴趣的:(SpringBoot,缓存,java,spring,boot)