springboot中使用redis

Posted zhenghuasheng

tags:

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

springboot中使用redis

有关redis的基本知识不在此阐述,redis相关注解的使用请查看另外一篇文章:Spring Cache集成redis

直接上代码:

step 1:加入pom依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

step 2:创建redis配置
springboot配置文件中加入redis连接配置

spring.redis.database=0
spring.redis.host=
spring.redis.password=
spring.redis.pool.max-active=100
spring.redis.pool.max-idle=8
spring.redis.pool.max-wait=-1
spring.redis.pool.min-idle=0
spring.redis.port=6379
#spring.redis.sentinel.master= # Name of Redis server.
#spring.redis.sentinel.nodes= # Comma-separated list of host:port pairs.
spring.redis.timeout=10000

/**
 *
 * @author zhenghuasheng
 * @date 2016/5/9
 */

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport 

    @Value(value = "$spring.redis.password")
    private String redisPassword;

    @Bean
    public KeyGenerator localKeyGenerator()
        return (target, method, params) -> 
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName());
            sb.append(method.getName());
            for (Object obj : params) 
                if (obj != null) 
                    sb.append(obj.toString());
                
            
            return sb.toString();
        ;
    

    @Bean
    public CacheManager cacheManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) 
        RedisCacheManager redisCacheManager =  new RedisCacheManager(redisTemplate);
        redisCacheManager.setUsePrefix(true);
        redisCacheManager.setDefaultExpiration(1800L);
        return redisCacheManager;
    

    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) 
        StringRedisTemplate template = new StringRedisTemplate(factory);

        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//        JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    
//    @Bean
//    public JedisConnectionFactory redisConnectionFactory() 
//        JedisConnectionFactory factory = new JedisConnectionFactory();
//        factory.setPassword(redisPassword);
//        factory.setPoolConfig(new JedisPoolConfig());
        factory.setHostName(host);
        factory.setPort(port);
        factory.setTimeout(timeout); //设置连接超时时间
//        return factory;
//    

说明:@EnableCaching注解是spring framework中的注解驱动的缓存管理功能。自spring版本3.1起加入了该注解。如果你使用了这个注解,那么你就不需要在XML文件中配置cache manager了。

当你在配置类(@Configuration)上使用@EnableCaching注解时,会触发一个post processor,这会扫描每一个spring bean,查看是否已经存在注解对应的缓存。如果找到了,就会自动创建一个代理拦截方法调用,使用缓存的bean执行处理。

localKeyGenerator():为自定义缓存key的生成方法。
redisConnectionFactory():可以自定义factory的属性,也可以使用默认配置
redisTemplate(RedisConnectionFactory factory):此处配置redis的序列化配置,
当我们的数据存储到Redis的时候,我们的键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。RedisTemplate默认使用的是JdkSerializationRedisSerializer,StringRedisTemplate默认使用的是StringRedisSerializer。

Spring Data JPA为我们提供了下面的Serializer:GenericToStringSerializer、Jackson2JsonRedisSerializer、JacksonJsonRedisSerializer、JdkSerializationRedisSerializer、OxmSerializer、StringRedisSerializer。

序列化方式对比:

JdkSerializationRedisSerializer: 使用JDK提供的序列化功能。 优点是反序列化时不需要提供类型信息(class),但缺点是需要实现Serializable接口,还有序列化后的结果非常庞大,是JSON格式的5倍左右,这样就会消耗redis服务器的大量内存。
Jackson2JsonRedisSerializer: 使用Jackson库将对象序列化为JSON字符串。优点是速度快,序列化后的字符串短小精悍,不需要实现Serializable接口。但缺点也非常致命,那就是此类的构造函数中有一个类型参数,必须提供要序列化对象的类型信息(.class对象)。 通过查看源代码,发现其只在反序列化过程中用到了类型信息。

在此,我们使用的是StringRedisTemplate
StringRedisTemplate template = new StringRedisTemplate(factory);

这会有个问题,序列化会报类型转换错误,如XXX类不能转换成String。

问题分析:
使用StringRedisSerializer做key的序列化时,StringRedisSerializer的泛型指定的是String,传其他对象就会报类型转换错误,在使用@Cacheable注解是key属性就只能传String进来。把这个序列化方式重写了,将泛型改成Object。源码:

/**
 * @author zhenghuasheng
 * @date 2018/8/23.16:49
 */
public class StringRedisSerializer implements RedisSerializer<Object> 
    private final Charset charset;

    private final String target = "\\"";

    private final String replacement = "";

    public StringRedisSerializer() 
        this(Charset.forName("UTF8"));
    

    public StringRedisSerializer(Charset charset) 
        Assert.notNull(charset, "Charset must not be null!");
        this.charset = charset;
    

    @Override
    public String deserialize(byte[] bytes) 
        return (bytes == null ? null : new String(bytes, charset));
    

    @Override
    public byte[] serialize(Object object) 
        if (object == null) 
            return null;
        
        String string = JSON.toJSONString(object);
        string = string.replace(target, replacement);
        return string.getBytes(charset);
    

value的序列化使用jackson2JsonRedisSerializer
template.setValueSerializer(jackson2JsonRedisSerializer);
这里可能还有个不是问题的问题,如果存储的class中 字段无set方法时,会出现反序列化失败,在此我们为了避免该问题,可以将value的序列化使用fastjson来实现

/**
 * @author zhenghuasheng
 * @date 2018/8/23.20:28
 */
public class FastJsonRedisSerializer<T> implements RedisSerializer<T>

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

    private Class<T> clazz;

    public FastJsonRedisSerializer(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).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 (T) JSON.parseObject(str, clazz);
    

更改对应的配置

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) 
    RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(redisConnectionFactory);

FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
// 全局开启AutoType,不建议使用
// ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
// 建议使用这种方式,小范围指定白名单
ParserConfig.getGlobalInstance().addAccept("com.jz");

// 设置值(value)的序列化采用FastJsonRedisSerializer。
redisTemplate.setValueSerializer(fastJsonRedisSerializer);
redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
// 设置键(key)的序列化采用StringRedisSerializer。
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());

redisTemplate.afterPropertiesSet();
return redisTemplate;

redis的相关配置已经完成,可以使用相关注解或 RedisTemplate 来操作redis!!!

以上是关于springboot中使用redis的主要内容,如果未能解决你的问题,请参考以下文章

springboot中使用RedisTemplate实现redis数据缓存

为啥springboot中使用redis不配置会报错

springboot中使用redis

springboot中使用redis

SpringBoot :Spring boot 中 Redis 的使用

springboot 集成redis 和http请求