SpringBoot>11 - 集成 Redis

简介:

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




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



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 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 extends RedisAccessor implements RedisOperations, 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 functionDomainRedisTemplate() {
        RedisTemplate redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        initDomainRedisTemplate(redisTemplate);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * 设置数据存入 redis 的序列化方式
     * 
redisTemplate 序列化默认使用的jdkSerializeable, 存储二进制字节码, 所以自定义序列化类 * * @param redisTemplate */ private void initDomainRedisTemplate( RedisTemplate 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 valueOperations( RedisTemplate redisTemplate) { return redisTemplate.opsForValue(); } /** * 实例化 HashOperations 对象,可以使用 Hash 类型操作 * @param redisTemplate * @return */ @Bean public HashOperations hashOperations( RedisTemplate redisTemplate) { return redisTemplate.opsForHash(); } /** * 实例化 ListOperations 对象,可以使用 List 操作 * @param redisTemplate * @return */ @Bean public ListOperations listOperations( RedisTemplate redisTemplate) { return redisTemplate.opsForList(); } /** * 实例化 SetOperations 对象,可以使用 Set 操作 * @param redisTemplate * @return */ @Bean public SetOperations setOperations( RedisTemplate redisTemplate) { return redisTemplate.opsForSet(); } /** * 实例化 ZSetOperations 对象,可以使用 ZSet 操作 * @param redisTemplate * @return */ @Bean public ZSetOperations zSetOperations( RedisTemplate redisTemplate) { return redisTemplate.opsForZSet(); }

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

A罗恩和Java

你可能感兴趣的:(SpringBoot>11 - 集成 Redis)