java --spring boot 框架集成redis缓存

22 篇文章 0 订阅
4 篇文章 0 订阅

首先需要主要 ,redis 集成到spring boot 中有好多不同种类的框架, 网上好多解决的有3中,

一种:  完全注解  方式,

如:需要在主程序中用:@EnableCaching   服务层用:@Cacheable  和 @CacheEvict

@Cacheable :当redis缓存中没有对应的数据时,向数据库查询,并将查询结果新增到redis缓存;

@CacheEvict:此方法执行后,删除对应的redis缓存;

这里不详细讲解。

二种:Jedis

1:这是一个框架需要引用到的包:

        <dependency>  
            <groupid>org.springframework.data</groupid>  
            <artifactid>spring-data-redis</artifactid>  
            <version>1.7.2.RELEASE</version>  
        </dependency>  
        <dependency>  
            <groupid>redis.clients</groupid>  
            <artifactid>jedis</artifactid>  
            <version>2.8.1</version>  
        </dependency> 

2. 添加redis配置文件(非必须,可以在spring配置文件中直接配置)

redis.host=127.0.0.1 
#访问端口 
redis.port=6379 
#注意,如果没有password,此处不设置值,但这一项要保留 
redis.password= 
#最大空闲数,数据库连接的最大空闲时间。超过空闲时间,数据库连接将被标记为不可用,然后被释放。设为0表示无限制。 
redis.maxIdle=300 
#连接池的最大数据库连接数。设为0表示无限制 
redis.maxActive=600 
#最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。 
redis.maxWait=1000 
#在borrow一个jedis实例时,是否提前进行alidate操作;如果为true,则得到的jedis实例均是可用的; 
redis.testOnBorrow=true  

 写的不完整,因为没有用这种方式集成redis

三种:  RedisTemplate

(1)本文所采用的SpringBoot的版本如下

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

(2)加入Redis相关依赖

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

二、application.properties中加入redis相关配置

一般现在用的比较多的是yml

spring:
    redis:
      host: 127.0.0.1
      port: 6379
      database: 0
      timeout: 3000

三、写一个redis配置类 

redis模板源码:这段代码开发人员不需要处理,是依赖包中的。

其实现在就可以在代码中注入RedisTemplate,为啥可以直接注入呢?先看下源码吧。下图为 RedisAutoConfiguration类中的截图,为了防止图片失效,代码也贴上 。

@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(
            RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }


    @Bean
    @ConditionalOnMissingBean
    public StringRedisTemplate stringRedisTemplate(
            RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;

    }
}

通过源码可以看出,SpringBoot自动帮我们在容器中生成了一个RedisTemplate和一个StringRedisTemplate。但是,这个RedisTemplate的泛型是<Object,Object>,写代码不方便,需要写好多类型转换的代码;我们需要一个泛型为<String,Object>形式的RedisTemplate。并且,这个RedisTemplate没有设置数据存在Redis时,key及value的序列化方式。

        看到这个@ConditionalOnMissingBean注解后,就知道如果Spring容器中有了RedisTemplate对象了,这个自动配置的RedisTemplate不会实例化。因此我们可以直接自己写个配置类,配置RedisTemplate。

 开发人员只需要添加一个配置类:

