resources目录下配置文件

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!-- @Value -->
    <!-- 将多个配置文件位置放到列表中 -->
    <bean id="configProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
        <property name="locations">
            <list>
                <value>classpath*:redis.properties</value>
            </list>
        </property>
    </bean>
    <!-- 将配置文件读取到容器中,交给Spring管理 -->
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
        <property name="properties" ref="configProperties"/>
    </bean>

    <beans>
        <!--定义包扫描路径-->
        <context:component-scan base-package="com.diy.sigmund"/>
        <!--与com.diy.sigmund.conifg.BeansConfig作用相同@Configuration,@Bean(initMethod = "initJedisCluster")-->
<!--        <bean id="redisUtil" class="com.diy.sigmund.redis.RedisUtil" init-method="initJedisCluster">-->
<!--            <constructor-arg ref="redisClusterConfig"/>-->
<!--        </bean>-->
    </beans>
</beans>

log4j. properties

### 设置###
log4j.rootLogger=debug,stdout,D,E
### 输出信息到控制抬,仅输出INFO级别以上 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.Threshold=INFO
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
### 输出DEBUG 级别以上的日志到=F:/project/knight/github/logs/app.log ###
log4j.appender.D=org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File=F:/project/knight/github/logs/app.log
log4j.appender.D.Append=true
log4j.appender.D.Threshold=DEBUG
log4j.appender.D.layout=org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
### 输出ERROR 级别以上的日志到=F:/project/knight/github/logs/error.log ###
log4j.appender.E=org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File=F:/project/knight/github/logs/error.log
log4j.appender.E.Append=true
log4j.appender.E.Threshold=ERROR
log4j.appender.E.layout=org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

redis.properties

redis.cluster.nodes=192.168.92.100:8000,192.168.92.100:8001,192.168.92.100:8002,192.168.92.100:8003,192.168.92.100:8004,192.168.92.100:8005
redis.cluster.pwd=123456

工具类

JsonUtil

package com.diy.sigmund.redis;

import java.util.Objects;
import java.util.function.Supplier;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * @author ylm-sigmund
 * @since 2020/11/21 13:16
 */
public class JsonUtil {
    /**
     * 对象映射器
     */
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    /**
     * 日志
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(JsonUtil.class);

    /**
     * 序列化对象为字符串
     * 
     * OBJECT_MAPPER.writerWithDefaultPrettyPrinter() 漂亮打印机
     * 
     * @param value
     *            value
     * @return String
     */
    public static String toJson(Object value) {
        if (Objects.isNull(value)) {
            return "";
        }
        String jsonString = "";
        try {
            jsonString = OBJECT_MAPPER.writeValueAsString(value);
        } catch (Exception e) {
            LOGGER.error("toJson error, value is {}", value);
        }
        return jsonString;
    }

    /**
     * 反序列化对象
     * 
     * 例如:JsonUtil.toObject(mapJson, () -> new TypeReference<Map<Integer, String>>() {});
     * 
     * @param content
     *            content
     * @param supplier
     *            supplier
     * @param <T>
     *            <T>
     * @return 结果信息
     */
    public static <T> T toObject(String content, Supplier<TypeReference<T>> supplier) {
        T object = null;
        try {
            final TypeReference<T> valueType = supplier.get();
            object = OBJECT_MAPPER.readValue(content, valueType);
        } catch (Exception e) {
            LOGGER.error("toObject error, content is {}", content);
        }
        return object;
    }
}

RedisClusterConfig

package com.diy.sigmund.redis;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author ylm-sigmund
 * @since 2020/11/20 20:43
 */
@Component
public class RedisClusterConfig {
    @Value("${redis.cluster.nodes}")
    private String nodes;
    @Value("${redis.cluster.pwd}")
    private String pwd;

    public String getNodes() {
        return nodes;
    }

    public String getPwd() {
        return pwd;
    }
}

RedisUtil

package com.diy.sigmund.redis;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.core.type.TypeReference;

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @author ylm-sigmund
 * @since 2020/11/20 20:43
 */
@Component
public class RedisUtil {
    private static JedisCluster jedisCluster;

    private RedisClusterConfig redisClusterConfig;

    public RedisUtil(RedisClusterConfig redisClusterConfig) {
        this.redisClusterConfig = redisClusterConfig;
    }

