美文网首页springboot系列一
SpringBoot>11 - 集成 Redis

SpringBoot>11 - 集成 Redis

作者: cpla | 来源:发表于2019-01-17 11:16 被阅读0次

    简介:

    Redis 作为一个nosql 数据库,在项目中的作用是非常重要的。本章将介绍 springboot1.5 集成 Redis的基础知识。

    学习链接: Redis中文网:http://www.redis.cn
    Redis的下载,安装,以及客户端在这里都可以找到对应入口。可自行下载安装,我使用的是阿里云docker 镜像安装,Docker 系列会涉及。

    一、Redis的五种数据类型(来自官网)

    1、String(字符串): 一个key对应一个value, 可以包含任何数据。比如jpg图片或者序列化的对象。最大能储存512MB。
    2、Hash(哈希): 键值对集合,类似对象,

    redis> HMSET person field1 "Hello" field2 "World"
    redis> HGET person field1"Hello"
    redis> HGET person field2"World"
    

    3、List(列表): 简单的字符串列表,按照插入的顺序排序,可以添加一个字符串到头部或者尾部。

    redis 127.0.0.1:6379> lpush runoob redis(integer) 1
    redis 127.0.0.1:6379> lpush runoob mongodb(integer) 2
    redis 127.0.0.1:6379> lpush runoob rabitmq(integer) 3
    redis 127.0.0.1:6379> lrange runoob 0 10
    1) "rabitmq"
    2) "mongodb"
    3) "redis"
    redis 127.0.0.1:6379>
    

    4、Set(集合): String类型的无序集合。注意集合中的元素是无序但唯一的。重复元素插入将被忽略。
    5、Zset(Socted set 有序集合): String 类型元素的集合,但元素可以重复。

    redis 127.0.0.1:6379> zadd runoob 0 redis(integer) 1
    redis 127.0.0.1:6379> zadd runoob 0 mongodb(integer) 1
    redis 127.0.0.1:6379> zadd runoob 0 rabitmq(integer) 1
    redis 127.0.0.1:6379> zadd runoob 0 rabitmq(integer) 0
    redis 127.0.0.1:6379> > ZRANGEBYSCORE runoob 0 1000
    1) "mongodb"
    2) "rabitmq"
    3) "redis"
    

    二、SpringBoot集成Redis

    1、pom依赖:(使用的是1.5.1版本的springboot,所以依赖选择带有data的)

    <!-- Spring Boot Redis 依赖 -->
    <!-- Spring Boot 1.47以后 -->
    <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!-- Spring Boot 1.47以前 -->
    <!--<dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-redis</artifactId>
    </dependency>-->
    

    2、application.properties文件中添加

    ## Redis 配置
    ## Redis数据库索引(默认为0)
    spring.redis.database=0
    ## Redis服务器地址
    spring.redis.host=127.0.0.1
    ## Redis服务器连接端口
    spring.redis.port=6379
    ## Redis服务器连接密码(默认为空)
    spring.redis.password=
    ## 连接池最大连接数(使用负值表示没有限制)
    spring.redis.pool.max-active=8
    ## 连接池最大阻塞等待时间(使用负值表示没有限制)
    spring.redis.pool.max-wait=-1
    ## 连接池中的最大空闲连接
    spring.redis.pool.max-idle=8
    ## 连接池中的最小空闲连接
    spring.redis.pool.min-idle=0
    ## 连接超时时间(毫秒)
    spring.redis.timeout=3000
    

    因为springboot引入了redis的starter,完成以上配置redis可以正常使用了。个性化的东西可以添加到 redis的配置类中,请看下文Redis自定义配置。

    三、Redis 操作

    1、源码:我们搜索 RedisAutoConfiguration 类:

    @Configuration
    @ConditionalOnClass({JedisConnection.class, RedisOperations.class, Jedis.class})
    @EnableConfigurationProperties({RedisProperties.class})
    public class RedisAutoConfiguration {
         ...
        @Configuration
        protected static class RedisConfiguration {
            protected RedisConfiguration() {
            }
            @Bean
            @ConditionalOnMissingBean(
                name = {"redisTemplate"}
            )
            public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
                RedisTemplate template = new RedisTemplate();
                template.setConnectionFactory(redisConnectionFactory);
                return template;
            }
            @Bean
            @ConditionalOnMissingBean({StringRedisTemplate.class})
            public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
                StringRedisTemplate template = new StringRedisTemplate();
                template.setConnectionFactory(redisConnectionFactory);
                return template;
            }
        }
        ...
    }
    

    发现:这里引入了Jedis 的操作、注入了redisTemplatestringRedisTemplate。使用的时候只需要自动注入即可。

    2、测试:使用@Test 测试

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class SpringbootApplicationTests {
        /**
         * RedisAutoConfiguration 中注入 :
         * redisTemplate  : k - v 操作对象的
         * stringRedisTemplate : k - v 操作字符串的
         */
        @Autowired
        private RedisTemplate redisTemplate;
        @Autowired
        private StringRedisTemplate stringRedisTemplate;
    
        /**
         * 操作String 使用 stringRedisTemplate 
         * stringRedisTemplate.opsForValue() 操作String
         * stringRedisTemplate.opsForList()   操作list 列表
         * stringRedisTemplate.opsForSet()     操作set集合
         * stringRedisTemplate.opsForHash()   操作hash散列
         * stringRedisTemplate.opsForZSet()   操作有序集合
         *
         * 后面直接跟上 redis的命令即可。
         */
        @Test
        public void test01(){
            // 操作String
    //        stringRedisTemplate.opsForValue().append("str1","hello");
    //        String str1 = stringRedisTemplate.opsForValue().get("str1");
    //        // >>>>>>>>>>>>>>>>>>str: hellohello  原因: 执行了两次 追加。
    //        log.info(">>>>>>>>>>>>>>>>>>str: {}",str1);
    
            // 操作list 列表 
            stringRedisTemplate.opsForList().leftPush("list1","广东省");
            stringRedisTemplate.opsForList().leftPush("list1","22222");
        }
    
        /**
         * 测试保存对象  使用 redisTemplate
         */
        @Test
        public void test02(){
            User user = new User();
            user.setId(1);
            user.setName("ron");
            redisTemplate.opsForValue().set("person",user);
        }
    }
    

    注意:此处的实体类(User)需实现序列化接口Serializable

    3、redis客户端中出现(类似 \xac\xed\x00\x05t\x00)乱码。

    乱码分析: 查看 RedisTemplate 类源码

    public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
    ...
            // 序列化器
            private RedisSerializer keySerializer = null;
            private RedisSerializer valueSerializer = null;
            private RedisSerializer hashKeySerializer = null;
            private RedisSerializer hashValueSerializer = null;
    ...
            public void afterPropertiesSet() {
                   super.afterPropertiesSet();
                   boolean defaultUsed = false;
                   if(this.defaultSerializer == null) {
                         this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null?this.classLoader:this.getClass().getClassLoader());
                       }...
            }
    }
    

    可以看到 redisTemplate 保存对象默认使用 JDK 序列化机制,序列化后的数据保存到Redis中乱码。

    乱码解决:
    方案1、将对象转为json
    方案2、改变RedisTemplate默认序列化规则。

    /**
     * @Auther: xf
     * @Date: 2018/11/21 22:07
     * @Description: 自定义序列化器
     */
    @Configuration
    public class MyRedisConfig {
    
        @Autowired
        private RedisTemplate redisTemplate;
        
        @Bean
        public RedisTemplate redisTemplateInit() {
            //设置序列化Key的实例化对象
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            //设置序列化Value的实例化对象
            redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
            return redisTemplate;
        }
    }
    

    再次测试,乱码解决。

    四、Redis自定义配置:

    Redis配置类:并非必需,根据自身开发环境要求选取配置

    /**
     * @Auther: admin
     * @Date: 2018/11/20 23:10
     * @Description:  redis 配置类
     */
    @Slf4j
    @Configuration
    @EnableCaching//(缓存支持)
    //继承CachingConfigurerSupport,重写CacheManager方法。
    public class RedisConfig extends CachingConfigurerSupport {
    
        /**
         * 注入 RedisConnectionFactory
         */
        @Autowired
        RedisConnectionFactory redisConnectionFactory;
    
        /**
         * 指定key的生成策略
         * @return KeyGenerator
         */
        @Bean
        public KeyGenerator keyGenerator() {
            return new KeyGenerator() {
                @Override public Object generate(Object target, Method method, Object... params) {
                    StringBuilder sb = new StringBuilder();
                    String[] value = new String[1];
                    // sb.append(target.getClass().getName());
                    // sb.append(":" + method.getName());
                    Cacheable cacheable = method.getAnnotation(Cacheable.class);
                    if (cacheable != null) {
                        value = cacheable.value();
                    }
                    CachePut cachePut = method.getAnnotation(CachePut.class);
                    if (cachePut != null) {
                        value = cachePut.value();
                    }
                    CacheEvict cacheEvict = method.getAnnotation(CacheEvict.class);
                    if (cacheEvict != null) {
                        value = cacheEvict.value();
                    }
                    sb.append(value[0]);
                    //获取参数值
                    for (Object obj : params) {
                        sb.append(":" + obj.toString());
                    }
                    return sb.toString();
                }
            };
        }
    
        /**
         * 实例化 CacheManager 对象,指定使用RedisCacheManager作缓存管理
         *
         * @return CacheManager
         */
        @Bean
        public CacheManager cacheManager(RedisTemplate redisTemplate) {
            RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
            // 设置缓存过期时间(单位:秒),60秒
            rcm.setDefaultExpiration(600);
            return rcm;
        }
    
        /**
         * 实例化 RedisTemplate 对象
         * @return RedisTemplate
         */
        @Bean
        public RedisTemplate<String, Object> functionDomainRedisTemplate() {
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(redisConnectionFactory);
            initDomainRedisTemplate(redisTemplate);
            redisTemplate.afterPropertiesSet();
            return redisTemplate;
        }
    
        /**
         * 设置数据存入 redis 的序列化方式
         * </br>redisTemplate 序列化默认使用的jdkSerializeable, 存储二进制字节码, 所以自定义序列化类
         *
         * @param redisTemplate
         */
        private void initDomainRedisTemplate(
                RedisTemplate<String, Object> redisTemplate) {
            //key序列化方式;(不然会出现乱码;),但是如果方法上有Long等非String类型的话,会报类型转换错误;
            //所以在没有自己定义key生成策略的时候,以下这个代码建议不要这么写,可以不配置或者自己实现ObjectRedisSerializer
            //或者JdkSerializationRedisSerializer序列化方式;
    
            // 使用Jackson2JsonRedisSerialize 替换默认序列化
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(
                    Object.class);
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            //// string结构的数据,设置value的序列化规则和 key的序列化规则
            //StringRedisSerializer解决key中午乱码问题。//Long类型不可以会出现异常信息;
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            //value乱码问题:Jackson2JsonRedisSerializer
            redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
    
            //设置Hash结构的key和value的序列化方式
            //redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);
            //redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        }
    
        /**
         * redis数据操作异常处理
         * 这里的处理:在日志中打印出错误信息,但是放行
         * 保证redis服务器出现连接等问题的时候不影响程序的正常运行,使得能够出问题时不用缓存
         * @return
         */
        @Bean
        @Override public CacheErrorHandler errorHandler() {
            CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {
                @Override public void handleCacheGetError(RuntimeException e,
                                                          Cache cache, Object key) {
                    log.error("redis异常:key=[{}]", key, e);
                }
    
                @Override public void handleCachePutError(RuntimeException e,
                                                          Cache cache, Object key, Object value) {
                    log.error("redis异常:key=[{}]", key, e);
                }
    
                @Override public void handleCacheEvictError(RuntimeException e,
                                                            Cache cache, Object key) {
                    log.error("redis异常:key=[{}]", key, e);
                }
    
                @Override public void handleCacheClearError(RuntimeException e,
                        Cache cache) {
                    log.error("redis异常:", e);
                }
            };
            return cacheErrorHandler;
        }
    
        /**
         * 实例化 ValueOperations 对象,可以使用 String 操作
         * @param redisTemplate
         * @return
         */
        @Bean
        public ValueOperations<String, Object> valueOperations(
                RedisTemplate<String, Object> redisTemplate) {
            return redisTemplate.opsForValue();
        }
    
        /**
         * 实例化 HashOperations 对象,可以使用 Hash 类型操作
         * @param redisTemplate
         * @return
         */
        @Bean
        public HashOperations<String, String, Object> hashOperations(
                RedisTemplate<String, Object> redisTemplate) {
            return redisTemplate.opsForHash();
        }
    
        /**
         * 实例化 ListOperations 对象,可以使用 List 操作
         * @param redisTemplate
         * @return
         */
        @Bean
        public ListOperations<String, Object> listOperations(
                RedisTemplate<String, Object> redisTemplate) {
            return redisTemplate.opsForList();
        }
    
        /**
         * 实例化 SetOperations 对象,可以使用 Set 操作
         * @param redisTemplate
         * @return
         */
        @Bean
        public SetOperations<String, Object> setOperations(
                RedisTemplate<String, Object> redisTemplate) {
            return redisTemplate.opsForSet();
        }
    
        /**
         * 实例化 ZSetOperations 对象,可以使用 ZSet 操作
         * @param redisTemplate
         * @return
         */
        @Bean
        public ZSetOperations<String, Object> zSetOperations(
                RedisTemplate<String, Object> redisTemplate) {
            return redisTemplate.opsForZSet();
        }
    

    个人学习分享
    更多 springboot、springcloud、docker 文章关注微信公众号吧:

    A罗恩和Java

    相关文章

      网友评论

        本文标题:SpringBoot>11 - 集成 Redis

        本文链接:https://www.haomeiwen.com/subject/ruobdqtx.html