因为redis内部有一个模板,这个只是对模板重新配置。重新配置一个RedisTemplate

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
        // 值采用json序列化
        redisTemplate.setValueSerializer(serializer);
        //使用StringRedisSerializer来序列化和反序列化redis的key值
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        // 设置hash key 和value序列化模式
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(serializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

四、写一个Redis工具类

   直接用RedisTemplate操作Redis,需要很多行代码,因此直接封装好一个RedisUtils,这样写代码更方便点。这个RedisUtils交给Spring容器实例化,使用时直接注解注入。

        工具类代码如下: 开发人员直接复制到自己的项目中即可。

package com.unit.mapping.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * xul
 * 封装  redis
 */

@Component
@Slf4j
public class RedisUtils {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * @param key
     * @return 获得值
     * redis有五种数据类型 opsForValue表示是操作字符串类型
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue( ).get(key);
    }

    //本来只可以放入string类型,但是我们配置了自动序列化所以这儿可以传入object
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue( ).set(key, value);
            return true;
        } catch (Exception e) {
            log.error("redis set value exception:{}", e);
            return false;
        }
    }

    /**
     * 原子操作
     *
     * @param key
     * @param value
     * @param expire 过期时间 秒
     * @return
     */
    public boolean setex(String key, Object value, long expire) {
        try {
            //TimeUnit.SECONDS指定类型为秒
            redisTemplate.opsForValue( ).set(key, value, expire, TimeUnit.SECONDS);
            return true;
        } catch (Exception e) {
            log.error("redis set value and expire exception:{}", e);
            return false;
        }
    }

    /**
     * 非原子操作
     *
     * @param key
     * @param expire
     * @return
     */
    public boolean expire(String key, long expire) {
        try {
            //这儿没有ops什么的是因为每种数据类型都能设置过期时间
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
            return true;
        } catch (Exception e) {
            log.error("redis set key expire exception:{}", e);
            return false;
        }
    }

    /**
     * @param key
     * @return 获取key的过期时间
     */
    public long ttl(String key) {
        return redisTemplate.getExpire(key);
    }

    /**
     * @param keys 删除key 可变参数
     */
    public void del(String... keys) {
        if (keys != null && keys.length > 0) {
            redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(keys));
        }
    }

    /**
     * @param key
     * @param step 传入正数 就是加多少 传入负数就是减多少
     * @return
     */
    public long incrBy(String key, long step) {
        return redisTemplate.opsForValue( ).increment(key, step);
    }

    /**
     * @param key
     * @param value
     * @return 如果该key存在就返回false 设置不成功 key不存在就返回ture设置成功
     */
    public boolean setnx(String key, Object value) {
        return redisTemplate.opsForValue( ).setIfAbsent(key, value);
    }

    /**
     * 原子操作
     *
     * @param key
     * @param value
     * @param expire 在上面方法加上过期时间设置
     * @return
     */
    public boolean setnxAndExpire(String key, Object value, long expire) {
        return redisTemplate.opsForValue( ).setIfAbsent(key, value, expire, TimeUnit.SECONDS);
    }

    /**
     * @param key
     * @param value
     * @return 如果该key存在就返回之前的value  不存在就返回null
     */
    public Object getAndSet(String key, Object value) {
        return redisTemplate.opsForValue( ).getAndSet(key, value);
    }

    /**
     * @param key
     * @return 判断key是否存在
     */
    public boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /***list的长度**/
    public long llen(String key) {
        return redisTemplate.opsForList( ).size(key);
    }

    /**
     * 获取key中index位置的值,负数就反过来数,-1为最后一个
     *
     * @param key
     * @param index
     * @return
     */
    public Object lgetByIndex(String key, long index) {
        try {
            return redisTemplate.opsForList( ).index(key, index);
        } catch (Exception e) {
            log.error("redis lgetByIndex error,key:{},index:{}exception:{}", key, index, e);
            return null;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean lrpush(String key, Object value) {
        try {
            redisTemplate.opsForList( ).rightPush(key, value);
            return true;
        } catch (Exception e) {
            log.error("redis lrpush error,key:{},value:{}exception:{}", key, value, e);
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    public boolean lrpush(String key, Object value, long time) {
        try {
            redisTemplate.opsForList( ).rightPush(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            log.error("redis lrpush error,key:{},value:{},timeL{},exception:{}", key, value, time, e);
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean lrpush(String key, List<Object> value) {
        try {
            redisTemplate.opsForList( ).rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            log.error("redis lrpush error,key:{},value:{},exception:{}", key, value, e);
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    public boolean lrpush(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList( ).rightPushAll(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            log.error("redis lrpush error,key:{},value:{},time:{},exception:{}", key, value, time, e);
            return false;
        }
    }

    /**
     * 获取列表指定范围内的元素
     *
     * @param key
     * @param start
     *            开始位置, 0是开始位置
     * @param end
     *            结束位置, -1返回所有
     * @return
     */
    public List<Object> lRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 根据索引修改list中的某条数据
     *
     * @param key   键
     * @param index 索引
     * @param value 值
     * @return
     */
    public boolean lUpdateByIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList( ).set(key, index, value);
            return true;
        } catch (Exception e) {
            log.error("redis lUpdateByIndex error,key:{},index:{},value:{},exception:{}", key, index, value, e);
            return false;
        }
    }

    /**
     * 移除N个值为value
     *
     * @param key   键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */
    public long lrem(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList( ).remove(key, count, value);
            return remove;
        } catch (Exception e) {
            log.error("redis lrem error,key:{},count:{},value:{},exception:{}", key, count, value, e);
            return 0;
        }
    }
    /*****hash数据类型方法   opsForHash表示是操作字符串类型*****/

    /**
     * @param key   健
     * @param field 属性
     * @param value 值
     * @return
     */
    public boolean hset(String key, String field, Object value) {
        try {
            redisTemplate.opsForHash( ).put(key, field, value);
            return true;
        } catch (Exception e) {
            log.error("redis hset eror,key:{},field:{},value:{}", key, field, value);
            return false;
        }
    }

    /**
     * @param key
     * @param field
     * @param value
     * @param seconds(秒) 过期时间
     * @return
     */
    public boolean hset(String key, String field, Object value, long seconds) {
        try {
            redisTemplate.opsForHash( ).put(key, field, value);
            expire(key, seconds);//调用通用方法设置过期时间
            return true;
        } catch (Exception e) {
            log.error("redis hset and expire eror,key:{},field:{},value:{},exception:{}", key, field, value, e);
            return false;
        }
    }

    /**
     * 获取key中field属性的值
     *
     * @param key
     * @param field
     * @return
     */
    public Object hget(String key, String field) {
        return redisTemplate.opsForHash( ).get(key, field);
    }

    /**
     * 获取key中多个属性的键值对,这儿使用map来接收
     *
     * @param key
     * @param fields
     * @return
     */
    public Map<String, Object> hmget(String key, String... fields) {
        Map<String, Object> map = new HashMap<>( );
        for (String field : fields) {
            map.put(field, hget(key, field));
        }
        return map;
    }

    /**
     * @param key 获得该key下的所有键值对
     * @return
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash( ).entries(key);
    }

//    /**
//     * @param key 获得该key下的所有键值对
//     * @return
//     */
//    @Autowired
//    private JacksonUtil jacksonUtil;
//    //map----json字符串---->对象
//    public <T>T hmgetObject(String key,Class<T> tClass){
//        Map<Object, Object> hmget = hmget(key);
//        if(CollectionUtils.isEmpty(hmget)) return null;
//        //查询到了 先把数据转成json字符串
//        String s = jacksonUtil.writeAsString(hmget);
//        //再把json字符串转回对象
//        return  jacksonUtil.readValue(s,tClass);
//    }

    /**
     * @param key 键
     * @param map 对应多个键值
     * @return
     */
    public boolean hmset(String key, Map<Object, Object> map) {
        try {
            redisTemplate.opsForHash( ).putAll(key, map);
            return true;
        } catch (Exception e) {
            log.error("redis hmset eror,key:{},value:{},exception:{}", key, map, e);
            return false;
        }
    }

//    public boolean hmset(String key,Object object){
//        try {
//            String s = jacksonUtil.writeAsString(object);
//            Map<String, String> map = jacksonUtil.readValueMap(s);
//            redisTemplate.opsForHash().putAll(key, map);
//            return true;
//        }catch (Exception e){
//            log.error("redis hmset eror,key:{},object:{},exception:{}",key,object,e);
//            return false;
//        }
//    }

    /**
     * @param key     键
     * @param map     对应多个键值
     * @param seconds 过期时间(秒)
     * @return
     */
    public boolean hmset(String key, Map<String, Object> map, long seconds) {
        try {
            redisTemplate.opsForHash( ).putAll(key, map);
            expire(key, seconds);
            return true;
        } catch (Exception e) {
            log.error("redis hmset eror,key:{},value:{},expireTime,exception:{}", key, map, seconds, e);
            return false;
        }
    }

    /**
     * 删除key中的属性
     *
     * @param key
     * @param fields
     */
    public void hdel(String key, Object... fields) {
        redisTemplate.opsForHash( ).delete(key, fields);
    }

    /**
     * 判断key中是否存在某属性
     *
     * @param key
     * @param field
     * @return
     */
    public boolean hHashKey(String key, String field) {
        return redisTemplate.opsForHash( ).hasKey(key, field);
    }

    /**
     * 对key中filed的value增加多少 如果是减少就传入负数
     *
     * @param key
     * @param field
     * @param step  正数增加,负数减少
     * @return
     */
    public double hincr(String key, String field, double step) {
        return redisTemplate.opsForHash( ).increment(key, field, step);
    }

    /**
     * key中多少个
     *
     * @param key
     * @return
     */
    public long hlen(String key) {
        return redisTemplate.opsForHash( ).size(key);
    }
    /******其他方法用到在增加********/

    /***set集合***/
    /**
     * 获取key中所有元素
     *
     * @param key
     * @return
     */
    public Set<Object> sgetAll(String key) {
        try {
            return redisTemplate.opsForSet( ).members(key);
        } catch (Exception e) {
            log.error("redis sgetAll error,key:{},exception:{}", key, e);
            return null;
        }
    }

    /**
     * 判断value是否在key中
     *
     * @param key
     * @param value
     * @return
     */
    public boolean sexists(String key, Object value) {
        try {
            return redisTemplate.opsForSet( ).isMember(key, value);
        } catch (Exception e) {
            log.error("redis sexists error,key:{},value:{},exception:{}", key, value, e);
            return false;
        }
    }

    /**
     * 插入多个元素
     *
     * @param key
     * @param values
     * @return 成功的个数
     */
    public long sset(String key, Object... values) {
        try {
            return redisTemplate.opsForSet( ).add(key, values);
        } catch (Exception e) {
            log.error("redis sset error,key:{},value:{},values:{},exception:{}", key, values, e);
            return 0;
        }
    }

    /**
     * 添加元素并设置过期时间  (非原子操作)
     *
     * @param key
     * @param time
     * @param values
     * @return
     */
    public long sset(String key, long time, Object... values) {
        try {
            long count = redisTemplate.opsForSet( ).add(key, values);
            expire(key, time);
            return count;
        } catch (Exception e) {
            log.error("redis sset error,key:{},value:{},values:{},exception:{}", key, values, e);
            return 0;
        }
    }

    /**
     * 获取set的长度
     *
     * @param key
     * @return
     */
    public long sgetSize(String key) {
        try {
            return redisTemplate.opsForSet( ).size(key);
        } catch (Exception e) {
            log.error("redis sgetSize error,key:{},exception:{}", key, e);
            return 0;
        }
    }

    /**
     * 移除值为value的
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 移除的个数
     */
    public long sRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet( ).remove(key, values);
            return count;
        } catch (Exception e) {
            log.error("redis sRemove error,key:{},values:{},exception:{}", key, values, e);
            return 0;
        }
    }

    /**
     * 随机取count个元素  count为正数就取不重复的  负数就有可能重复
     *
     * @param key
     * @param count
     * @return
     */
    public List<Object> sRandom(String key, long count) {
        try {
            return redisTemplate.opsForSet( ).randomMembers(key, count);
        } catch (Exception e) {
            log.error("redis sRandom error,key:{},count:{},exception:{}", key, count, e);
            return null;
        }
    }
    /****zset工具类***/
    /**
     * 添加元素
     *
     * @param key
     * @param member
     * @param score
     * @return
     */
    public boolean zadd(String key, Object member, double score) {
        try {
            return redisTemplate.opsForZSet( ).add(key, member, score);
        } catch (Exception e) {
            log.error("redis zadd error,key:{},value:{},score:{},exception:{}", key, member, score, e);
            return false;
        }
    }

    public Set<String> zrange(String key, int start, int end) {

        try {
            Set<Object> range = redisTemplate.opsForZSet( ).
                    range(key, start, end);
            if (range == null || range.size( ) == 0) {
                return null;
            }
            return range.stream( ).
                    map(o -> (String) o).collect(Collectors.toSet( ));
        } catch (Exception e) {
            log.error("redis zrange error,key:{},start:{},end:{},exception:{}",
                    key, start, end, e);
            return null;
        }
    }

}

五、 在server层的使用

1:根据key值查询redis,这里是保存的对象为:CityInfo,所以取出来的值需要转换为对象。默认取出来的是string

   //先查询redis信息
        Object str = redisUtils.get(RedisKeyConstant.Mei_Mapping_CityInfo);
        List<CityInfo> allInfo = new ArrayList<>();
        if (str != null) {
            allInfo = JSON.parseArray(str.toString(), CityInfo.class);
            if (allInfo != null && allInfo.size() > 0) {
                return allInfo;
            }
        }

2:根据key值保存数据,

这里保存其实没有那么多代码,只需要下面一行代码即可。

redisUtils.setex(RedisKeyConstant.Mei_Mapping_CityInfo, JSONArray.toJSONString(lists), 30 * 60);
   // 如果redis 获取的数据是空 ,再查询数据库
        List<CityInfo> lists = new ArrayList<>();
        if (allInfo == null || allInfo.size() <= 0) {
            lists = this.cityInfoDao.queryAllByLimit(offset, limit);
            redisUtils.setex(RedisKeyConstant.Mei_Mapping_CityInfo, JSONArray.toJSONString(lists), 30 * 60);
        }

六、小结

        整合其实不麻烦,网上好多博文都有。注意设置下key和value的序列化方式,不然存到Redis的中数据看起来像乱码一下。

另外如果有异常:

Unsatisfied dependency expressed through method 'redisConnectionFactory' parameter 1; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'lettuceClientResources' defined in class path resource 

提示'redisConnectionFactory或者  RedisTemplate 没有注入的情况。这种情况一般是pom文件的引用问题。可以把maven清空一下,再重新下载。

用:mvn clean compile -U

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值