    /**
     * 日志
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(RedisUtil.class);

    /**
     * 解析redis机器的ip+port
     * 
     * @param nodes
     *            nodes
     * @return node
     */
    private Set<HostAndPort> parseHostAndPort(String nodes) {
        Set<HostAndPort> node = new HashSet<>();
        String[] hostAndPorts = nodes.split(",");
        for (String hostAndPort : hostAndPorts) {
            String host = hostAndPort.split(":")[0];
            int port = Integer.parseInt(hostAndPort.split(":")[1]);
            HostAndPort hap = new HostAndPort(host, port);
            node.add(hap);
        }
        return node;
    }

    /**
     * 初始化 JedisCluster
     */

    private void initJedisCluster() {
        Set<HostAndPort> node = parseHostAndPort(redisClusterConfig.getNodes());
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // 注意:这里超时时间不要太短,他会有超时重试机制。而且其他像httpclient、dubbo等RPC框架也要注意这点
        jedisCluster = new JedisCluster(node, 1000, 1000, 1, redisClusterConfig.getPwd(), jedisPoolConfig);
    }

    /**
     * 将键 key 的值设置为 value , 并将键 key 的生存时间设置为 seconds 秒钟。
     *
     * 如果键 key 已经存在, 那么 SETEX 命令将覆盖已有的值。
     * 
     * @param key
     *            key
     * @param value
     *            value
     * @param timeout
     *            超时时间
     * @param timeUnit
     *            时间单位 SECONDS(秒),MINUTES(分),HOURS(时),DAYS(天),
     * @return 成功true或者失败false
     */
    public static boolean setex(String key, Object value, int timeout, TimeUnit timeUnit) {
        try {
            int seconds = (int)timeUnit.toSeconds(timeout);
            jedisCluster.setex(key, seconds, JsonUtil.toJson(value));
            return true;
        } catch (Exception e) {
            LOGGER.error("Redis setex key {} value {} timeout {} timeUnit {}", key, value, timeout, timeUnit);
            return false;
        }
    }

    /**
     * 返回与键 key 相关联的值(已将字符串处理为对象)。
     * 
     * 例如:RedisUtil.get(key1, () -> new TypeReference<User>() {});
     *
     * @param key
     *            key
     * @param supplier
     *            supplier
     * @param <T>
     *            <T>
     * @return return
     */
    public static <T> T get(String key, Supplier<TypeReference<T>> supplier) {
        try {
            final String json = jedisCluster.get(key);
            return JsonUtil.toObject(json, supplier);
        } catch (Exception e) {
            LOGGER.error("Redis get key {}", key);
            return null;
        }
    }

    /**
     * Redis Expire 命令用于设置 key 的过期时间。key 过期后将不再可用。若 key 存在,则会覆盖过期时间
     * 
     * @param key
     *            key
     * @param timeout
     *            timeout
     * @param timeUnit
     *            timeUnit
     * @return Long
     */
    public static Long expire(String key, int timeout, TimeUnit timeUnit) {
        try {
            int seconds = (int)timeUnit.toSeconds(timeout);
            return jedisCluster.expire(key, seconds);
        } catch (Exception e) {
            LOGGER.error("Redis expire key {}", key);
            return null;
        }
    }

    /**
     * Redis EXISTS 命令用于检查给定 key 是否存在。
     * 
     * @param key
     *            key
     * @return Boolean
     */
    public static Boolean exists(String key) {
        try {
            return jedisCluster.exists(key);
        } catch (Exception e) {
            LOGGER.error("Redis exists key {}", key);
            return false;
        }
    }

    /**
     * Redis TTL 命令以秒为单位返回 key 的剩余过期时间。
     * 
     * @param key
     *            key
     * @return Long
     */
    public static Long ttl(String key) {
        try {
            return jedisCluster.ttl(key);
        } catch (Exception e) {
            LOGGER.error("Redis ttl key {}", key);
            return null;
        }
    }

    /**
     * Redis DEL 命令用于删除已存在的键。不存在的 key 会被忽略。
     * 
     * @param key
     *            key
     * @return Long
     */
    public static Long del(String key) {
        try {
            return jedisCluster.del(key);
        } catch (Exception e) {
            LOGGER.error("Redis del key {}", key);
            return null;
        }
    }
}

配置类

BeansConfig

package com.diy.sigmund.conifg;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import com.diy.sigmund.redis.RedisClusterConfig;
import com.diy.sigmund.redis.RedisUtil;

/**
 * @author ylm-sigmund
 * @since 2020/11/22 12:34
 */
@Configuration
@ComponentScan("com.diy.sigmund")
public class BeansConfig {
    public RedisClusterConfig redisClusterConfig() {
        return new RedisClusterConfig();
    }

