• springboot集成Redis


    1    添加redis依赖

    <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-pool2</artifactId>
       <version>2.4.2</version>
    </dependency>
    <dependency>
       <groupId>org.springframework.data</groupId>
       <artifactId>spring-data-redis</artifactId>
       <version>1.7.10.RELEASE</version>
    </dependency>
    <dependency>
       <groupId>org.springframework.session</groupId>
       <artifactId>spring-session</artifactId>
       <version>1.3.1.RELEASE</version>
    </dependency>
    

      

    2  Redis配置

    @Configuration
    @EnableCaching
    public class RedisCacheConfig  extends CachingConfigurerSupport {
        Logger logger = LoggerFactory.getLogger(RedisCacheConfig.class);
        @Value("${spring.redis.hostName}")
        private String host;
    
        @Value("${spring.redis.port}")
        private int port;
    
        @Value("${spring.redis.database}")
        private int database;
    
        @Value("${spring.redis.password}")
        private String password;
    
        @Value("${spring.redis.timeout}")
        private int timeout;
    
        @Autowired
        private MyStringRedisSerizlizer stringRedisSerizlizer;
    
    
        @Bean
        public static ConfigureRedisAction configureRedisAction() {
            return ConfigureRedisAction.NO_OP;//ERR unknown command 'CONFIG'
        }
       
        /**
         * @Bean 和 @ConfigurationProperties
         * 该功能在官方文档是没有提到的,我们可以把@ConfigurationProperties和@Bean和在一起使用。
         * 举个例子,我们需要用@Bean配置一个Config对象,Config对象有a,b,c成员变量需要配置,
         * 那么我们只要在yml或properties中定义了a=1,b=2,c=3,
         * 然后通过@ConfigurationProperties就能把值注入进Config对象中
         * @return
         **/
        @Bean
        @ConfigurationProperties(prefix = "spring.redis.pool")
        public JedisPoolConfig getRedisConfig() {
            JedisPoolConfig config = new JedisPoolConfig();
            return config;
        }
    
        @Bean
      //  @ConfigurationProperties(prefix = "spring.redis")
        public JedisConnectionFactory getConnectionFactory() {
            JedisConnectionFactory factory = new JedisConnectionFactory();
            factory.setUsePool(true);
            JedisPoolConfig config = getRedisConfig();
            factory.setPoolConfig(config);
            factory.setHostName(host);
            factory.setPort(port);
            factory.setDatabase(database);
            factory.setPassword(password);
            factory.setTimeout(timeout);
            logger.info("JedisConnectionFactory bean init success.");
            return factory;
        }
    
        @Bean
        public RedisTemplate<String, Object> redisTemplate() {
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
            JedisConnectionFactory factory = getConnectionFactory();
            redisTemplate.setConnectionFactory(factory);
    
            // 使用Jackson2JsonRedisSerialize 替换默认序列化
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
    
            // 设置key的序列化规则
            redisTemplate.setKeySerializer(stringRedisSerizlizer);
            redisTemplate.setHashKeySerializer(stringRedisSerizlizer);
            //设置value的序列化规则
            redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
            redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
            redisTemplate.afterPropertiesSet();
            return redisTemplate;
        }
    
        @Bean
        public RedisTemplate<String, Object> redisZipTemplate() {
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
            JedisConnectionFactory factory = getConnectionFactory();
            redisTemplate.setConnectionFactory(factory);
    
            // 使用Jackson2JsonRedisSerialize 替换默认序列化
            Jackson2JsonRedisZipVersionSerializer jackson2JsonRedisZipSerializer = new Jackson2JsonRedisZipVersionSerializer(Object.class);
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisZipSerializer.setObjectMapper(objectMapper);
    
            // 设置key的序列化规则
            redisTemplate.setKeySerializer(stringRedisSerizlizer);
            redisTemplate.setHashKeySerializer(stringRedisSerizlizer);
            //设置value的序列化规则
            redisTemplate.setValueSerializer(jackson2JsonRedisZipSerializer);
            redisTemplate.setHashValueSerializer(jackson2JsonRedisZipSerializer);
            redisTemplate.afterPropertiesSet();
            return redisTemplate;
        }
    
        @Bean
        public RedisTemplate<String, String> stringRedisTemplate() {
            RedisTemplate<String, String> redisTemplate = new StringRedisTemplate();
            JedisConnectionFactory factory = getConnectionFactory();
            redisTemplate.setConnectionFactory(factory);
            return redisTemplate;
        }
    
        @Bean
        public CacheManager cacheManager(RedisTemplate redisTemplate) {
            RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
    
            //默认超时时间,单位秒
            cacheManager.setDefaultExpiration(3000);
            //根据缓存名称设置超时时间,0为不超时
            Map<String,Long> expires = new ConcurrentHashMap<>();
            cacheManager.setExpires(expires);
    
            return cacheManager;
        }
    
        /**
         * 用于spring session,防止每次创建一个线程
         * @return
         */
        @Bean
        public ThreadPoolTaskExecutor springSessionRedisTaskExecutor(){
            ThreadPoolTaskExecutor springSessionRedisTaskExecutor = new ThreadPoolTaskExecutor();
            springSessionRedisTaskExecutor.setCorePoolSize(8);
            springSessionRedisTaskExecutor.setMaxPoolSize(16);
            springSessionRedisTaskExecutor.setKeepAliveSeconds(10);
            springSessionRedisTaskExecutor.setQueueCapacity(1000);
            springSessionRedisTaskExecutor.setThreadNamePrefix("Spring session redis executor thread: ");
            return springSessionRedisTaskExecutor;
        }
    
    }
    

      

    3   自定义序列化

    /**
     * @author xbchen
     * @date 2018-8-30
     * @description Redis缓存, key的序列化, 基于StringRedisSerializer改造,使之适配key为非string类型的数据
     */
    @Component
    public class MyStringRedisSerizlizer implements RedisSerializer<Object> {
        private final Charset charset;
    
        private final String target = """;
    
        private final String replacement = "";
    
        public MyStringRedisSerizlizer() {
            this(Charset.forName("UTF8"));
        }
    
        public MyStringRedisSerizlizer(Charset charset) {
            Assert.notNull(charset, "Charset must not be null!");
            this.charset = charset;
        }
    
        public String deserialize(byte[] bytes) {
            return (bytes == null ? null : new String(bytes, charset));
        }
    
        public byte[] serialize(Object obj) {
            String string = JSONUtils.toJSONString(obj);
            if (string == null) {
                return null;
            }
            string = string.replace(target, replacement);
            return string.getBytes(charset);
        }
    }
    

      

    4    redis Session共享配置

    @Configuration
    //maxInactiveIntervalInSeconds session超时时间,单位秒60*60*48=172800
    @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 172800)
    public class RedisSessionConfig {
    }
    

    5 定义redis 接口、实现用于业务使用

    @Service("redisService")
    public class RedisServiceImpl implements IRedisService {
    
        private static Logger logger = LoggerFactory.getLogger(RedisServiceImpl.class);
    
        @Resource
        private RedisTemplate<String, Object> redisTemplate;
        @Resource
        private ITimerTaskLogService timerTaskLogService;
    
        // Lua脚本
        private String script = "if redis.call("get",KEYS[1]) == ARGV[1] then
    " +
                "    return redis.call("del",KEYS[1])
    " +
                "else
    " +
                "    return 0
    " +
                "end";
    
        private static String lockScript = " if 1 == redis.call('setnx',KEYS[1],ARGV[1]) then" +
                " redis.call('expire',KEYS[1],ARGV[2])" +
                " return 1;" +
                " else" +
                " return 0;" +
                " end;";
        private static RedisScript<Boolean> newSetIfAbsentScript = new DefaultRedisScript<>(lockScript,Boolean.class );
        //=============================common============================
        /**
         * 指定缓存失效时间
         * @param key 键
         * @param time 时间(秒)
         * @return
         */
        @Override
        public boolean expire(String key,long time){
            try {
                if(time>0){
                    redisTemplate.expire(key, time, TimeUnit.SECONDS);
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 根据key 获取过期时间
         * @param key 键 不能为null
         * @return 时间(秒) 返回0代表为永久有效
         */
        @Override
        public long getExpire(String key){
            return redisTemplate.getExpire(key,TimeUnit.SECONDS);
        }
    
        /**
         * 判断key是否存在
         * @param key 键
         * @return true 存在 false不存在
         */
        @Override
        public boolean hasKey(String key){
            try {
                return redisTemplate.hasKey(key);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 删除缓存
         * @param key 可以传一个值 或多个
         */
        @SuppressWarnings("unchecked")
        @Override
        public void del(String ... key){
            if(key!=null&&key.length>0){
                if(key.length==1){
                    redisTemplate.delete(key[0]);
                }else{
                    redisTemplate.delete(CollectionUtils.arrayToList(key));
                }
            }
        }
    
        //============================String=============================
        /**
         * 普通缓存获取
         * @param key 键
         * @return 值
         */
        @Override
        public Object get(String key){
            return key==null?null:redisTemplate.opsForValue().get(key);
        }
    
        /**
         * 普通缓存放入
         * @param key 键
         * @param value 值
         * @return true成功 false失败
         */
        @Override
        public boolean set(String key,Object value) {
            try {
                redisTemplate.opsForValue().set(key, value);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
    
        }
    
        /**
         * 普通缓存放入并设置时间
         * @param key 键
         * @param value 值
         * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
         * @return true成功 false 失败
         */
        @Override
        public boolean set(String key,Object value,long time){
            try {
                if(time>0){
                    redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
                }else{
                    set(key, value);
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 递增
         * @param key 键
         * @param delta 要增加几(大于0)
         * @return
         */
        @Override
        public long incr(String key, long delta){
            if(delta<0){
                throw new RuntimeException("递增因子必须大于0");
            }
            return redisTemplate.opsForValue().increment(key, delta);
        }
    
        /**
         * 递减
         * @param key 键
         * @param delta 要减少几(小于0)
         * @return
         */
        @Override
        public long decr(String key, long delta){
            if(delta<0){
                throw new RuntimeException("递减因子必须大于0");
            }
            return redisTemplate.opsForValue().increment(key, -delta);
        }
    
        //================================Map=================================
        /**
         * HashGet
         * @param key 键 不能为null
         * @param item 项 不能为null
         * @return 值
         */
        @Override
        public Object hget(String key,String item){
            return redisTemplate.opsForHash().get(key, item);
        }
    
        /**
         * 获取hashKey对应的所有键值
         * @param key 键
         * @return 对应的多个键值
         */
        @Override
        public Map<Object,Object> hmget(String key){
            return redisTemplate.opsForHash().entries(key);
        }
    
        /**
         * HashSet
         * @param key 键
         * @param map 对应多个键值
         * @return true 成功 false 失败
         */
        @Override
        public boolean hmset(String key, Map<String,Object> map){
            try {
                redisTemplate.opsForHash().putAll(key, map);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * HashSet 并设置时间
         * @param key 键
         * @param map 对应多个键值
         * @param time 时间(秒)
         * @return true成功 false失败
         */
        @Override
        public boolean hmset(String key, Map<String,Object> map, long time){
            try {
                redisTemplate.opsForHash().putAll(key, map);
                if(time>0){
                    expire(key, time);
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 向一张hash表中放入数据,如果不存在将创建
         * @param key 键
         * @param item 项
         * @param value 值
         * @return true 成功 false失败
         */
        @Override
        public boolean hset(String key,String item,Object value) {
            try {
                redisTemplate.opsForHash().put(key, item, value);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 向一张hash表中放入数据,如果不存在将创建
         * @param key 键
         * @param item 项
         * @param value 值
         * @param time 时间(秒)  注意:如果已存在的hash表有时间,这里将会替换原有的时间
         * @return true 成功 false失败
         */
        @Override
        public boolean hset(String key,String item,Object value,long time) {
            try {
                redisTemplate.opsForHash().put(key, item, value);
                if(time>0){
                    expire(key, time);
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 删除hash表中的值
         * @param key 键 不能为null
         * @param item 项 可以使多个 不能为null
         */
        @Override
        public void hdel(String key, Object... item){
            redisTemplate.opsForHash().delete(key,item);
        }
    
        /**
         * 判断hash表中是否有该项的值
         * @param key 键 不能为null
         * @param item 项 不能为null
         * @return true 存在 false不存在
         */
        @Override
        public boolean hHasKey(String key, String item){
            return redisTemplate.opsForHash().hasKey(key, item);
        }
    
        /**
         * hash递增 如果不存在,就会创建一个 并把新增后的值返回
         * @param key 键
         * @param item 项
         * @param by 要增加几(大于0)
         * @return
         */
        @Override
        public double hincr(String key, String item,double by){
            return redisTemplate.opsForHash().increment(key, item, by);
        }
    
        /**
         * hash递减
         * @param key 键
         * @param item 项
         * @param by 要减少记(小于0)
         * @return
         */
        @Override
        public double hdecr(String key, String item,double by){
            return redisTemplate.opsForHash().increment(key, item,-by);
        }
    
        //============================set=============================
        /**
         * 根据key获取Set中的所有值
         * @param key 键
         * @return
         */
        @Override
        public Set<Object> sGet(String key){
            try {
                return redisTemplate.opsForSet().members(key);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        /**
         * 根据value从一个set中查询,是否存在
         * @param key 键
         * @param value 值
         * @return true 存在 false不存在
         */
        @Override
        public boolean sHasKey(String key,Object value){
            try {
                return redisTemplate.opsForSet().isMember(key, value);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 将数据放入set缓存
         * @param key 键
         * @param values 值 可以是多个
         * @return 成功个数
         */
        @Override
        public long sSet(String key, Object...values) {
            try {
                return redisTemplate.opsForSet().add(key, values);
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }
    
        /**
         * 将set数据放入缓存
         * @param key 键
         * @param time 时间(秒)
         * @param values 值 可以是多个
         * @return 成功个数
         */
        @Override
        public long sSetAndTime(String key,long time,Object...values) {
            try {
                Long count = redisTemplate.opsForSet().add(key, values);
                if(time>0) expire(key, time);
                return count;
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }
    
        /**
         * 获取set缓存的长度
         * @param key 键
         * @return
         */
        @Override
        public long sGetSetSize(String key){
            try {
                return redisTemplate.opsForSet().size(key);
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }
    
        /**
         * 移除值为value的
         * @param key 键
         * @param values 值 可以是多个
         * @return 移除的个数
         */
        @Override
        public long setRemove(String key, Object ...values) {
            try {
                Long count = redisTemplate.opsForSet().remove(key, values);
                return count;
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }
        //===============================list=================================
    
        /**
         * 获取list缓存的内容
         * @param key 键
         * @param start 开始
         * @param end 结束  0 到 -1代表所有值
         * @return
         */
        @Override
        public List<Object> lGet(String key,long start, long end){
            try {
                return redisTemplate.opsForList().range(key, start, end);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        /**
         * 获取list缓存的长度
         * @param key 键
         * @return
         */
        @Override
        public long lGetListSize(String key){
            try {
                return redisTemplate.opsForList().size(key);
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }
    
        /**
         * 通过索引 获取list中的值
         * @param key 键
         * @param index 索引  index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
         * @return
         */
        @Override
        public Object lGetIndex(String key,long index){
            try {
                return redisTemplate.opsForList().index(key, index);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        /**
         * 将list放入缓存
         * @param key 键
         * @param value 值
         * @return
         */
        @Override
        public boolean lSet(String key, Object value) {
            try {
                redisTemplate.opsForList().rightPush(key, value);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 将list放入缓存
         * @param key 键
         * @param value 值
         * @param time 时间(秒)
         * @return
         */
        @Override
        public boolean lSet(String key, Object value, long time) {
            try {
                redisTemplate.opsForList().rightPush(key, value);
                if (time > 0) expire(key, time);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 将list放入缓存
         * @param key 键
         * @param value 值
         * @return
         */
        @Override
        public boolean lSet(String key, List<Object> value) {
            try {
                redisTemplate.opsForList().rightPushAll(key, value);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 将list放入缓存
         * @param key 键
         * @param value 值
         * @param time 时间(秒)
         * @return
         */
        @Override
        public boolean lSet(String key, List<Object> value, long time) {
            try {
                redisTemplate.opsForList().rightPushAll(key, value);
                if (time > 0) expire(key, time);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 根据索引修改list中的某条数据
         * @param key 键
         * @param index 索引
         * @param value 值
         * @return
         */
        @Override
        public boolean lUpdateIndex(String key, long index,Object value) {
            try {
                redisTemplate.opsForList().set(key, index, value);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 移除N个值为value
         * @param key 键
         * @param count 移除多少个
         * @param value 值
         * @return 移除的个数
         */
        @Override
        public long lRemove(String key,long count,Object value) {
            try {
                Long remove = redisTemplate.opsForList().remove(key, count, value);
                return remove;
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }
        /**
         * 分布式锁-加锁
         * @param key
         * @param value 唯一val
         * @param timer 设置过期时间
         * @return
         */
        public boolean lock(String key, String value, Long timer) {
            Jedis jedis = (Jedis)redisTemplate.getConnectionFactory().getConnection().getNativeConnection();
            try {
                String result = jedis.set(key, value, "NX", "PX", timer);
                if ("OK".equalsIgnoreCase(result)) {
                    return true;
                }
                return false;
            } finally {
                if (jedis != null && jedis.isConnected()) { // 确保jedis顺利关闭
                    jedis.close();
                }
            }
        }
    
    
        public boolean setIfAbsent(String key,String value,Long seconds){
            List<String> keys = new ArrayList<>();
            keys.add(key);
            Object[] args = {value,seconds};
            return redisTemplate.<Boolean>execute(newSetIfAbsentScript,keys, args);
        }
    
        /**
         * 分布式锁-解锁
         */
        public void unLock(String key, String value) {
            Jedis jedis = (Jedis)redisTemplate.getConnectionFactory().getConnection().getNativeConnection();
            try {
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                jedis.eval(script, Collections.singletonList(key), Collections.singletonList(value));
            } catch (Throwable e) {
                logger.error("[redis分布式锁] 解锁异常, {}", e.getMessage(), e);
            }finally {
                if (jedis != null && jedis.isConnected()) { // 确保jedis顺利关闭
                    jedis.close();
                }
            }
        }
    
        @Override
        public boolean timelock(String key, String currentTimeMillis, long lockTime) {
            boolean isTimeLock = false;
            try {
                Object obj = redisTemplate.opsForValue().get(key);
                if(obj == null) {
                    //首次
                    redisTemplate.opsForValue().set(key, currentTimeMillis);
                }else {
                    long oldVal = Long.valueOf((String)obj);
                    long currenTime = Long.valueOf(currentTimeMillis);
                    if((currenTime-oldVal) > lockTime) {
                        //超过时间锁锁定时间赋新值
                        redisTemplate.opsForValue().set(key, currentTimeMillis);
                    }else {
                        isTimeLock = true;
                    }
                }
            } catch (Exception e) {
                isTimeLock = true;
            }
            return isTimeLock;
        }
    }
    

      

  • 相关阅读:
    JVM 常量池、运行时常量池、字符串常量池
    JVM Direct Memory
    JVM 方法区
    JVM GC Roots
    jvm 堆
    jvm slot复用
    JVM 虚拟机栈
    JVM 程序计数器
    java打印树形目录结构
    java 通过反射获取数组
  • 原文地址:https://www.cnblogs.com/brant/p/12496899.html
Copyright © 2020-2023  润新知