SpringBoot2集成RedisCache

Posted 疯狂的妞妞

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot2集成RedisCache相关的知识,希望对你有一定的参考价值。

应用层的东西,找到接口实现它即可。

如果想要自己选择序列化工具,难点还是在自动转型上,在字符串转成对象的过程中,Spring并未提供有效的、带Class参数的接口,类型自动转换问题,需要第三方框架自行处理。
最好选用带自动转型的序列化框架,错误的写法,很容易导致类型强转失败,本文采用的是FastJSON。

 

简单的工具类,按自己需求封装,可以指定各种数据类型序列化格式。

/**
 * @author ChenSS
 * @date    2018年7月13日  v1
 *          2019年10月16日 v2  优化日期
 */
public class FastJsonUtils {
    public static final SerializeConfig serializeConfig;

    static {
        serializeConfig = new SerializeConfig();
        FastJsonDateSerializer dateTimeSerializer = new FastJsonDateSerializer("yyyy-MM-dd HH:mm:ss");
        serializeConfig.put(Date.class, dateTimeSerializer);
        serializeConfig.put(java.sql.Timestamp.class, dateTimeSerializer);
        serializeConfig.put(java.sql.Date.class, new FastJsonDateSerializer("yyyy-MM-dd"));
        serializeConfig.put(java.sql.Time.class, new FastJsonDateSerializer("HH:mm:ss"));

//        // 使用和json-lib兼容的日期输出格式
//        config.put(java.util.Date.class, new JSONLibDataFormatSerializer());
//        config.put(java.sql.Date.class, new JSONLibDataFormatSerializer());
    }
}

FastJson2JsonRedisSerializer

很多人写这个类,完全模仿Jackson的写法,其实没必要,明确自己的需求,保留最少的代码即可。

我泛型直接写Object,因为代码已经能够处理全部类型的数据了。

import cn.seaboot.common.core.FastJsonUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

import java.nio.charset.Charset;

/**
 * @author Mr.css
 * @date 2020/1/2 11:24
 */
public class FastJson2JsonRedisSerializer implements RedisSerializer<Object> {
  @Override
  public byte[] serialize(Object o) throws SerializationException {
    if (o == null) {
      return new byte[0];
    } else {
      return JSON.toJSONString(o, FastJsonUtils.serializeConfig, SerializerFeature.WriteClassName).getBytes(Charset.defaultCharset());
    }
  }

  @Override
  public Object deserialize(byte[] bytes) throws SerializationException {
    if (bytes == null || bytes.length <= 0) {
      return null;
    } else {
      return JSON.parse(new String(bytes, Charset.defaultCharset()));
    }
  }
}

RedisConfig

如果在使用Cache注解的时候有写key的习惯,KeyGenerator 可以不需要配置,我这里把函数名和所有的参数拼在一起,做成默认的Key值。

import cn.seaboot.common.core.Converter;
import com.alibaba.fastjson.parser.ParserConfig;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
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.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import javax.annotation.Resource;
import java.time.Duration;

/**
 * @author Mr.css on 2019/12/26
 * @date 2019/12/31
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

  @Resource
  private LettuceConnectionFactory lettuceConnectionFactory;

  /**
   * Cache注解可以不指定key,需要有默认策略,按需调整
   */
  @Bean
  @Override
  public KeyGenerator keyGenerator() {
    return (target, method, params) -> {
      StringBuilder sb = new StringBuilder();
      sb.append(method.getName());
      if(params.length > 0){
        for (int i = 1; i < params.length; i++) {
          sb.append(Converter.toString(params[i]));
        }
      }
      return sb.toString();
    };
  }

  @Bean
  @Override
  public CacheManager cacheManager() {
    RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(lettuceConnectionFactory);

    FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer();
    RedisSerializationContext.SerializationPair<Object> pair =
        RedisSerializationContext.SerializationPair.fromSerializer(serializer);
    RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
    //设置过期时间 30天
    defaultCacheConfig = defaultCacheConfig.entryTtl(Duration.ofDays(30));
    //初始化RedisCacheManager
    RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
    //反序列化白名单
    ParserConfig.getGlobalInstance().addAccept("cn.seaboot.admin.bean.");
    return cacheManager;
  }
}

yml

  redis:
    host: 127.0.0.1
    port: 6379
    timeout: 1000
    jedis:
      pool:
        min-idle: 1
        max-idle: 8
        max-wait: 5000

Maven

        <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>

 

补充

集成RedisCache,上面代码已经足够,这里介绍FastJSON的一些问题。

FastJSON自动转型的写法

  public static void main(String[] args) {
    ParserConfig.getGlobalInstance().addAccept("cn.swsk.xbry.entity");
    TUserInfoEntity entity = new TUserInfoEntity();
    entity.setId("13123");
    //使用 SerializerFeature.WriteClassName 可以让JSON自动转型
    //不依赖 clazz 参数也达到 JSON.parseObject(String json, Class<T> clazz) 相同效果
    String str = JSON.toJSONString(entity, SerializerFeature.WriteClassName);
    System.out.println(JSON.parse(str).getClass());
  }

ParserConfig.getGlobalInstance()的必要性

FastJSON最初是没有白名单这个要求的,addAccept接口的设计源自于系统漏洞。

假设去除掉白名单的设计,在知道全类名的情况下,通过Http接口即可创建出系统的任何对象。

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

/**
 * @author Mr.css
 * @date 2020/1/6
 */
public class Test {
  public static void main(String[] args) {
    //在没有 ParserConfig.getGlobalInstance() 的情况下,只要知道全类名,即可 new 出程序中任何一个对象
    ParserConfig.getGlobalInstance().addAccept("cn.swsk.xbry.entity");
    //下列这行代码,等效于Class.forName().newInstance()
    //也就是说,在使用FastJSON的系统中,但凡用到@ResponseBody的接口,都可以通过外部接口利用此bug
    System.out.println(JSON.parse("{"@type":"cn.swsk.xbry.entity.TUserInfoEntity"}").getClass());
  }
}

 

以上是关于SpringBoot2集成RedisCache的主要内容,如果未能解决你的问题,请参考以下文章

springboot2.0+mybatis多数据源集成

SpringBoot2.0 基础案例(13):基于Cache注解模式,管理Redis缓存

SpringBoot2.6.3集成quartz

SpringBoot2.6.3集成quartz

SpringBoot2.6.3集成quartz

SpringBoot2.x集成MQTT实现消息订阅(附源码)