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());
}
}