    @Bean(initMethod = "initJedisCluster")
    public RedisUtil redisUtil(RedisClusterConfig redisClusterConfig) {
        return new RedisUtil(redisClusterConfig);
    }
}

pom文件

<dependencies>
        <!--Spring AOP-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.2.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.11.2</version>
        </dependency>


        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>

        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.2</version>
        </dependency>

        <!--slf4j只是一个日志标准,并不是日志系统的具体实现-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>
        <!--slf4j-simple是slf4j的具体实现-->
<!--        <dependency>-->
<!--            <groupId>org.slf4j</groupId>-->
<!--            <artifactId>slf4j-simple</artifactId>-->
<!--            <version>1.7.30</version>-->
<!--            <scope>test</scope>-->
<!--        </dependency>-->
        <!--log4j并不直接实现slf4j,但是有专门的一层桥接slf4j-log4j12来实现slf4j-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.30</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.6.RELEASE</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

单元测试

package com.diy.sigmund.redis;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.diy.sigmund.entity.User;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;

/**
 * @author ylm-sigmund
 * @since 2020/11/20 20:50
 */
// @ComponentScan("com.diy.sigmund")//该注解在单元测试下无效
@RunWith(SpringJUnit4ClassRunner.class)
// @ContextConfiguration属性locations和classes只能取一个,否则会报错configure one or the other, but not both.
// @ContextConfiguration(locations = {"classpath:applicationContext.xml"},classes = BeansConfig.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class RedisClusterConfigTest {
    /**
     * 日志
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(RedisClusterConfigTest.class);
    @Autowired
    private RedisClusterConfig redisClusterConfig;

    @Test
    public void setex() {
        final User user = new User();
        user.setId(1234);
        user.setName("jackson包");
        String key1 = "qqq";
        LOGGER.info("key1 是否存在 {}", RedisUtil.exists(key1));
        final boolean setex = RedisUtil.setex(key1, user, 1, TimeUnit.MINUTES);
        LOGGER.info("setex key1 是否成功 {}", setex);
        LOGGER.info("key1 过期时间 {} 秒", RedisUtil.ttl(key1));
        final Long expire = RedisUtil.expire(key1, 1, TimeUnit.HOURS);
        LOGGER.info("expire key1 设置过期时间 {}", expire);
        LOGGER.info("key1 过期时间 {} 秒", RedisUtil.ttl(key1));
        final User user1 = RedisUtil.get(key1, () -> new TypeReference<User>() {});
        LOGGER.info("get key1 {}", Objects.requireNonNull(user1).toString());
        final Long del = RedisUtil.del(key1);
        LOGGER.info("del key1 {}", del);
        LOGGER.info("key1 是否存在 {}", RedisUtil.exists(key1));
        final Long expire1 = RedisUtil.expire(key1, 1, TimeUnit.MINUTES);
        LOGGER.info("expire key1 设置过期时间 {}", expire1);
        LOGGER.info("key1 过期时间 {} 秒", RedisUtil.ttl(key1));
    }

    /**
     * 测试序列化和反序列化
     * 
     * @throws JsonProcessingException
     *             JsonProcessingException
     */
    @Test
    public void testJackson() throws JsonProcessingException {
        LOGGER.info("key1 是否存在 {}", RedisUtil.exists("key1"));
        // 对象
        final User user = new User();
        user.setId(1234);
        user.setName("jackson包");
        // 序列化
        final String json = JsonUtil.toJson(user);
        LOGGER.info("user is {}", json);
        // 反序列化
        final User user1 = JsonUtil.toObject(json, () -> new TypeReference<User>() {});
        LOGGER.info(user1.toString());

        // map
        final Map<Integer, String> map = new HashMap<Integer, String>();
        map.put(2234, "jackson包");
        // 序列化
        final String mapJson = JsonUtil.toJson(map);
        LOGGER.info(mapJson);
        // 反序列化
        final Map<Integer, String> hashMap =
            JsonUtil.toObject(mapJson, () -> new TypeReference<Map<Integer, String>>() {});
        LOGGER.info(hashMap.toString());

        // list
        final List<User> collect = Stream.of(user).collect(Collectors.toList());
        // 序列化
        final String listJson = JsonUtil.toJson(collect);
        LOGGER.info(listJson);
        // 反序列化
        final List<User> arrayList = JsonUtil.toObject(listJson, () -> new TypeReference<List<User>>() {});
        LOGGER.info(arrayList.toString());
    }

}