zoukankan      html  css  js  c++  java
  • Redis

    首先,我们知道,mysql是持久化存储,存放在磁盘里面,检索的话,会涉及到一定的IO,为了解决这个瓶颈,于是出现了缓存,比如现在用的最多的 memcached(简称mc)。
    首先,用户访问mc,如果未命中,就去访问mysql,之后像内存和硬盘一样,把数据复制到mc一部分。
      
    redis和mc都是缓存,并且都是驻留在内存中运行的,这大大提升了高数据量web访问的访问速度。然而mc只是提供了简单的数据结构,比如 string存储;redis却提供了大量的数据结构
    比如string、list、set、hashset、sorted set这些,这使得用户方便了好多,毕竟封装了一层实用的功能,同时实现了同样的效果,当然用redis而慢慢舍弃mc。   
    内存和硬盘的关系,硬盘放置主体数据用于持久化存储,而内存则是当前运行的那部分数据,CPU访问内存而不是磁盘,这大大提升了运行的速度,当然这是基于程序的局部化访问原理。
      
    推理到redis+mysql,它是内存+磁盘关系的一个映射,mysql放在磁盘,redis放在内存,这样的话,web应用每次只访问redis,如果没有找到的数据,才去访问Mysql。   然而redis+mysql和内存+磁盘的用法最好是不同的

    redis:remote dictionary service(远程字典服务器),开源免费,用C语言编写,是一个高性能的KV键值对服务器
    相比memcached
    1、redis支持数据的持久化,可以将内存中数据保持在磁盘,重启时可以再次加载进行使用;
    2、支持的数据结构更多如:string、list、set、hashset、sorted set
    3、支持的数据备份
    redis的键是一直保存在内存中的,而值并不是,当redis发现内存达到某个阈值时,会将一些key对应的值保存到磁盘中

    注意:redis是单线程程序,顺序执行所有指令,其它指令必须等到当前的指令执行完了才可以继续。


    redis在spring boot中的简单使用:
    1、引入jar包:
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-pool2</artifactId>
            </dependency>    
    

     2、在spring boot2之后,对redis连接的支持,默认就采用了lettuce(之前是Jedis)。这就一定程度说明了lettuce 和Jedis的优劣。

      Jedis:是老牌的Redis的Java实现客户端,提供了比较全面的Redis命令的支持,

      Redisson:促使使用者对Redis的关注分离,提供很多分布式相关操作服务,例如,分布式锁,分布式集合,可通过Redis支持延迟队列

           Lettuce:高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器

     比较:

      Jedis在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个jedis实例增加物理连接

      lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访,所以一个连接实例可以满足多线程环境下的并发访问,一个连接实例不够的情况也可以按需增加连接实例

    3、redis配置(默认是不使用连接池的,只有配置 redis.lettuce.pool下的属性的时候才可以使用到redis连接池)

    spring.redis.database=0
    spring.redis.password=Test123456
    spring.redis.host=172.19.1.159
    spring.redis.port=6379
    spring.redis.timeout=5000
    # 连接池最大连接数(使用负值表示没有限制) spring.redis.lettuce.pool.max-active=50
    # 连接池中的最大空闲连接 spring.redis.lettuce.pool.max-idle=20
    # 连接池中的最小空闲连接 spring.redis.lettuce.pool.min-idle=10
    # 连接池最大阻塞等待时间(使用负值表示没有限制) spring.redis.lettuce.pool.max-wait=6000

     4、redis工具类:

    package com.aikucun.bill.util;
    
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import java.util.concurrent.TimeUnit;
    
    import javax.annotation.Resource;
    
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.support.atomic.RedisAtomicLong;
    import org.springframework.stereotype.Component;
    import org.springframework.util.CollectionUtils;
    
    /**
     * @author: wangzhikun
     * @date: 2019-04-09
     */
    
    @Component
    public final class RedisUtil {
        @Resource 
        private RedisTemplate<String, Object> redisTemplate;
    
        // =============================common============================
    
        /**
         * 指定缓存失效时间
         *
         * @param key  键
         * @param time 时间(秒)
         * @return
         */
        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代表为永久有效
         */
        public long getExpire(String key) {
            return redisTemplate.getExpire(key, TimeUnit.SECONDS);
        }
    
        /**
         * 判断key是否存在
         *
         * @param key 键
         * @return true 存在 false不存在
         */
        public boolean hasKey(String key) {
            try {
                return redisTemplate.hasKey(key);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 删除缓存
         *
         * @param key 可以传一个值 或多个
         */
        @SuppressWarnings("unchecked")
        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*/
        public Object get(String key) {
            return key == null ? null : redisTemplate.opsForValue().get(key);
        }
    
        /**
         * 普通缓存放入
         *
         * @param key   键
         * @param value 值
         * @return true成功 false失败
         */
    
        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 失败
         */
    
        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 134
         */
    
        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
         */
    
        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*/
        public Object hget(String key, String item) {
            try {
                return redisTemplate.opsForHash().get(key, item);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        /**
         * 获取hashKey对应的所有键值
         *
         * @param key 键
         * @return 对应的多个键值
         */
    
        public Map<Object, Object> hmget(String key) {
            return redisTemplate.opsForHash().entries(key);
        }
    
        /**
         * HashSet
         *
         * @param key 键
         * @param map 对应多个键值
         * @return true 成功 false 失败
         */
        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失败
         */
    
        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失败
         */
        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失败
         */
        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
         */
    
        public void hdel(String key, Object... item) {
            redisTemplate.opsForHash().delete(key, item);
        }
    
        /**
         * 判断hash表中是否有该项的值
         *
         * @param key  键 不能为null
         * @param item 项 不能为null
         * @return true 存在 false不存在
         */
    
        public boolean hHasKey(String key, String item) {
            return redisTemplate.opsForHash().hasKey(key, item);
        }
    
        /**
         * hash递增 如果不存在,就会创建一个 并把新增后的值返回
         *
         * @param key  键
         * @param item 项
         * @param by   要增加几(大于0)
         * @return
         */
        public long hincr(String key, String item, long by) {
            try {
                return redisTemplate.opsForHash().increment(key, item, by);
            } catch (Exception e) {
                e.printStackTrace();
                return 1;
            }
        }
    
        /**
         * hash递减
         *
         * @param key  键
         * @param item 项
         * @param by   要减少记(小于0)
         * @return
         */
    
        public double hdecr(String key, String item, double by) {
            return redisTemplate.opsForHash().increment(key, item, -by);
        }
    
    
        // ============================set=============================
    
        /**
         * 根据key获取Set中的所有值
         *
         * @param key 键
         * @return
         */
        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不存在
         */
    
        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 成功个数
         */
    
        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 成功个数
         */
    
        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 358
         */
    
        public long sGetSetSize(String key) {
            try {
                return redisTemplate.opsForSet().size(key);
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }
    
        /**
         * 移除值为value的
         *
         * @param key    键
         * @param values 值 可以是多个
         * @return 移除的个数
         */
    
        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 391
         */
    
        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 405
         */
    
        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 420
         */
        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 436
         */
    
        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 453
         */
    
        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 472
         */
    
        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 490
         */
    
        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 509
         */
    
        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 移除的个数
         */
    
        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;
            }
        }
    
        public long getAndIncrement(String key, int defaultValue) {
            long incrementId = defaultValue;
            try {
                RedisAtomicLong counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
                if (counter.get() <= defaultValue) {
                    counter.set(defaultValue);
                }
                incrementId = counter.getAndIncrement();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return incrementId;
        }
    }
    View Code

    5、测试用例

     1 package com.aikucun.bill.controller;
     2 
     3 import org.junit.Test;
     4 import org.junit.runner.RunWith;
     5 import org.springframework.beans.factory.annotation.Autowired;
     6 import org.springframework.boot.test.context.SpringBootTest;
     7 import org.springframework.test.context.junit4.SpringRunner;
     8 
     9 import com.aikucun.bill.util.RedisUtil;
    10 
    11 @RunWith(SpringRunner.class)
    12 @SpringBootTest
    13 public class RedisInfoTest {
    14 
    15     @Autowired
    16     private RedisUtil redisUtil;
    17 
    18     @Test
    19     public void test() {
    20         System.out.println("--------------------");
    21         redisUtil.set("PANSHI-BILL-TEST1", "wwwwwwwwwwwwwwwwwww");
    22         Object value1 = redisUtil.get("PANSHI-BILL-TEST1");
    23         Object value2 = redisUtil.get("wwww");
    24         System.out.println("value1:" + value1 + "            value2:" + value2);
    25     }
    26 }
    View Code

    6、分布式锁:

    有3种:

    1、基于数据库乐观锁

    如:SELECT * from goods where ID =1 for update;

       UPDATE goods set stock = stock - 1;

    2、基于zookeeper:

    大致思想即为:每个客户端对某个功能加锁时,在zookeeper上的与该功能对应的指定节点的目录下,生成一个唯一的瞬时有序节点。判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。当释放锁的时候,只需将这个瞬时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。

    优点:锁安全性高,zk可持久化   缺点: 性能开销比较高。因为其需要动态产生、销毁瞬时节点来实现锁功能

    3、基于redis:(推荐)

    1)方法一:springboot2.x 以上使用redis时,用了lettuce封装,比起jedis线程安全:

    需要的依赖:

        <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
                <version>2.0.5.RELEASE</version>
        </dependency>
    View Code

    封装工具类:

    package com.aikucun.delivery.util;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.data.redis.connection.RedisStringCommands;
    import org.springframework.data.redis.connection.ReturnType;
    import org.springframework.data.redis.core.RedisCallback;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.types.Expiration;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    import java.nio.charset.Charset;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @Description: 分布式锁工具类
     * @Author:
     * @CreateDate:
     */
    @Component
    @Slf4j
    public class RedisLock {
        public static final String UNLOCK_LUA;
    
        /**
         * 释放锁脚本,原子操作
         */
        static {
            StringBuilder sb = new StringBuilder();
            sb.append("if redis.call("get",KEYS[1]) == ARGV[1] ");
            sb.append("then ");
            sb.append("    return redis.call("del",KEYS[1]) ");
            sb.append("else ");
            sb.append("    return 0 ");
            sb.append("end ");
            UNLOCK_LUA = sb.toString();
        }
    
        @Resource
        private RedisTemplate redisTemplate;
    
        /**
         * 获取分布式锁,原子操作
         * @param lockKey
         * @param requestId 唯一ID, 可以使用UUID.randomUUID().toString();
         * @param expire
         * @param timeUnit
         * @return
         */
        public boolean tryLock(String lockKey, String requestId, long expire, TimeUnit timeUnit) {
            try {
                RedisCallback<Boolean> callback = (connection) -> {
                    return connection.set(lockKey.getBytes(Charset.forName("UTF-8")), requestId.getBytes(Charset.forName("UTF-8")), Expiration.seconds(timeUnit.toSeconds(expire)), RedisStringCommands.SetOption.SET_IF_ABSENT);
                };
                return (Boolean) redisTemplate.execute(callback);
            } catch (Exception e) {
                log.error("redis lock error.", e);
            }
            return false;
        }
    
        /**
         * 释放锁
         * @param lockKey
         * @param requestId 唯一ID
         * @return
         */
        public boolean releaseLock(String lockKey, String requestId) {
            RedisCallback<Boolean> callback = (connection) -> {
                return connection.eval(UNLOCK_LUA.getBytes(), ReturnType.BOOLEAN, 1, lockKey.getBytes(Charset.forName("UTF-8")), requestId.getBytes(Charset.forName("UTF-8")));
            };
            return (Boolean) redisTemplate.execute(callback);
        }
    
        /**
         * 获取Redis锁的value值
         * @param lockKey
         * @return
         */
        public String get(String lockKey) {
            try {
                RedisCallback<String> callback = (connection) -> {
                    return new String(connection.get(lockKey.getBytes()), Charset.forName("UTF-8"));
                };
                return (String) redisTemplate.execute(callback);
            } catch (Exception e) {
                log.error("get redis occurred an exception", e);
            }
            return null;
        }
    
    }
    View Code

    2)方法二:也可以用spring-data-redis加jedis或者lettuce

    @Repository
    public class RedisLock {
    
        private static final String unlockScript =
                  "if redis.call("get",KEYS[1]) == ARGV[1]
    "
                + "then
    "
                + "    return redis.call("del",KEYS[1])
    "
                + "else
    "
                + "    return 0
    "
                + "end";
    
        private StringRedisTemplate redisTemplate;
    
        public RedisLock(StringRedisTemplate redisTemplate) {
            this.redisTemplate = redisTemplate;
        }
    
        public String lock(String name, long expire, long timeout) throws InterruptedException {
            Assert.isTrue(timeout>0 && timeout<60000, "timeout must greater than 0 and less than 1 min");
    
            long startTime = System.currentTimeMillis();
            String token;
            do{
                token = tryLock(name, expire);
                if(token == null) {
                    if((System.currentTimeMillis()-startTime) > (timeout-50))
                        break;
                    Thread.sleep(50); //try 50 per sec
                }
            }while(token==null);
    
            return token;
        }
      
        public String tryLock(String name, long expire) {
            Assert.notNull(name, "Lock name must not be null");
            Assert.isTrue(expire>0, "expire must greater than 0");
    
            String token = UUID.randomUUID().toString();
            RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
            RedisConnection conn = factory.getConnection();
            try{
                Boolean result = conn.set(name.getBytes(Charset.forName("UTF-8")), token.getBytes(Charset.forName("UTF-8")), 
                        Expiration.from(expire, TimeUnit.MILLISECONDS), RedisStringCommands.SetOption.SET_IF_ABSENT);
                if(result!=null && result)
                    return token;
            }finally {
                RedisConnectionUtils.releaseConnection(conn, factory);
            }
            return null;
        }
    
        public boolean unlock(String name, String token) {
            Assert.notNull(name, "Lock name must not be null");
            Assert.notNull(token, "Token must not be null");
    
            byte[][] keysAndArgs = new byte[2][];
            keysAndArgs[0] = name.getBytes(Charset.forName("UTF-8"));
            keysAndArgs[1] = token.getBytes(Charset.forName("UTF-8"));
            RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
            RedisConnection conn = factory.getConnection();
            try {
                Long result = (Long)conn.scriptingCommands().eval(unlockScript.getBytes(Charset.forName("UTF-8")), ReturnType.INTEGER, 1, keysAndArgs);
                if(result!=null && result>0)
                    return true;
            }finally {
                RedisConnectionUtils.releaseConnection(conn, factory);
            }
            
            return false;
        }
    }
    View Code

      





  • 相关阅读:
    idea设置全局ignore
    win 2012 安装mysql 5.7.20 及报错 This application requires Visual Studio 2013 Redistributable. Please ins
    win 2012 安装mysql 5.7.20 及报错 This application requires Visual Studio 2013 Redistr
    kafka 删除 topic
    java编译中出现了Exception in thread “main" java.lang.UnsupportedClassVersionError
    Centos中使用yum安装java时,没有jps的问题的解决
    Spring 整合Junit
    Spring纯注解配置
    Spring 基于注解的 IOC 配置
    打印java系统的信息
  • 原文地址:https://www.cnblogs.com/wzk-0000/p/6866333.html
Copyright © 2011-2022 走看看