feat(utils): 添加 AES 加密解密工具类
All checks were successful
Gitea Actions Build / Build (push) Successful in 51s

- 新增 AesUtils 类,提供 AES/GCM 模式的加密解密方法- 更新 .prettierrc配置,将 useTabs 设置为 true
- 重构 Base64Utils 和 Collection 类的代码结构- 简化 ComponentBean 注解定义
This commit is contained in:
Armamem0t 2025-08-17 19:12:08 +08:00
parent ceef51506d
commit 34dccd1895
Signed by: minglipro
GPG Key ID: 5F355A77B22AA93B
48 changed files with 4214 additions and 3952 deletions

View File

@ -4,5 +4,5 @@
"prettier-plugin-java" "prettier-plugin-java"
], ],
"tabWidth": 4, "tabWidth": 4,
"useTabs": false "useTabs": true
} }

View File

@ -1,4 +1,4 @@
JDKVERSIONS=1.8 JDKVERSIONS=1.8
GROUPSID=com.mingliqiye.utils GROUPSID=com.mingliqiye.utils
ARTIFACTID=mingli-utils ARTIFACTID=mingli-utils
VERSIONS=1.0.5 VERSIONS=1.0.7

View File

@ -1,14 +1,14 @@
{ {
"name": "maven-repository", "name": "maven-repository",
"version": "1.0.0", "version": "1.0.0",
"scripts": { "scripts": {
"build": "gradle build-jar", "build": "gradle build-jar",
"buildw": "gradlew build-jar", "buildw": "gradlew build-jar",
"format": "prettier --write \"**/*.{js,ts,jsx,tsx,cjs,cts,mjs,mts,vue,astro,json,java}\"" "format": "prettier --write \"**/*.{js,ts,jsx,tsx,cjs,cts,mjs,mts,vue,astro,json,java}\""
}, },
"packageManager": "pnpm@10.4.1", "packageManager": "pnpm@10.4.1",
"devDependencies": { "devDependencies": {
"prettier-plugin-java": "^2.7.1", "prettier-plugin-java": "^2.7.1",
"prettier": "^3.6.2" "prettier": "^3.6.2"
} }
} }

View File

@ -4,36 +4,49 @@ import com.mingliqiye.utils.collection.Lists;
import com.mingliqiye.utils.string.StringUtil; import com.mingliqiye.utils.string.StringUtil;
import com.mingliqiye.utils.time.DateTime; import com.mingliqiye.utils.time.DateTime;
import com.mingliqiye.utils.time.Formatter; import com.mingliqiye.utils.time.Formatter;
import java.util.List; import java.util.List;
public class Main { public class Main {
public static void main(String[] args) { public static void main(String[] args) {
// 字符串工具使用示例 // 字符串工具使用示例
System.out.println("=== 字符串工具使用示例 ==="); System.out.println("=== 字符串工具使用示例 ===");
String formatted = StringUtil.format("你好 {},今天是{}年{}月{}日", "张三", 2025, 7, 25); String formatted = StringUtil.format(
System.out.println(formatted); "你好 {},今天是{}年{}月{}日",
"张三",
2025,
7,
25
);
System.out.println(formatted);
List<String> fruits = Lists.newArrayList("苹果", "香蕉", "橙子"); List<String> fruits = Lists.newArrayList("苹果", "香蕉", "橙子");
String joined = StringUtil.join(", ", fruits); String joined = StringUtil.join(", ", fruits);
System.out.println("水果列表: " + joined); System.out.println("水果列表: " + joined);
// 时间工具使用示例 // 时间工具使用示例
System.out.println("\n=== 时间工具使用示例 ==="); System.out.println("\n=== 时间工具使用示例 ===");
DateTime now = DateTime.now(); DateTime now = DateTime.now();
System.out.println("当前时间: " + now); System.out.println("当前时间: " + now);
System.out.println("标准格式: " + now.format(Formatter.STANDARD_DATETIME)); System.out.println(
"标准格式: " + now.format(Formatter.STANDARD_DATETIME)
);
DateTime specificDate = DateTime.of(2025, 1, 1, 12, 0, 0); DateTime specificDate = DateTime.of(2025, 1, 1, 12, 0, 0);
System.out.println("指定时间: " + specificDate.format(Formatter.STANDARD_DATETIME)); System.out.println(
"指定时间: " + specificDate.format(Formatter.STANDARD_DATETIME)
);
// 集合工具使用示例 // 集合工具使用示例
System.out.println("\n=== 集合工具使用示例 ==="); System.out.println("\n=== 集合工具使用示例 ===");
List<Integer> numbers = Lists.newArrayList(10, 20, 30); List<Integer> numbers = Lists.newArrayList(10, 20, 30);
System.out.println("数字列表: " + numbers); System.out.println("数字列表: " + numbers);
List<String> linkedList = Lists.newLinkedList("第一个", "第二个", "第三个"); List<String> linkedList = Lists.newLinkedList(
System.out.println("链表内容: " + linkedList); "第一个",
} "第二个",
"第三个"
);
System.out.println("链表内容: " + linkedList);
}
} }

View File

@ -0,0 +1,112 @@
package com.mingliqiye.utils.aes;
import com.mingliqiye.utils.base64.Base64Utils;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.SecureRandom;
public class AesUtils {
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES/GCM/NoPadding";
private static final int GCM_IV_LENGTH = 12;
private static final int GCM_TAG_LENGTH = 16;
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
/**
* AES加密方法使用GCM模式
* @param sSrc 待加密的字符串
* @param sKey 加密密钥
* @return 加密后的字符串格式为 IV:EncryptedData+Tag均为Base64编码
* @throws GeneralSecurityException 加密错误
*/
public static String encrypt(String sSrc, String sKey)
throws GeneralSecurityException {
if (sKey == null) {
return null;
}
// 生成密钥
SecretKeySpec secretKeySpec = createSecretKey(sKey);
// 生成安全随机IV
byte[] iv = new byte[GCM_IV_LENGTH];
SECURE_RANDOM.nextBytes(iv);
// 初始化加密器
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(
GCM_TAG_LENGTH * 8,
iv
);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec);
// 执行加密
byte[] encrypted = cipher.doFinal(
sSrc.getBytes(StandardCharsets.UTF_8)
);
// 将IV和加密数据包含认证标签组合返回
return Base64Utils.encode(
(Base64Utils.encode(iv) +
":" +
Base64Utils.encode(encrypted)).getBytes()
);
}
/**
* AES解密方法使用GCM模式
* @param sSrc 待解密的字符串格式为 IV:EncryptedData+Tag均为Base64编码
* @param sKey 解密密钥
* @return 解密后的原始字符串
*/
public static String decrypt(String sSrc, String sKey) {
if (sKey == null) {
return null;
}
try {
// 分割IV和加密数据
String sSrcs = new String(Base64Utils.decode(sSrc));
String[] parts = sSrcs.split(":", 2);
if (parts.length != 2) {
return null;
}
byte[] iv = Base64Utils.decode(parts[0]);
byte[] encryptedData = Base64Utils.decode(parts[1]);
if (iv.length != GCM_IV_LENGTH) {
return null;
}
SecretKeySpec secretKeySpec = createSecretKey(sKey);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(
GCM_TAG_LENGTH * 8,
iv
);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, gcmParameterSpec);
byte[] original = cipher.doFinal(encryptedData);
return new String(original, StandardCharsets.UTF_8);
} catch (Exception e) {
return null;
}
}
/**
* 创建AES密钥支持任意长度的密钥
* @param sKey 字符串密钥
* @return SecretKeySpec对象
* @throws Exception 可能抛出的异常
*/
private static SecretKeySpec createSecretKey(String sKey)
throws GeneralSecurityException {
byte[] key = sKey.getBytes(StandardCharsets.UTF_8);
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(key);
return new SecretKeySpec(digest, ALGORITHM);
}
}

View File

@ -10,163 +10,163 @@ import java.util.Base64;
*/ */
public class Base64Utils { public class Base64Utils {
// Base64编码器实例 // Base64编码器实例
private static final Base64.Encoder BASE_64_ENCODER = Base64.getEncoder(); private static final Base64.Encoder BASE_64_ENCODER = Base64.getEncoder();
// Base64解码器实例 // Base64解码器实例
private static final Base64.Decoder BASE_64_DECODER = Base64.getDecoder(); private static final Base64.Decoder BASE_64_DECODER = Base64.getDecoder();
/** /**
* 对字节数组进行Base64编码 * 对字节数组进行Base64编码
* *
* @param bytes 待编码的字节数组 * @param bytes 待编码的字节数组
* @return 编码后的Base64字符串 * @return 编码后的Base64字符串
*/ */
public static String encode(byte[] bytes) { public static String encode(byte[] bytes) {
return BASE_64_ENCODER.encodeToString(bytes); return BASE_64_ENCODER.encodeToString(bytes);
} }
/** /**
* 对文件内容进行Base64编码 * 对文件内容进行Base64编码
* *
* @param file 待编码的文件对象 * @param file 待编码的文件对象
* @return 编码后的Base64字符串 * @return 编码后的Base64字符串
* @throws RuntimeException 如果读取文件时发生IO异常 * @throws RuntimeException 如果读取文件时发生IO异常
*/ */
public static String encode(File file) { public static String encode(File file) {
try { try {
byte[] bytes = java.nio.file.Files.readAllBytes(file.toPath()); byte[] bytes = java.nio.file.Files.readAllBytes(file.toPath());
return encode(bytes); return encode(bytes);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
/** /**
* 根据文件路径对文件内容进行Base64编码 * 根据文件路径对文件内容进行Base64编码
* *
* @param filePath 文件路径 * @param filePath 文件路径
* @return 编码后的Base64字符串 * @return 编码后的Base64字符串
*/ */
public static String encode(String filePath) { public static String encode(String filePath) {
return encode(new File(filePath)); return encode(new File(filePath));
} }
/** /**
* 安全地对文件内容进行Base64编码出错时返回null * 安全地对文件内容进行Base64编码出错时返回null
* *
* @param file 待编码的文件对象 * @param file 待编码的文件对象
* @return 编码后的Base64字符串出错时返回null * @return 编码后的Base64字符串出错时返回null
*/ */
public static String encodeSafe(File file) { public static String encodeSafe(File file) {
try { try {
return encode(file); return encode(file);
} catch (Exception e) { } catch (Exception e) {
return null; return null;
} }
} }
/** /**
* 安全地根据文件路径对文件内容进行Base64编码出错时返回null * 安全地根据文件路径对文件内容进行Base64编码出错时返回null
* *
* @param filePath 文件路径 * @param filePath 文件路径
* @return 编码后的Base64字符串出错时返回null * @return 编码后的Base64字符串出错时返回null
*/ */
public static String encodeSafe(String filePath) { public static String encodeSafe(String filePath) {
try { try {
return encode(filePath); return encode(filePath);
} catch (Exception e) { } catch (Exception e) {
return null; return null;
} }
} }
/** /**
* 对Base64字符串进行解码 * 对Base64字符串进行解码
* *
* @param base64 待解码的Base64字符串 * @param base64 待解码的Base64字符串
* @return 解码后的字节数组 * @return 解码后的字节数组
*/ */
public static byte[] decode(String base64) { public static byte[] decode(String base64) {
return BASE_64_DECODER.decode(base64); return BASE_64_DECODER.decode(base64);
} }
/** /**
* 安全地对Base64字符串进行解码出错时返回null * 安全地对Base64字符串进行解码出错时返回null
* *
* @param base64 待解码的Base64字符串 * @param base64 待解码的Base64字符串
* @return 解码后的字节数组出错时返回null * @return 解码后的字节数组出错时返回null
*/ */
public static byte[] decodeSafe(String base64) { public static byte[] decodeSafe(String base64) {
try { try {
return decode(base64); return decode(base64);
} catch (Exception e) { } catch (Exception e) {
return null; return null;
} }
} }
/** /**
* 将Base64字符串解码并写入指定文件 * 将Base64字符串解码并写入指定文件
* *
* @param base64 待解码的Base64字符串 * @param base64 待解码的Base64字符串
* @param file 目标文件对象 * @param file 目标文件对象
* @throws RuntimeException 如果写入文件时发生IO异常 * @throws RuntimeException 如果写入文件时发生IO异常
*/ */
public static void decodeToFile(String base64, File file) { public static void decodeToFile(String base64, File file) {
try (FileOutputStream fos = new FileOutputStream(file)) { try (FileOutputStream fos = new FileOutputStream(file)) {
byte[] bytes = decode(base64); byte[] bytes = decode(base64);
fos.write(bytes); fos.write(bytes);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
/** /**
* 将Base64字符串解码并写入指定路径的文件 * 将Base64字符串解码并写入指定路径的文件
* *
* @param base64 待解码的Base64字符串 * @param base64 待解码的Base64字符串
* @param filePath 目标文件路径 * @param filePath 目标文件路径
*/ */
public static void decodeToFile(String base64, String filePath) { public static void decodeToFile(String base64, String filePath) {
decodeToFile(base64, new File(filePath)); decodeToFile(base64, new File(filePath));
} }
/** /**
* 安全地将Base64字符串解码并写入指定文件出错时返回false * 安全地将Base64字符串解码并写入指定文件出错时返回false
* *
* @param base64 待解码的Base64字符串 * @param base64 待解码的Base64字符串
* @param file 目标文件对象 * @param file 目标文件对象
* @return 成功写入返回true否则返回false * @return 成功写入返回true否则返回false
*/ */
public static boolean decodeToFileSafe(String base64, File file) { public static boolean decodeToFileSafe(String base64, File file) {
try { try {
decodeToFile(base64, file); decodeToFile(base64, file);
return true; return true;
} catch (Exception e) { } catch (Exception e) {
return false; return false;
} }
} }
/** /**
* 安全地将Base64字符串解码并写入指定路径的文件出错时返回false * 安全地将Base64字符串解码并写入指定路径的文件出错时返回false
* *
* @param base64 待解码的Base64字符串 * @param base64 待解码的Base64字符串
* @param filePath 目标文件路径 * @param filePath 目标文件路径
* @return 成功写入返回true否则返回false * @return 成功写入返回true否则返回false
*/ */
public static boolean decodeToFileSafe(String base64, String filePath) { public static boolean decodeToFileSafe(String base64, String filePath) {
return decodeToFileSafe(base64, new File(filePath)); return decodeToFileSafe(base64, new File(filePath));
} }
/** /**
* 对字节数组中指定范围的数据进行Base64编码 * 对字节数组中指定范围的数据进行Base64编码
* *
* @param bytes 源字节数组 * @param bytes 源字节数组
* @param offset 起始偏移量 * @param offset 起始偏移量
* @param length 要编码的数据长度 * @param length 要编码的数据长度
* @return 编码后的Base64字符串 * @return 编码后的Base64字符串
*/ */
public static String encodeBytes(byte[] bytes, int offset, int length) { public static String encodeBytes(byte[] bytes, int offset, int length) {
byte[] data = new byte[length]; byte[] data = new byte[length];
System.arraycopy(bytes, offset, data, 0, length); System.arraycopy(bytes, offset, data, 0, length);
return encode(data); return encode(data);
} }
} }

View File

@ -2,7 +2,6 @@ package com.mingliqiye.utils.bean;
import com.mingliqiye.utils.bean.annotation.ComponentBean; import com.mingliqiye.utils.bean.annotation.ComponentBean;
import com.mingliqiye.utils.bean.annotation.InjectBean; import com.mingliqiye.utils.bean.annotation.InjectBean;
import java.io.File; import java.io.File;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.net.URL; import java.net.URL;
@ -17,233 +16,233 @@ import java.util.concurrent.ConcurrentMap;
*/ */
public class Factory { public class Factory {
/** /**
* 存储所有已注册的Bean实例键为Bean名称值为Bean实例 * 存储所有已注册的Bean实例键为Bean名称值为Bean实例
*/ */
public static final ConcurrentMap<String, Object> beans = public static final ConcurrentMap<String, Object> beans =
new ConcurrentHashMap<>(); new ConcurrentHashMap<>();
/** /**
* 存储按类型查找的Bean实例键为Bean的Class对象值为Bean实例 * 存储按类型查找的Bean实例键为Bean的Class对象值为Bean实例
*/ */
private static final ConcurrentMap<Class<?>, Object> typeBeans = private static final ConcurrentMap<Class<?>, Object> typeBeans =
new ConcurrentHashMap<>(); new ConcurrentHashMap<>();
/** /**
* 私有构造函数防止外部实例化该类 * 私有构造函数防止外部实例化该类
*/ */
private Factory() {} private Factory() {}
/** /**
* 自动扫描指定类所在包下的所有类并注册为Bean * 自动扫描指定类所在包下的所有类并注册为Bean
* *
* @param c 指定的类用于获取其所在的包 * @param c 指定的类用于获取其所在的包
* @throws IllegalArgumentException 如果传入的类为null或位于默认包中 * @throws IllegalArgumentException 如果传入的类为null或位于默认包中
*/ */
public static void autoScan(Class<?> c) { public static void autoScan(Class<?> c) {
if (c == null) { if (c == null) {
throw new IllegalArgumentException("Class cannot be null"); throw new IllegalArgumentException("Class cannot be null");
} }
Package pkg = c.getPackage(); Package pkg = c.getPackage();
if (pkg == null) { if (pkg == null) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Class is in the default package" "Class is in the default package"
); );
} }
scan(pkg.getName()); scan(pkg.getName());
} }
/** /**
* 扫描指定包路径下的所有类文件并注册其中带有@ComponentBean注解的类为Bean * 扫描指定包路径下的所有类文件并注册其中带有@ComponentBean注解的类为Bean
* *
* @param basePackage 要扫描的基础包名 * @param basePackage 要扫描的基础包名
* @throws RuntimeException 如果在扫描过程中发生异常 * @throws RuntimeException 如果在扫描过程中发生异常
*/ */
public static void scan(String basePackage) { public static void scan(String basePackage) {
try { try {
String path = basePackage.replace('.', '/'); String path = basePackage.replace('.', '/');
ClassLoader classLoader = ClassLoader classLoader =
Thread.currentThread().getContextClassLoader(); Thread.currentThread().getContextClassLoader();
Enumeration<URL> resources = null; Enumeration<URL> resources = null;
resources = classLoader.getResources(path); resources = classLoader.getResources(path);
while (resources.hasMoreElements()) { while (resources.hasMoreElements()) {
URL resource = resources.nextElement(); URL resource = resources.nextElement();
File file = new File(resource.toURI()); File file = new File(resource.toURI());
scanDirectory(file, basePackage); scanDirectory(file, basePackage);
} }
injectDependencies(); injectDependencies();
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
/** /**
* 递归扫描目录中的所有类文件并注册符合条件的类为Bean * 递归扫描目录中的所有类文件并注册符合条件的类为Bean
* *
* @param directory 当前要扫描的目录 * @param directory 当前要扫描的目录
* @param packageName 当前目录对应的包名 * @param packageName 当前目录对应的包名
* @throws Exception 如果在扫描或类加载过程中发生异常 * @throws Exception 如果在扫描或类加载过程中发生异常
*/ */
private static void scanDirectory(File directory, String packageName) private static void scanDirectory(File directory, String packageName)
throws Exception { throws Exception {
File[] files = directory.listFiles(); File[] files = directory.listFiles();
if (files == null) { if (files == null) {
return; return;
} }
for (File file : files) { for (File file : files) {
if (file.isDirectory()) { if (file.isDirectory()) {
scanDirectory(file, packageName + "." + file.getName()); scanDirectory(file, packageName + "." + file.getName());
} else if (file.getName().endsWith(".class")) { } else if (file.getName().endsWith(".class")) {
String className = String className =
packageName + '.' + file.getName().replace(".class", ""); packageName + '.' + file.getName().replace(".class", "");
registerComponent(Class.forName(className)); registerComponent(Class.forName(className));
} }
} }
} }
/** /**
* 注册一个带有@ComponentBean注解的类为Bean实例 * 注册一个带有@ComponentBean注解的类为Bean实例
* *
* @param clazz 要注册的类 * @param clazz 要注册的类
* @throws Exception 如果在实例化类或处理注解时发生异常 * @throws Exception 如果在实例化类或处理注解时发生异常
*/ */
private static void registerComponent(Class<?> clazz) throws Exception { private static void registerComponent(Class<?> clazz) throws Exception {
if (clazz.isAnnotationPresent(ComponentBean.class)) { if (clazz.isAnnotationPresent(ComponentBean.class)) {
ComponentBean component = clazz.getAnnotation(ComponentBean.class); ComponentBean component = clazz.getAnnotation(ComponentBean.class);
String name = component.value().isEmpty() String name = component.value().isEmpty()
? clazz.getName() ? clazz.getName()
: component.value(); : component.value();
Object instance = clazz.getDeclaredConstructor().newInstance(); Object instance = clazz.getDeclaredConstructor().newInstance();
beans.put(name, instance); beans.put(name, instance);
typeBeans.put(clazz, instance); typeBeans.put(clazz, instance);
for (Class<?> interfaceClass : clazz.getInterfaces()) { for (Class<?> interfaceClass : clazz.getInterfaces()) {
typeBeans.putIfAbsent(interfaceClass, instance); typeBeans.putIfAbsent(interfaceClass, instance);
} }
} }
} }
/** /**
* 对所有已注册的Bean进行依赖注入处理 * 对所有已注册的Bean进行依赖注入处理
* *
* @throws Exception 如果在注入过程中发生异常 * @throws Exception 如果在注入过程中发生异常
*/ */
private static void injectDependencies() throws Exception { private static void injectDependencies() throws Exception {
for (Object bean : beans.values()) { for (Object bean : beans.values()) {
for (Field field : bean.getClass().getDeclaredFields()) { for (Field field : bean.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(InjectBean.class)) { if (field.isAnnotationPresent(InjectBean.class)) {
InjectBean inject = field.getAnnotation(InjectBean.class); InjectBean inject = field.getAnnotation(InjectBean.class);
Object dependency = findDependency( Object dependency = findDependency(
field.getType(), field.getType(),
inject.value() inject.value()
); );
if (dependency == null) { if (dependency == null) {
throw new IllegalStateException( throw new IllegalStateException(
"No suitable dependency found for field " + "No suitable dependency found for field " +
field.getName() + field.getName() +
" in class " + " in class " +
bean.getClass().getName() bean.getClass().getName()
); );
} }
field.setAccessible(true); field.setAccessible(true);
field.set(bean, dependency); field.set(bean, dependency);
} }
} }
} }
} }
/** /**
* 根据类型和名称查找对应的依赖实例 * 根据类型和名称查找对应的依赖实例
* *
* @param type 依赖的类型 * @param type 依赖的类型
* @param name 依赖的名称可为空 * @param name 依赖的名称可为空
* @return 找到的依赖实例未找到则返回null * @return 找到的依赖实例未找到则返回null
*/ */
private static Object findDependency(Class<?> type, String name) { private static Object findDependency(Class<?> type, String name) {
if (!name.isEmpty()) { if (!name.isEmpty()) {
return beans.get(name); return beans.get(name);
} }
Object dependency = typeBeans.get(type); Object dependency = typeBeans.get(type);
if (dependency != null) { if (dependency != null) {
return dependency; return dependency;
} }
for (Class<?> interfaceType : typeBeans.keySet()) { for (Class<?> interfaceType : typeBeans.keySet()) {
if (type.isAssignableFrom(interfaceType)) { if (type.isAssignableFrom(interfaceType)) {
return typeBeans.get(interfaceType); return typeBeans.get(interfaceType);
} }
} }
return null; return null;
} }
/** /**
* 将一个对象添加到Bean容器中使用其类名作为键 * 将一个对象添加到Bean容器中使用其类名作为键
* *
* @param object 要添加的对象 * @param object 要添加的对象
* @throws RuntimeException 如果在注入依赖时发生异常 * @throws RuntimeException 如果在注入依赖时发生异常
*/ */
public static void add(Object object) { public static void add(Object object) {
Class<?> clazz = object.getClass(); Class<?> clazz = object.getClass();
String name = clazz.getName(); String name = clazz.getName();
beans.put(name, object); beans.put(name, object);
typeBeans.put(clazz, object); typeBeans.put(clazz, object);
try { try {
injectDependencies(); injectDependencies();
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
/** /**
* 将一个对象以指定名称添加到Bean容器中 * 将一个对象以指定名称添加到Bean容器中
* *
* @param name Bean的名称 * @param name Bean的名称
* @param object 要添加的对象 * @param object 要添加的对象
* @throws RuntimeException 如果在注入依赖时发生异常 * @throws RuntimeException 如果在注入依赖时发生异常
*/ */
public static void add(String name, Object object) { public static void add(String name, Object object) {
beans.put(name, object); beans.put(name, object);
typeBeans.put(object.getClass(), object); typeBeans.put(object.getClass(), object);
try { try {
injectDependencies(); injectDependencies();
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
/** /**
* 根据类型获取对应的Bean实例 * 根据类型获取对应的Bean实例
* *
* @param objclass Bean的类型 * @param objclass Bean的类型
* @param <T> Bean的泛型类型 * @param <T> Bean的泛型类型
* @return 对应类型的Bean实例未找到则返回null * @return 对应类型的Bean实例未找到则返回null
*/ */
public static <T> T get(Class<T> objclass) { public static <T> T get(Class<T> objclass) {
return objclass.cast(typeBeans.get(objclass)); return objclass.cast(typeBeans.get(objclass));
} }
/** /**
* 根据名称和类型获取对应的Bean实例 * 根据名称和类型获取对应的Bean实例
* *
* @param name Bean的名称 * @param name Bean的名称
* @param objclass Bean的类型 * @param objclass Bean的类型
* @param <T> Bean的泛型类型 * @param <T> Bean的泛型类型
* @return 对应名称和类型的Bean实例未找到则返回null * @return 对应名称和类型的Bean实例未找到则返回null
*/ */
public static <T> T get(String name, Class<T> objclass) { public static <T> T get(String name, Class<T> objclass) {
return objclass.cast(beans.get(name)); return objclass.cast(beans.get(name));
} }
/** /**
* 根据名称获取对应的Bean实例 * 根据名称获取对应的Bean实例
* *
* @param name Bean的名称 * @param name Bean的名称
* @return 对应名称的Bean实例未找到则返回null * @return 对应名称的Bean实例未找到则返回null
*/ */
public static Object get(String name) { public static Object get(String name) {
return beans.get(name); return beans.get(name);
} }
} }

View File

@ -8,5 +8,5 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
public @interface ComponentBean { public @interface ComponentBean {
String value() default ""; String value() default "";
} }

View File

@ -8,5 +8,5 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD) @Target(ElementType.FIELD)
public @interface InjectBean { public @interface InjectBean {
String value() default ""; String value() default "";
} }

View File

@ -19,60 +19,57 @@ import org.springframework.stereotype.Component;
@Component @Component
public class SpringBeanUtil implements ApplicationContextAware { public class SpringBeanUtil implements ApplicationContextAware {
/** /**
* 获取applicationContext * 获取applicationContext
*/ */
@Getter @Getter
private static ApplicationContext applicationContext; private static ApplicationContext applicationContext;
/** /**
* 通过bean名称获取Bean实例 * 通过bean名称获取Bean实例
* *
* @param name bean名称 * @param name bean名称
* @return bean实例对象 * @return bean实例对象
*/ */
public static Object getBean(String name) { public static Object getBean(String name) {
return getApplicationContext().getBean(name); return getApplicationContext().getBean(name);
} }
/** /**
* 通过bean类型获取Bean实例 * 通过bean类型获取Bean实例
* *
* @param clazz bean的Class类型 * @param clazz bean的Class类型
* @param <T> 泛型类型 * @param <T> 泛型类型
* @return 指定类型的bean实例 * @return 指定类型的bean实例
*/ */
public static <T> T getBean(Class<T> clazz) { public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz); return getApplicationContext().getBean(clazz);
} }
/** /**
* 通过bean名称和类型获取指定的Bean实例 * 通过bean名称和类型获取指定的Bean实例
* *
* @param name bean名称 * @param name bean名称
* @param clazz bean的Class类型 * @param clazz bean的Class类型
* @param <T> 泛型类型 * @param <T> 泛型类型
* @return 指定名称和类型的bean实例 * @return 指定名称和类型的bean实例
*/ */
public static <T> T getBean(String name, Class<T> clazz) { public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz); return getApplicationContext().getBean(name, clazz);
} }
/** /**
* 设置ApplicationContext上下文对象 * 设置ApplicationContext上下文对象
* 当Spring容器初始化时会自动调用此方法将ApplicationContext注入到本工具类中 * 当Spring容器初始化时会自动调用此方法将ApplicationContext注入到本工具类中
* 通过判断避免重复赋值确保只设置一次ApplicationContext * 通过判断避免重复赋值确保只设置一次ApplicationContext
* *
* @param applicationContext Spring应用上下文对象 * @param applicationContext Spring应用上下文对象
* @throws BeansException bean异常 * @throws BeansException bean异常
*/ */
@Override @Override
public void setApplicationContext( public void setApplicationContext(
@NotNull ApplicationContext applicationContext @NotNull ApplicationContext applicationContext
) throws BeansException { ) throws BeansException {
// 避免重复赋值确保只设置一次ApplicationContext SpringBeanUtil.applicationContext = applicationContext;
if (SpringBeanUtil.applicationContext == null) { }
SpringBeanUtil.applicationContext = applicationContext;
}
}
} }

View File

@ -1,13 +1,12 @@
package com.mingliqiye.utils.collection; package com.mingliqiye.utils.collection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Predicate; import java.util.function.Predicate;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* 集合工具类提供对列表和数组的常用操作方法 * 集合工具类提供对列表和数组的常用操作方法
@ -16,289 +15,289 @@ import java.util.function.Predicate;
*/ */
public class Collection { public class Collection {
/** /**
* 获取集合的第一个元素 * 获取集合的第一个元素
* *
* @param collection 集合 * @param collection 集合
* @param <T> 元素类型 * @param <T> 元素类型
* @return 第一个元素如果集合为空或为null则返回 null * @return 第一个元素如果集合为空或为null则返回 null
*/ */
@Nullable @Nullable
public static <T> T getFirst(@Nullable java.util.Collection<T> collection) { public static <T> T getFirst(@Nullable java.util.Collection<T> collection) {
if (collection == null || collection.isEmpty()) { if (collection == null || collection.isEmpty()) {
return null; return null;
} }
// 对于List类型直接获取第一个元素 // 对于List类型直接获取第一个元素
if (collection instanceof List) { if (collection instanceof List) {
return ((List<T>) collection).get(0); return ((List<T>) collection).get(0);
} }
// 对于其他Collection类型使用迭代器获取第一个元素 // 对于其他Collection类型使用迭代器获取第一个元素
return collection.iterator().next(); return collection.iterator().next();
} }
/** /**
* 获取数组的第一个元素 * 获取数组的第一个元素
* *
* @param list 数组不能为空 * @param list 数组不能为空
* @param <T> 元素类型 * @param <T> 元素类型
* @return 第一个元素如果数组为空则返回 null * @return 第一个元素如果数组为空则返回 null
*/ */
@Nullable @Nullable
public static <T> T getFirst(@NotNull T[] list) { public static <T> T getFirst(@NotNull T[] list) {
if (list.length == 0) { if (list.length == 0) {
return null; return null;
} }
return list[0]; return list[0];
} }
/** /**
* 获取集合的最后一个元素 * 获取集合的最后一个元素
* *
* @param collection 集合 * @param collection 集合
* @param <T> 元素类型 * @param <T> 元素类型
* @return 最后一个元素如果集合为空或为null则返回 null * @return 最后一个元素如果集合为空或为null则返回 null
*/ */
@Nullable @Nullable
public static <T> T getLast(@Nullable java.util.Collection<T> collection) { public static <T> T getLast(@Nullable java.util.Collection<T> collection) {
if (collection == null || collection.isEmpty()) { if (collection == null || collection.isEmpty()) {
return null; return null;
} }
// 对于List类型直接获取最后一个元素 // 对于List类型直接获取最后一个元素
if (collection instanceof List) { if (collection instanceof List) {
List<T> list = (List<T>) collection; List<T> list = (List<T>) collection;
return list.get(list.size() - 1); return list.get(list.size() - 1);
} }
// 对于其他Collection类型需要遍历到最后一个元素 // 对于其他Collection类型需要遍历到最后一个元素
T lastElement = null; T lastElement = null;
for (T element : collection) { for (T element : collection) {
lastElement = element; lastElement = element;
} }
return lastElement; return lastElement;
} }
/** /**
* 获取数组的最后一个元素 * 获取数组的最后一个元素
* *
* @param list 数组不能为空 * @param list 数组不能为空
* @param <T> 元素类型 * @param <T> 元素类型
* @return 最后一个元素如果数组为空则返回 null * @return 最后一个元素如果数组为空则返回 null
*/ */
@Nullable @Nullable
public static <T> T getLast(@NotNull T[] list) { public static <T> T getLast(@NotNull T[] list) {
if (list.length == 0) { if (list.length == 0) {
return null; return null;
} }
return list[list.length - 1]; return list[list.length - 1];
} }
/** /**
* 获取列表中指定索引的元素如果索引超出范围则返回默认值 * 获取列表中指定索引的元素如果索引超出范围则返回默认值
* *
* @param list 列表 * @param list 列表
* @param index 索引 * @param index 索引
* @param defaultValue 默认值 * @param defaultValue 默认值
* @param <T> 元素类型 * @param <T> 元素类型
* @return 指定索引的元素或默认值 * @return 指定索引的元素或默认值
*/ */
@Nullable @Nullable
public static <T> T getOrDefault( public static <T> T getOrDefault(
@NotNull List<T> list, @NotNull List<T> list,
int index, int index,
@Nullable T defaultValue @Nullable T defaultValue
) { ) {
if (index < 0 || index >= list.size()) { if (index < 0 || index >= list.size()) {
return defaultValue; return defaultValue;
} }
return list.get(index); return list.get(index);
} }
/** /**
* 获取数组中指定索引的元素如果索引超出范围则返回默认值 * 获取数组中指定索引的元素如果索引超出范围则返回默认值
* *
* @param array 数组 * @param array 数组
* @param index 索引 * @param index 索引
* @param defaultValue 默认值 * @param defaultValue 默认值
* @param <T> 元素类型 * @param <T> 元素类型
* @return 指定索引的元素或默认值 * @return 指定索引的元素或默认值
*/ */
@Nullable @Nullable
public static <T> T getOrDefault( public static <T> T getOrDefault(
@NotNull T[] array, @NotNull T[] array,
int index, int index,
@Nullable T defaultValue @Nullable T defaultValue
) { ) {
if (index < 0 || index >= array.length) { if (index < 0 || index >= array.length) {
return defaultValue; return defaultValue;
} }
return array[index]; return array[index];
} }
/** /**
* 获取列表的安全子列表自动处理边界情况 * 获取列表的安全子列表自动处理边界情况
* *
* @param list 原始列表 * @param list 原始列表
* @param fromIndex 起始索引包含 * @param fromIndex 起始索引包含
* @param toIndex 结束索引不包含 * @param toIndex 结束索引不包含
* @param <T> 元素类型 * @param <T> 元素类型
* @return 子列表 * @return 子列表
*/ */
@NotNull @NotNull
public static <T> List<T> safeSubList( public static <T> List<T> safeSubList(
@NotNull List<T> list, @NotNull List<T> list,
int fromIndex, int fromIndex,
int toIndex int toIndex
) { ) {
int size = list.size(); int size = list.size();
if (size == 0) { if (size == 0) {
return Collections.emptyList(); return Collections.emptyList();
} }
// 调整边界 // 调整边界
fromIndex = Math.max(0, fromIndex); fromIndex = Math.max(0, fromIndex);
toIndex = Math.min(size, toIndex); toIndex = Math.min(size, toIndex);
if (fromIndex >= toIndex) { if (fromIndex >= toIndex) {
return Collections.emptyList(); return Collections.emptyList();
} }
return list.subList(fromIndex, toIndex); return list.subList(fromIndex, toIndex);
} }
/** /**
* 判断列表是否为空或null * 判断列表是否为空或null
* *
* @param list 待检查的列表 * @param list 待检查的列表
* @return 如果列表为null或空则返回true否则返回false * @return 如果列表为null或空则返回true否则返回false
*/ */
public static boolean isEmpty(@Nullable List<?> list) { public static boolean isEmpty(@Nullable List<?> list) {
return list == null || list.isEmpty(); return list == null || list.isEmpty();
} }
/** /**
* 判断数组是否为空或null * 判断数组是否为空或null
* *
* @param array 待检查的数组 * @param array 待检查的数组
* @return 如果数组为null或空则返回true否则返回false * @return 如果数组为null或空则返回true否则返回false
*/ */
public static boolean isEmpty(@Nullable Object[] array) { public static boolean isEmpty(@Nullable Object[] array) {
return array == null || array.length == 0; return array == null || array.length == 0;
} }
/** /**
* 查找列表中第一个满足条件的元素 * 查找列表中第一个满足条件的元素
* *
* @param list 列表 * @param list 列表
* @param predicate 条件谓词 * @param predicate 条件谓词
* @param <T> 元素类型 * @param <T> 元素类型
* @return 第一个满足条件的元素如果没有则返回null * @return 第一个满足条件的元素如果没有则返回null
*/ */
@Nullable @Nullable
public static <T> T findFirst( public static <T> T findFirst(
@NotNull List<T> list, @NotNull List<T> list,
@NotNull Predicate<T> predicate @NotNull Predicate<T> predicate
) { ) {
for (T item : list) { for (T item : list) {
if (predicate.test(item)) { if (predicate.test(item)) {
return item; return item;
} }
} }
return null; return null;
} }
/** /**
* 查找数组中第一个满足条件的元素 * 查找数组中第一个满足条件的元素
* *
* @param array 数组 * @param array 数组
* @param predicate 条件谓词 * @param predicate 条件谓词
* @param <T> 元素类型 * @param <T> 元素类型
* @return 第一个满足条件的元素如果没有则返回null * @return 第一个满足条件的元素如果没有则返回null
*/ */
@Nullable @Nullable
public static <T> T findFirst( public static <T> T findFirst(
@NotNull T[] array, @NotNull T[] array,
@NotNull Predicate<T> predicate @NotNull Predicate<T> predicate
) { ) {
for (T item : array) { for (T item : array) {
if (predicate.test(item)) { if (predicate.test(item)) {
return item; return item;
} }
} }
return null; return null;
} }
/** /**
* 过滤列表中满足条件的元素 * 过滤列表中满足条件的元素
* *
* @param list 原始列表 * @param list 原始列表
* @param predicate 条件谓词 * @param predicate 条件谓词
* @param <T> 元素类型 * @param <T> 元素类型
* @return 包含满足条件元素的新列表 * @return 包含满足条件元素的新列表
*/ */
@NotNull @NotNull
public static <T> List<T> filter( public static <T> List<T> filter(
@NotNull List<T> list, @NotNull List<T> list,
@NotNull Predicate<T> predicate @NotNull Predicate<T> predicate
) { ) {
List<T> result = new ArrayList<>(); List<T> result = new ArrayList<>();
for (T item : list) { for (T item : list) {
if (predicate.test(item)) { if (predicate.test(item)) {
result.add(item); result.add(item);
} }
} }
return result; return result;
} }
/** /**
* 过滤数组中满足条件的元素 * 过滤数组中满足条件的元素
* *
* @param array 原始数组 * @param array 原始数组
* @param predicate 条件谓词 * @param predicate 条件谓词
* @param <T> 元素类型 * @param <T> 元素类型
* @return 包含满足条件元素的新列表 * @return 包含满足条件元素的新列表
*/ */
@NotNull @NotNull
public static <T> List<T> filter( public static <T> List<T> filter(
@NotNull T[] array, @NotNull T[] array,
@NotNull Predicate<T> predicate @NotNull Predicate<T> predicate
) { ) {
return filter(Arrays.asList(array), predicate); return filter(Arrays.asList(array), predicate);
} }
/** /**
* 将列表转换为数组 * 将列表转换为数组
* *
* @param list 列表 * @param list 列表
* @param clazz 元素类型class * @param clazz 元素类型class
* @param <T> 元素类型 * @param <T> 元素类型
* @return 转换后的数组 * @return 转换后的数组
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@NotNull @NotNull
public static <T> T[] toArray( public static <T> T[] toArray(
@NotNull List<T> list, @NotNull List<T> list,
@NotNull Class<T> clazz @NotNull Class<T> clazz
) { ) {
T[] array = (T[]) java.lang.reflect.Array.newInstance( T[] array = (T[]) java.lang.reflect.Array.newInstance(
clazz, clazz,
list.size() list.size()
); );
return list.toArray(array); return list.toArray(array);
} }
/** /**
* 将集合转换为列表 * 将集合转换为列表
* *
* @param collection 集合 * @param collection 集合
* @param <T> 元素类型 * @param <T> 元素类型
* @return 转换后的列表 * @return 转换后的列表
*/ */
@NotNull @NotNull
public static <T> List<T> toList( public static <T> List<T> toList(
@NotNull java.util.Collection<T> collection @NotNull java.util.Collection<T> collection
) { ) {
return new ArrayList<>(collection); return new ArrayList<>(collection);
} }
} }

View File

@ -10,192 +10,192 @@ import java.util.concurrent.ConcurrentMap;
*/ */
public class ForEach { public class ForEach {
/** /**
* 对给定的集合执行指定的操作操作包含元素值和索引 * 对给定的集合执行指定的操作操作包含元素值和索引
* 根据集合类型选择最优的遍历方式以提高性能 * 根据集合类型选择最优的遍历方式以提高性能
* *
* @param collection 要遍历的集合可以是 List 或其他 Collection 实现 * @param collection 要遍历的集合可以是 List 或其他 Collection 实现
* @param action 要对每个元素执行的操作接收元素值和索引作为参数 * @param action 要对每个元素执行的操作接收元素值和索引作为参数
* @param <T> 集合中元素的类型 * @param <T> 集合中元素的类型
*/ */
public static <T> void forEach( public static <T> void forEach(
Collection<T> collection, Collection<T> collection,
ForEach.Consumer<? super T> action ForEach.Consumer<? super T> action
) { ) {
// 参数校验如果集合或操作为空则直接返回 // 参数校验如果集合或操作为空则直接返回
if (collection == null || action == null) { if (collection == null || action == null) {
return; return;
} }
// 如果集合实现了 RandomAccess 接口 ArrayList使用索引访问优化性能 // 如果集合实现了 RandomAccess 接口 ArrayList使用索引访问优化性能
if (collection instanceof RandomAccess && collection instanceof List) { if (collection instanceof RandomAccess && collection instanceof List) {
List<T> list = (List<T>) collection; List<T> list = (List<T>) collection;
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
action.call(list.get(i), i); action.call(list.get(i), i);
} }
} }
// 如果是普通 List使用迭代器遍历并手动维护索引 // 如果是普通 List使用迭代器遍历并手动维护索引
else if (collection instanceof List) { else if (collection instanceof List) {
int index = 0; int index = 0;
Iterator<T> it = collection.iterator(); Iterator<T> it = collection.iterator();
while (it.hasNext()) { while (it.hasNext()) {
action.call(it.next(), index); action.call(it.next(), index);
index++; index++;
} }
} }
// 其他类型的集合使用增强 for 循环并手动维护索引 // 其他类型的集合使用增强 for 循环并手动维护索引
else { else {
int index = 0; int index = 0;
for (T element : collection) { for (T element : collection) {
action.call(element, index); action.call(element, index);
index++; index++;
} }
} }
} }
/** /**
* 对给定的集合执行指定的操作仅处理元素值 * 对给定的集合执行指定的操作仅处理元素值
* 根据集合是否实现 RandomAccess 接口选择最优的遍历方式 * 根据集合是否实现 RandomAccess 接口选择最优的遍历方式
* *
* @param collection 要遍历的集合 * @param collection 要遍历的集合
* @param action 要对每个元素执行的操作只接收元素值作为参数 * @param action 要对每个元素执行的操作只接收元素值作为参数
* @param <T> 集合中元素的类型 * @param <T> 集合中元素的类型
*/ */
public static <T> void forEach( public static <T> void forEach(
Collection<T> collection, Collection<T> collection,
java.util.function.Consumer<? super T> action java.util.function.Consumer<? super T> action
) { ) {
// 参数校验如果集合或操作为空则直接返回 // 参数校验如果集合或操作为空则直接返回
if (collection == null || action == null) { if (collection == null || action == null) {
return; return;
} }
// 如果集合实现了 RandomAccess 接口使用索引访问提升性能 // 如果集合实现了 RandomAccess 接口使用索引访问提升性能
if (collection instanceof RandomAccess) { if (collection instanceof RandomAccess) {
List<T> list = (List<T>) collection; List<T> list = (List<T>) collection;
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
action.accept(list.get(i)); action.accept(list.get(i));
} }
} }
// 否则使用增强 for 循环进行遍历 // 否则使用增强 for 循环进行遍历
else { else {
for (T element : collection) { for (T element : collection) {
action.accept(element); action.accept(element);
} }
} }
} }
/** /**
* 对给定的映射执行指定的操作操作包含键值和索引 * 对给定的映射执行指定的操作操作包含键值和索引
* 根据映射类型选择不同的遍历策略 * 根据映射类型选择不同的遍历策略
* *
* @param map 要遍历的映射 * @param map 要遍历的映射
* @param action 要对每个键值对执行的操作接收键值和索引作为参数 * @param action 要对每个键值对执行的操作接收键值和索引作为参数
* @param <K> 映射中键的类型 * @param <K> 映射中键的类型
* @param <V> 映射中值的类型 * @param <V> 映射中值的类型
*/ */
public static <K, V> void forEach( public static <K, V> void forEach(
Map<K, V> map, Map<K, V> map,
BiConsumer<? super K, ? super V> action BiConsumer<? super K, ? super V> action
) { ) {
// 参数校验如果映射或操作为空则直接返回 // 参数校验如果映射或操作为空则直接返回
if (map == null || action == null) { if (map == null || action == null) {
return; return;
} }
// 遍历 TreeMap 的条目集合并传递索引 // 遍历 TreeMap 的条目集合并传递索引
if (map instanceof TreeMap) { if (map instanceof TreeMap) {
int index = 0; int index = 0;
for (Map.Entry<K, V> entry : map.entrySet()) { for (Map.Entry<K, V> entry : map.entrySet()) {
action.call(entry.getKey(), entry.getValue(), index); action.call(entry.getKey(), entry.getValue(), index);
index++; index++;
} }
} }
// 遍历 ConcurrentMap LinkedHashMap 的条目集合并传递索引 // 遍历 ConcurrentMap LinkedHashMap 的条目集合并传递索引
else if (map instanceof ConcurrentMap || map instanceof LinkedHashMap) { else if (map instanceof ConcurrentMap || map instanceof LinkedHashMap) {
int index = 0; int index = 0;
for (Map.Entry<K, V> entry : map.entrySet()) { for (Map.Entry<K, V> entry : map.entrySet()) {
action.call(entry.getKey(), entry.getValue(), index); action.call(entry.getKey(), entry.getValue(), index);
index++; index++;
} }
} }
// 遍历其他类型映射的条目集合并传递索引 // 遍历其他类型映射的条目集合并传递索引
else { else {
int index = 0; int index = 0;
for (Map.Entry<K, V> entry : map.entrySet()) { for (Map.Entry<K, V> entry : map.entrySet()) {
action.call(entry.getKey(), entry.getValue(), index); action.call(entry.getKey(), entry.getValue(), index);
index++; index++;
} }
} }
} }
/** /**
* 对给定的映射执行指定的操作仅处理键和值 * 对给定的映射执行指定的操作仅处理键和值
* 根据映射类型选择不同的遍历策略 * 根据映射类型选择不同的遍历策略
* *
* @param map 要遍历的映射 * @param map 要遍历的映射
* @param action 要对每个键值对执行的操作接收键和值作为参数 * @param action 要对每个键值对执行的操作接收键和值作为参数
* @param <K> 映射中键的类型 * @param <K> 映射中键的类型
* @param <V> 映射中值的类型 * @param <V> 映射中值的类型
*/ */
public static <K, V> void forEach( public static <K, V> void forEach(
Map<K, V> map, Map<K, V> map,
java.util.function.BiConsumer<? super K, ? super V> action java.util.function.BiConsumer<? super K, ? super V> action
) { ) {
// 参数校验如果映射或操作为空则直接返回 // 参数校验如果映射或操作为空则直接返回
if (map == null || action == null) { if (map == null || action == null) {
return; return;
} }
// 遍历 TreeMap 的条目集合 // 遍历 TreeMap 的条目集合
if (map instanceof TreeMap) { if (map instanceof TreeMap) {
for (Map.Entry<K, V> entry : map.entrySet()) { for (Map.Entry<K, V> entry : map.entrySet()) {
action.accept(entry.getKey(), entry.getValue()); action.accept(entry.getKey(), entry.getValue());
} }
} }
// 如果是 ConcurrentMap LinkedHashMap使用其内置的 forEach 方法 // 如果是 ConcurrentMap LinkedHashMap使用其内置的 forEach 方法
else if (map instanceof ConcurrentMap || map instanceof LinkedHashMap) { else if (map instanceof ConcurrentMap || map instanceof LinkedHashMap) {
map.forEach(action); map.forEach(action);
} }
// 遍历其他类型映射的条目集合 // 遍历其他类型映射的条目集合
else { else {
for (Map.Entry<K, V> entry : map.entrySet()) { for (Map.Entry<K, V> entry : map.entrySet()) {
action.accept(entry.getKey(), entry.getValue()); action.accept(entry.getKey(), entry.getValue());
} }
} }
} }
/** /**
* 自定义消费者接口用于接收元素值和索引 * 自定义消费者接口用于接收元素值和索引
* *
* @param <T> 元素类型 * @param <T> 元素类型
*/ */
@FunctionalInterface @FunctionalInterface
public interface Consumer<T> { public interface Consumer<T> {
/** /**
* 执行消费操作 * 执行消费操作
* *
* @param value 元素值 * @param value 元素值
* @param index 元素在集合中的索引 * @param index 元素在集合中的索引
*/ */
void call(T value, int index); void call(T value, int index);
} }
/** /**
* 自定义二元消费者接口用于接收键值和索引 * 自定义二元消费者接口用于接收键值和索引
* *
* @param <K> 键类型 * @param <K> 键类型
* @param <V> 值类型 * @param <V> 值类型
*/ */
@FunctionalInterface @FunctionalInterface
public interface BiConsumer<K, V> { public interface BiConsumer<K, V> {
/** /**
* 执行消费操作 * 执行消费操作
* *
* @param key * @param key
* @param value * @param value
* @param index 键值对在映射中的索引 * @param index 键值对在映射中的索引
*/ */
void call(K key, V value, int index); void call(K key, V value, int index);
} }
} }

View File

@ -1,8 +1,7 @@
package com.mingliqiye.utils.collection; package com.mingliqiye.utils.collection;
import org.jetbrains.annotations.NotNull;
import java.util.*; import java.util.*;
import org.jetbrains.annotations.NotNull;
/** /**
* Lists工具类提供了一系列创建List实现的便捷方法 * Lists工具类提供了一系列创建List实现的便捷方法
@ -11,239 +10,239 @@ import java.util.*;
*/ */
public class Lists { public class Lists {
/** /**
* 创建一个空的ArrayList实例 * 创建一个空的ArrayList实例
* *
* @param <T> 列表元素的类型 * @param <T> 列表元素的类型
* @return 新创建的空ArrayList实例 * @return 新创建的空ArrayList实例
*/ */
public static <T> List<T> newArrayList() { public static <T> List<T> newArrayList() {
return new ArrayList<>(); return new ArrayList<>();
} }
/** /**
* 根据可变参数创建一个包含指定元素的ArrayList实例 * 根据可变参数创建一个包含指定元素的ArrayList实例
* *
* @param ts 要添加到列表中的元素可以为0个或多个 * @param ts 要添加到列表中的元素可以为0个或多个
* @param <T> 列表元素的类型 * @param <T> 列表元素的类型
* @return 包含指定元素的新ArrayList实例 * @return 包含指定元素的新ArrayList实例
*/ */
public static <T> List<T> newArrayList(T... ts) { public static <T> List<T> newArrayList(T... ts) {
List<T> list = newArrayList(); List<T> list = newArrayList();
list.addAll(Arrays.asList(ts)); list.addAll(Arrays.asList(ts));
return list; return list;
} }
/** /**
* 根据已有列表创建一个新的ArrayList实例 * 根据已有列表创建一个新的ArrayList实例
* *
* @param list 要复制的列表 * @param list 要复制的列表
* @param <T> 列表元素的类型 * @param <T> 列表元素的类型
* @return 包含原列表所有元素的新ArrayList实例 * @return 包含原列表所有元素的新ArrayList实例
*/ */
public static <T> List<T> newArrayList(List<T> list) { public static <T> List<T> newArrayList(List<T> list) {
List<T> newList = newArrayList(); List<T> newList = newArrayList();
newList.addAll(list); newList.addAll(list);
return newList; return newList;
} }
/** /**
* 根据可迭代对象创建一个ArrayList实例 * 根据可迭代对象创建一个ArrayList实例
* *
* @param iterable 可迭代对象 * @param iterable 可迭代对象
* @param <T> 列表元素的类型 * @param <T> 列表元素的类型
* @return 包含可迭代对象中所有元素的新ArrayList实例 * @return 包含可迭代对象中所有元素的新ArrayList实例
*/ */
public static <T> List<T> newArrayList(Iterable<T> iterable) { public static <T> List<T> newArrayList(Iterable<T> iterable) {
List<T> list = newArrayList(); List<T> list = newArrayList();
for (T t : iterable) { for (T t : iterable) {
list.add(t); list.add(t);
} }
return list; return list;
} }
/** /**
* 创建一个指定初始容量的空ArrayList实例 * 创建一个指定初始容量的空ArrayList实例
* *
* @param size 初始容量大小 * @param size 初始容量大小
* @param <T> 列表元素的类型 * @param <T> 列表元素的类型
* @return 指定初始容量的空ArrayList实例 * @return 指定初始容量的空ArrayList实例
*/ */
public static <T> List<T> newArrayList(int size) { public static <T> List<T> newArrayList(int size) {
return new ArrayList<>(size); return new ArrayList<>(size);
} }
/** /**
* 创建一个指定大小并用单个元素填充的ArrayList实例 * 创建一个指定大小并用单个元素填充的ArrayList实例
* *
* @param size 列表大小 * @param size 列表大小
* @param t 用于填充列表的元素 * @param t 用于填充列表的元素
* @param <T> 列表元素的类型 * @param <T> 列表元素的类型
* @return 指定大小且所有元素都相同的ArrayList实例 * @return 指定大小且所有元素都相同的ArrayList实例
*/ */
public static <T> List<T> newArrayList(int size, T t) { public static <T> List<T> newArrayList(int size, T t) {
List<T> list = newArrayList(size); List<T> list = newArrayList(size);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
list.add(t); list.add(t);
} }
return list; return list;
} }
/** /**
* 创建一个指定大小并交替使用两个元素填充的ArrayList实例 * 创建一个指定大小并交替使用两个元素填充的ArrayList实例
* *
* @param size 列表大小 * @param size 列表大小
* @param t 第一个填充元素索引为偶数时使用 * @param t 第一个填充元素索引为偶数时使用
* @param t1 第二个填充元素索引为奇数时使用 * @param t1 第二个填充元素索引为奇数时使用
* @param <T> 列表元素的类型 * @param <T> 列表元素的类型
* @return 指定大小且交替填充两个元素的ArrayList实例 * @return 指定大小且交替填充两个元素的ArrayList实例
*/ */
public static <T> List<T> newArrayList(int size, T t, T t1) { public static <T> List<T> newArrayList(int size, T t, T t1) {
List<T> list = newArrayList(size); List<T> list = newArrayList(size);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
list.add(i % 2 == 0 ? t : t1); list.add(i % 2 == 0 ? t : t1);
} }
return list; return list;
} }
/** /**
* 创建一个指定大小并循环使用三个元素填充的ArrayList实例 * 创建一个指定大小并循环使用三个元素填充的ArrayList实例
* *
* @param size 列表大小 * @param size 列表大小
* @param t 第一个填充元素索引模3等于0时使用 * @param t 第一个填充元素索引模3等于0时使用
* @param t1 第二个填充元素索引模3等于1时使用 * @param t1 第二个填充元素索引模3等于1时使用
* @param t2 第三个填充元素索引模3等于2时使用 * @param t2 第三个填充元素索引模3等于2时使用
* @param <T> 列表元素的类型 * @param <T> 列表元素的类型
* @return 指定大小且循环填充三个元素的ArrayList实例 * @return 指定大小且循环填充三个元素的ArrayList实例
*/ */
public static <T> List<T> newArrayList(int size, T t, T t1, T t2) { public static <T> List<T> newArrayList(int size, T t, T t1, T t2) {
List<T> list = newArrayList(size); List<T> list = newArrayList(size);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
list.add(i % 3 == 0 ? t : i % 3 == 1 ? t1 : t2); list.add(i % 3 == 0 ? t : i % 3 == 1 ? t1 : t2);
} }
return list; return list;
} }
/** /**
* 创建一个指定大小并循环使用四个元素填充的ArrayList实例 * 创建一个指定大小并循环使用四个元素填充的ArrayList实例
* *
* @param size 列表大小 * @param size 列表大小
* @param t 第一个填充元素索引模4等于0时使用 * @param t 第一个填充元素索引模4等于0时使用
* @param t1 第二个填充元素索引模4等于1时使用 * @param t1 第二个填充元素索引模4等于1时使用
* @param t2 第三个填充元素索引模4等于2时使用 * @param t2 第三个填充元素索引模4等于2时使用
* @param t3 第四个填充元素索引模4等于3时使用 * @param t3 第四个填充元素索引模4等于3时使用
* @param <T> 列表元素的类型 * @param <T> 列表元素的类型
* @return 指定大小且循环填充四个元素的ArrayList实例 * @return 指定大小且循环填充四个元素的ArrayList实例
*/ */
public static <T> List<T> newArrayList(int size, T t, T t1, T t2, T t3) { public static <T> List<T> newArrayList(int size, T t, T t1, T t2, T t3) {
List<T> list = newArrayList(size); List<T> list = newArrayList(size);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
list.add(i % 4 == 0 ? t : i % 4 == 1 ? t1 : i % 4 == 2 ? t2 : t3); list.add(i % 4 == 0 ? t : i % 4 == 1 ? t1 : i % 4 == 2 ? t2 : t3);
} }
return list; return list;
} }
/** /**
* 创建一个空的LinkedList实例 * 创建一个空的LinkedList实例
* *
* @param <T> 列表元素的类型 * @param <T> 列表元素的类型
* @return 新创建的空LinkedList实例 * @return 新创建的空LinkedList实例
*/ */
public static <T> List<T> newLinkedList() { public static <T> List<T> newLinkedList() {
return new LinkedList<>(); return new LinkedList<>();
} }
/** /**
* 根据可变参数创建一个包含指定元素的LinkedList实例 * 根据可变参数创建一个包含指定元素的LinkedList实例
* *
* @param ts 要添加到列表中的元素可以为0个或多个 * @param ts 要添加到列表中的元素可以为0个或多个
* @param <T> 列表元素的类型 * @param <T> 列表元素的类型
* @return 包含指定元素的新LinkedList实例 * @return 包含指定元素的新LinkedList实例
*/ */
public static <T> List<T> newLinkedList(T... ts) { public static <T> List<T> newLinkedList(T... ts) {
List<T> list = newLinkedList(); List<T> list = newLinkedList();
list.addAll(Arrays.asList(ts)); list.addAll(Arrays.asList(ts));
return list; return list;
} }
/** /**
* 根据已有列表创建一个新的LinkedList实例 * 根据已有列表创建一个新的LinkedList实例
* *
* @param list 要复制的列表 * @param list 要复制的列表
* @param <T> 列表元素的类型 * @param <T> 列表元素的类型
* @return 包含原列表所有元素的新LinkedList实例 * @return 包含原列表所有元素的新LinkedList实例
*/ */
public static <T> List<T> newLinkedList(List<T> list) { public static <T> List<T> newLinkedList(List<T> list) {
List<T> newList = newLinkedList(); List<T> newList = newLinkedList();
newList.addAll(list); newList.addAll(list);
return newList; return newList;
} }
/** /**
* 创建一个空的Vector实例 * 创建一个空的Vector实例
* *
* @param <T> 列表元素的类型 * @param <T> 列表元素的类型
* @return 新创建的空Vector实例 * @return 新创建的空Vector实例
*/ */
public static <T> List<T> newVector() { public static <T> List<T> newVector() {
return new Vector<>(); return new Vector<>();
} }
/** /**
* 根据可变参数创建一个包含指定元素的Vector实例 * 根据可变参数创建一个包含指定元素的Vector实例
* *
* @param ts 要添加到列表中的元素可以为0个或多个 * @param ts 要添加到列表中的元素可以为0个或多个
* @param <T> 列表元素的类型 * @param <T> 列表元素的类型
* @return 包含指定元素的新Vector实例 * @return 包含指定元素的新Vector实例
*/ */
public static <T> List<T> newVector(T... ts) { public static <T> List<T> newVector(T... ts) {
List<T> list = newVector(); List<T> list = newVector();
list.addAll(Arrays.asList(ts)); list.addAll(Arrays.asList(ts));
return list; return list;
} }
/** /**
* 根据已有列表创建一个新的Vector实例 * 根据已有列表创建一个新的Vector实例
* *
* @param list 要复制的列表 * @param list 要复制的列表
* @param <T> 列表元素的类型 * @param <T> 列表元素的类型
* @return 包含原列表所有元素的新Vector实例 * @return 包含原列表所有元素的新Vector实例
*/ */
public static <T> List<T> newVector(List<T> list) { public static <T> List<T> newVector(List<T> list) {
List<T> newList = newVector(); List<T> newList = newVector();
newList.addAll(list); newList.addAll(list);
return newList; return newList;
} }
/** /**
* 将指定列表中的每个元素转换为字符串表示形式 * 将指定列表中的每个元素转换为字符串表示形式
* *
* @param <T> 列表元素的类型 * @param <T> 列表元素的类型
* @param list 要转换的列表不能为空 * @param list 要转换的列表不能为空
* @return 包含原列表各元素字符串表示的新列表保持相同的顺序 * @return 包含原列表各元素字符串表示的新列表保持相同的顺序
*/ */
public static <T> List<String> toStringList(@NotNull List<T> list) { public static <T> List<String> toStringList(@NotNull List<T> list) {
// 创建与原列表相同大小的新列表用于存储字符串转换结果 // 创建与原列表相同大小的新列表用于存储字符串转换结果
List<String> newList = newArrayList(list.size()); List<String> newList = newArrayList(list.size());
for (T t : list) { for (T t : list) {
newList.add(t == null ? "null" : t.toString()); newList.add(t == null ? "null" : t.toString());
} }
return newList; return newList;
} }
/** /**
* 将指定数组中的每个元素转换为字符串表示形式 * 将指定数组中的每个元素转换为字符串表示形式
* *
* @param <T> 数组元素的类型 * @param <T> 数组元素的类型
* @param list 要转换的数组不能为空 * @param list 要转换的数组不能为空
* @return 包含原数组各元素字符串表示的新字符串数组 * @return 包含原数组各元素字符串表示的新字符串数组
*/ */
public static <T> String[] toStringList(@NotNull T[] list) { public static <T> String[] toStringList(@NotNull T[] list) {
// 创建新的字符串列表用于存储转换后的结果 // 创建新的字符串列表用于存储转换后的结果
List<String> newList = newArrayList(list.length); List<String> newList = newArrayList(list.length);
for (T t : list) { for (T t : list) {
newList.add(t == null ? "null" : t.toString()); newList.add(t == null ? "null" : t.toString());
} }
return newList.toArray(new String[0]); return newList.toArray(new String[0]);
} }
} }

View File

@ -10,178 +10,178 @@ import java.util.concurrent.ConcurrentHashMap;
*/ */
public class Maps { public class Maps {
/** /**
* 创建一个空的HashMap实例 * 创建一个空的HashMap实例
* *
* @param <K> Map键的类型 * @param <K> Map键的类型
* @param <V> Map值的类型 * @param <V> Map值的类型
* @return 新创建的空HashMap实例 * @return 新创建的空HashMap实例
*/ */
public static <K, V> Map<K, V> newHashMap() { public static <K, V> Map<K, V> newHashMap() {
return new HashMap<>(); return new HashMap<>();
} }
/** /**
* 创建一个指定初始容量的空HashMap实例 * 创建一个指定初始容量的空HashMap实例
* *
* @param size 初始容量大小 * @param size 初始容量大小
* @param <K> Map键的类型 * @param <K> Map键的类型
* @param <V> Map值的类型 * @param <V> Map值的类型
* @return 指定初始容量的空HashMap实例 * @return 指定初始容量的空HashMap实例
*/ */
public static <K, V> Map<K, V> newHashMap(int size) { public static <K, V> Map<K, V> newHashMap(int size) {
return new HashMap<>(size); return new HashMap<>(size);
} }
/** /**
* 根据已有Map创建一个新的HashMap实例 * 根据已有Map创建一个新的HashMap实例
* *
* @param map 要复制的Map * @param map 要复制的Map
* @param <K> Map键的类型 * @param <K> Map键的类型
* @param <V> Map值的类型 * @param <V> Map值的类型
* @return 包含原Map所有元素的新HashMap实例 * @return 包含原Map所有元素的新HashMap实例
*/ */
public static <K, V> Map<K, V> newHashMap(Map<K, V> map) { public static <K, V> Map<K, V> newHashMap(Map<K, V> map) {
Map<K, V> newMap = newHashMap(); Map<K, V> newMap = newHashMap();
newMap.putAll(map); newMap.putAll(map);
return newMap; return newMap;
} }
/** /**
* 创建一个空的LinkedHashMap实例 * 创建一个空的LinkedHashMap实例
* *
* @param <K> Map键的类型 * @param <K> Map键的类型
* @param <V> Map值的类型 * @param <V> Map值的类型
* @return 新创建的空LinkedHashMap实例 * @return 新创建的空LinkedHashMap实例
*/ */
public static <K, V> Map<K, V> newLinkedHashMap() { public static <K, V> Map<K, V> newLinkedHashMap() {
return new LinkedHashMap<>(); return new LinkedHashMap<>();
} }
/** /**
* 创建一个指定初始容量的空LinkedHashMap实例 * 创建一个指定初始容量的空LinkedHashMap实例
* *
* @param size 初始容量大小 * @param size 初始容量大小
* @param <K> Map键的类型 * @param <K> Map键的类型
* @param <V> Map值的类型 * @param <V> Map值的类型
* @return 指定初始容量的空LinkedHashMap实例 * @return 指定初始容量的空LinkedHashMap实例
*/ */
public static <K, V> Map<K, V> newLinkedHashMap(int size) { public static <K, V> Map<K, V> newLinkedHashMap(int size) {
return new LinkedHashMap<>(size); return new LinkedHashMap<>(size);
} }
/** /**
* 根据已有Map创建一个新的LinkedHashMap实例 * 根据已有Map创建一个新的LinkedHashMap实例
* *
* @param map 要复制的Map * @param map 要复制的Map
* @param <K> Map键的类型 * @param <K> Map键的类型
* @param <V> Map值的类型 * @param <V> Map值的类型
* @return 包含原Map所有元素的新LinkedHashMap实例 * @return 包含原Map所有元素的新LinkedHashMap实例
*/ */
public static <K, V> Map<K, V> newLinkedHashMap(Map<K, V> map) { public static <K, V> Map<K, V> newLinkedHashMap(Map<K, V> map) {
Map<K, V> newMap = newLinkedHashMap(); Map<K, V> newMap = newLinkedHashMap();
newMap.putAll(map); newMap.putAll(map);
return newMap; return newMap;
} }
/** /**
* 创建一个空的TreeMap实例 * 创建一个空的TreeMap实例
* *
* @param <K> Map键的类型必须实现Comparable接口 * @param <K> Map键的类型必须实现Comparable接口
* @param <V> Map值的类型 * @param <V> Map值的类型
* @return 新创建的空TreeMap实例 * @return 新创建的空TreeMap实例
*/ */
public static <K extends Comparable<K>, V> Map<K, V> newTreeMap() { public static <K extends Comparable<K>, V> Map<K, V> newTreeMap() {
return new TreeMap<>(); return new TreeMap<>();
} }
/** /**
* 根据已有Map创建一个新的TreeMap实例 * 根据已有Map创建一个新的TreeMap实例
* *
* @param map 要复制的Map * @param map 要复制的Map
* @param <K> Map键的类型必须实现Comparable接口 * @param <K> Map键的类型必须实现Comparable接口
* @param <V> Map值的类型 * @param <V> Map值的类型
* @return 包含原Map所有元素的新TreeMap实例 * @return 包含原Map所有元素的新TreeMap实例
*/ */
public static <K extends Comparable<K>, V> Map<K, V> newTreeMap( public static <K extends Comparable<K>, V> Map<K, V> newTreeMap(
Map<K, V> map Map<K, V> map
) { ) {
Map<K, V> newMap = newTreeMap(); Map<K, V> newMap = newTreeMap();
newMap.putAll(map); newMap.putAll(map);
return newMap; return newMap;
} }
/** /**
* 创建一个空的Hashtable实例 * 创建一个空的Hashtable实例
* *
* @param <K> Map键的类型 * @param <K> Map键的类型
* @param <V> Map值的类型 * @param <V> Map值的类型
* @return 新创建的空Hashtable实例 * @return 新创建的空Hashtable实例
*/ */
public static <K, V> Map<K, V> newHashtable() { public static <K, V> Map<K, V> newHashtable() {
return new Hashtable<>(); return new Hashtable<>();
} }
/** /**
* 创建一个指定初始容量的空Hashtable实例 * 创建一个指定初始容量的空Hashtable实例
* *
* @param size 初始容量大小 * @param size 初始容量大小
* @param <K> Map键的类型 * @param <K> Map键的类型
* @param <V> Map值的类型 * @param <V> Map值的类型
* @return 指定初始容量的空Hashtable实例 * @return 指定初始容量的空Hashtable实例
*/ */
public static <K, V> Map<K, V> newHashtable(int size) { public static <K, V> Map<K, V> newHashtable(int size) {
return new Hashtable<>(size); return new Hashtable<>(size);
} }
/** /**
* 根据已有Map创建一个新的Hashtable实例 * 根据已有Map创建一个新的Hashtable实例
* *
* @param map 要复制的Map * @param map 要复制的Map
* @param <K> Map键的类型 * @param <K> Map键的类型
* @param <V> Map值的类型 * @param <V> Map值的类型
* @return 包含原Map所有元素的新Hashtable实例 * @return 包含原Map所有元素的新Hashtable实例
*/ */
public static <K, V> Map<K, V> newHashtable(Map<K, V> map) { public static <K, V> Map<K, V> newHashtable(Map<K, V> map) {
Map<K, V> newMap = newHashtable(); Map<K, V> newMap = newHashtable();
newMap.putAll(map); newMap.putAll(map);
return newMap; return newMap;
} }
/** /**
* 创建一个空的ConcurrentHashMap实例 * 创建一个空的ConcurrentHashMap实例
* *
* @param <K> Map键的类型 * @param <K> Map键的类型
* @param <V> Map值的类型 * @param <V> Map值的类型
* @return 新创建的空ConcurrentHashMap实例 * @return 新创建的空ConcurrentHashMap实例
*/ */
public static <K, V> Map<K, V> newConcurrentHashMap() { public static <K, V> Map<K, V> newConcurrentHashMap() {
return new ConcurrentHashMap<>(); return new ConcurrentHashMap<>();
} }
/** /**
* 创建一个指定初始容量的空ConcurrentHashMap实例 * 创建一个指定初始容量的空ConcurrentHashMap实例
* *
* @param size 初始容量大小 * @param size 初始容量大小
* @param <K> Map键的类型 * @param <K> Map键的类型
* @param <V> Map值的类型 * @param <V> Map值的类型
* @return 指定初始容量的空ConcurrentHashMap实例 * @return 指定初始容量的空ConcurrentHashMap实例
*/ */
public static <K, V> Map<K, V> newConcurrentHashMap(int size) { public static <K, V> Map<K, V> newConcurrentHashMap(int size) {
return new ConcurrentHashMap<>(size); return new ConcurrentHashMap<>(size);
} }
/** /**
* 根据已有Map创建一个新的ConcurrentHashMap实例 * 根据已有Map创建一个新的ConcurrentHashMap实例
* *
* @param map 要复制的Map * @param map 要复制的Map
* @param <K> Map键的类型 * @param <K> Map键的类型
* @param <V> Map值的类型 * @param <V> Map值的类型
* @return 包含原Map所有元素的新ConcurrentHashMap实例 * @return 包含原Map所有元素的新ConcurrentHashMap实例
*/ */
public static <K, V> Map<K, V> newConcurrentHashMap(Map<K, V> map) { public static <K, V> Map<K, V> newConcurrentHashMap(Map<K, V> map) {
Map<K, V> newMap = newConcurrentHashMap(); Map<K, V> newMap = newConcurrentHashMap();
newMap.putAll(map); newMap.putAll(map);
return newMap; return newMap;
} }
} }

View File

@ -9,137 +9,137 @@ import java.util.*;
*/ */
public class Sets { public class Sets {
/** /**
* 创建一个空的HashSet实例 * 创建一个空的HashSet实例
* *
* @param <T> 集合元素的类型 * @param <T> 集合元素的类型
* @return 新创建的空HashSet实例 * @return 新创建的空HashSet实例
*/ */
public static <T> Set<T> newHashSet() { public static <T> Set<T> newHashSet() {
return new HashSet<>(); return new HashSet<>();
} }
/** /**
* 根据可变参数创建一个包含指定元素的HashSet实例 * 根据可变参数创建一个包含指定元素的HashSet实例
* *
* @param ts 要添加到集合中的元素可以为0个或多个 * @param ts 要添加到集合中的元素可以为0个或多个
* @param <T> 集合元素的类型 * @param <T> 集合元素的类型
* @return 包含指定元素的新HashSet实例 * @return 包含指定元素的新HashSet实例
*/ */
public static <T> Set<T> newHashSet(T... ts) { public static <T> Set<T> newHashSet(T... ts) {
Set<T> set = newHashSet(); Set<T> set = newHashSet();
set.addAll(Arrays.asList(ts)); set.addAll(Arrays.asList(ts));
return set; return set;
} }
/** /**
* 根据已有集合创建一个新的HashSet实例 * 根据已有集合创建一个新的HashSet实例
* *
* @param set 要复制的集合 * @param set 要复制的集合
* @param <T> 集合元素的类型 * @param <T> 集合元素的类型
* @return 包含原集合所有元素的新HashSet实例 * @return 包含原集合所有元素的新HashSet实例
*/ */
public static <T> Set<T> newHashSet(Set<T> set) { public static <T> Set<T> newHashSet(Set<T> set) {
Set<T> newSet = newHashSet(); Set<T> newSet = newHashSet();
newSet.addAll(set); newSet.addAll(set);
return newSet; return newSet;
} }
/** /**
* 根据可迭代对象创建一个HashSet实例 * 根据可迭代对象创建一个HashSet实例
* *
* @param iterable 可迭代对象 * @param iterable 可迭代对象
* @param <T> 集合元素的类型 * @param <T> 集合元素的类型
* @return 包含可迭代对象中所有元素的新HashSet实例 * @return 包含可迭代对象中所有元素的新HashSet实例
*/ */
public static <T> Set<T> newHashSet(Iterable<T> iterable) { public static <T> Set<T> newHashSet(Iterable<T> iterable) {
Set<T> set = newHashSet(); Set<T> set = newHashSet();
for (T t : iterable) { for (T t : iterable) {
set.add(t); set.add(t);
} }
return set; return set;
} }
/** /**
* 创建一个指定初始容量的空HashSet实例 * 创建一个指定初始容量的空HashSet实例
* *
* @param size 初始容量大小 * @param size 初始容量大小
* @param <T> 集合元素的类型 * @param <T> 集合元素的类型
* @return 指定初始容量的空HashSet实例 * @return 指定初始容量的空HashSet实例
*/ */
public static <T> Set<T> newHashSet(int size) { public static <T> Set<T> newHashSet(int size) {
return new HashSet<>(size); return new HashSet<>(size);
} }
/** /**
* 创建一个空的LinkedHashSet实例 * 创建一个空的LinkedHashSet实例
* *
* @param <T> 集合元素的类型 * @param <T> 集合元素的类型
* @return 新创建的空LinkedHashSet实例 * @return 新创建的空LinkedHashSet实例
*/ */
public static <T> Set<T> newLinkedHashSet() { public static <T> Set<T> newLinkedHashSet() {
return new LinkedHashSet<>(); return new LinkedHashSet<>();
} }
/** /**
* 根据可变参数创建一个包含指定元素的LinkedHashSet实例 * 根据可变参数创建一个包含指定元素的LinkedHashSet实例
* *
* @param ts 要添加到集合中的元素可以为0个或多个 * @param ts 要添加到集合中的元素可以为0个或多个
* @param <T> 集合元素的类型 * @param <T> 集合元素的类型
* @return 包含指定元素的新LinkedHashSet实例 * @return 包含指定元素的新LinkedHashSet实例
*/ */
public static <T> Set<T> newLinkedHashSet(T... ts) { public static <T> Set<T> newLinkedHashSet(T... ts) {
Set<T> set = newLinkedHashSet(); Set<T> set = newLinkedHashSet();
set.addAll(Arrays.asList(ts)); set.addAll(Arrays.asList(ts));
return set; return set;
} }
/** /**
* 根据已有集合创建一个新的LinkedHashSet实例 * 根据已有集合创建一个新的LinkedHashSet实例
* *
* @param set 要复制的集合 * @param set 要复制的集合
* @param <T> 集合元素的类型 * @param <T> 集合元素的类型
* @return 包含原集合所有元素的新LinkedHashSet实例 * @return 包含原集合所有元素的新LinkedHashSet实例
*/ */
public static <T> Set<T> newLinkedHashSet(Set<T> set) { public static <T> Set<T> newLinkedHashSet(Set<T> set) {
Set<T> newSet = newLinkedHashSet(); Set<T> newSet = newLinkedHashSet();
newSet.addAll(set); newSet.addAll(set);
return newSet; return newSet;
} }
/** /**
* 创建一个空的TreeSet实例 * 创建一个空的TreeSet实例
* *
* @param <T> 集合元素的类型必须实现Comparable接口 * @param <T> 集合元素的类型必须实现Comparable接口
* @return 新创建的空TreeSet实例 * @return 新创建的空TreeSet实例
*/ */
public static <T extends Comparable<T>> Set<T> newTreeSet() { public static <T extends Comparable<T>> Set<T> newTreeSet() {
return new TreeSet<>(); return new TreeSet<>();
} }
/** /**
* 根据可变参数创建一个包含指定元素的TreeSet实例 * 根据可变参数创建一个包含指定元素的TreeSet实例
* *
* @param ts 要添加到集合中的元素可以为0个或多个 * @param ts 要添加到集合中的元素可以为0个或多个
* @param <T> 集合元素的类型必须实现Comparable接口 * @param <T> 集合元素的类型必须实现Comparable接口
* @return 包含指定元素的新TreeSet实例 * @return 包含指定元素的新TreeSet实例
*/ */
public static <T extends Comparable<T>> Set<T> newTreeSet(T... ts) { public static <T extends Comparable<T>> Set<T> newTreeSet(T... ts) {
Set<T> set = newTreeSet(); Set<T> set = newTreeSet();
set.addAll(Arrays.asList(ts)); set.addAll(Arrays.asList(ts));
return set; return set;
} }
/** /**
* 根据已有集合创建一个新的TreeSet实例 * 根据已有集合创建一个新的TreeSet实例
* *
* @param set 要复制的集合 * @param set 要复制的集合
* @param <T> 集合元素的类型必须实现Comparable接口 * @param <T> 集合元素的类型必须实现Comparable接口
* @return 包含原集合所有元素的新TreeSet实例 * @return 包含原集合所有元素的新TreeSet实例
*/ */
public static <T extends Comparable<T>> Set<T> newTreeSet(Set<T> set) { public static <T extends Comparable<T>> Set<T> newTreeSet(Set<T> set) {
Set<T> newSet = newTreeSet(); Set<T> newSet = newTreeSet();
newSet.addAll(set); newSet.addAll(set);
return newSet; return newSet;
} }
} }

View File

@ -12,73 +12,73 @@ import java.util.concurrent.atomic.AtomicReference;
*/ */
public class IsChanged<T> { public class IsChanged<T> {
/** /**
* 使用 AtomicReference 来保证对数据的原子操作 * 使用 AtomicReference 来保证对数据的原子操作
*/ */
private final AtomicReference<T> atomicReferenceData; private final AtomicReference<T> atomicReferenceData;
/** /**
* 默认构造函数初始化数据为 null * 默认构造函数初始化数据为 null
*/ */
public IsChanged() { public IsChanged() {
this(null); this(null);
} }
/** /**
* 带参数的构造函数使用指定的初始值初始化 * 带参数的构造函数使用指定的初始值初始化
* *
* @param data 初始数据值 * @param data 初始数据值
*/ */
public IsChanged(T data) { public IsChanged(T data) {
atomicReferenceData = new AtomicReference<>(data); atomicReferenceData = new AtomicReference<>(data);
} }
/** /**
* 设置新的数据值不检查是否发生变化 * 设置新的数据值不检查是否发生变化
* *
* @param data 要设置的新数据值 * @param data 要设置的新数据值
*/ */
public void set(T data) { public void set(T data) {
atomicReferenceData.set(data); atomicReferenceData.set(data);
} }
/** /**
* 获取当前数据值 * 获取当前数据值
* *
* @return 当前数据值 * @return 当前数据值
*/ */
public T get() { public T get() {
return atomicReferenceData.get(); return atomicReferenceData.get();
} }
/** /**
* 设置新的数据值并返回旧的数据值 * 设置新的数据值并返回旧的数据值
* *
* @param data 要设置的新数据值 * @param data 要设置的新数据值
* @return 设置前的旧数据值 * @return 设置前的旧数据值
*/ */
public T setAndGet(T data) { public T setAndGet(T data) {
return atomicReferenceData.getAndSet(data); return atomicReferenceData.getAndSet(data);
} }
/** /**
* 设置新的数据值如果新值与当前值不同则更新并返回 true否则返回 false * 设置新的数据值如果新值与当前值不同则更新并返回 true否则返回 false
* 使用 CAS(Compare-And-Swap) 操作确保线程安全 * 使用 CAS(Compare-And-Swap) 操作确保线程安全
* *
* @param data 要设置的新数据值 * @param data 要设置的新数据值
* @return 如果值发生变化返回 true否则返回 false * @return 如果值发生变化返回 true否则返回 false
*/ */
public boolean setAndChanged(T data) { public boolean setAndChanged(T data) {
T currentData; T currentData;
do { do {
currentData = get(); currentData = get();
// 如果新值与当前值相等则认为没有变化直接返回 false // 如果新值与当前值相等则认为没有变化直接返回 false
if (Objects.equals(data, currentData)) { if (Objects.equals(data, currentData)) {
return false; return false;
} }
// 使用 CAS 操作尝试更新值如果失败则重试 // 使用 CAS 操作尝试更新值如果失败则重试
} while (!atomicReferenceData.compareAndSet(currentData, data)); } while (!atomicReferenceData.compareAndSet(currentData, data));
// 成功更新值返回 true 表示发生了变化 // 成功更新值返回 true 表示发生了变化
return true; return true;
} }
} }

View File

@ -10,74 +10,74 @@ package com.mingliqiye.utils.data;
*/ */
public class ThreadLocalDataHolder<T> { public class ThreadLocalDataHolder<T> {
private final ThreadLocal<T> threadLocal; private final ThreadLocal<T> threadLocal;
/** /**
* 构造函数初始化 ThreadLocal 实例 * 构造函数初始化 ThreadLocal 实例
*/ */
public ThreadLocalDataHolder() { public ThreadLocalDataHolder() {
this.threadLocal = new ThreadLocal<>(); this.threadLocal = new ThreadLocal<>();
} }
/** /**
* 获取当前线程存储的值 * 获取当前线程存储的值
* *
* @return 当前线程存储的值如果没有则返回null * @return 当前线程存储的值如果没有则返回null
*/ */
public T get() { public T get() {
return threadLocal.get(); return threadLocal.get();
} }
/** /**
* 设置当前线程的值 * 设置当前线程的值
* *
* @param value 要存储的值 * @param value 要存储的值
*/ */
public void set(T value) { public void set(T value) {
threadLocal.set(value); threadLocal.set(value);
} }
/** /**
* 移除当前线程存储的值 * 移除当前线程存储的值
* <p> * <p>
* 防止内存泄漏使用完毕后应调用此方法清理资源 * 防止内存泄漏使用完毕后应调用此方法清理资源
*/ */
public void remove() { public void remove() {
threadLocal.remove(); threadLocal.remove();
} }
/** /**
* 获取当前线程存储的值如果不存在则返回默认值 * 获取当前线程存储的值如果不存在则返回默认值
* *
* @param defaultValue 默认值 * @param defaultValue 默认值
* @return 当前线程存储的值或默认值 * @return 当前线程存储的值或默认值
*/ */
public T getOrDefault(T defaultValue) { public T getOrDefault(T defaultValue) {
T value = threadLocal.get(); T value = threadLocal.get();
return value != null ? value : defaultValue; return value != null ? value : defaultValue;
} }
/** /**
* 安全获取值避免NPE * 安全获取值避免NPE
* <p> * <p>
* 在某些异常情况下防止抛出异常直接返回 null * 在某些异常情况下防止抛出异常直接返回 null
* *
* @return 值或null * @return 值或null
*/ */
public T safeGet() { public T safeGet() {
try { try {
return threadLocal.get(); return threadLocal.get();
} catch (Exception e) { } catch (Exception e) {
return null; return null;
} }
} }
/** /**
* 检查当前线程是否有值 * 检查当前线程是否有值
* *
* @return 是否有值 * @return 是否有值
*/ */
public boolean isPresent() { public boolean isPresent() {
return threadLocal.get() != null; return threadLocal.get() != null;
} }
} }

View File

@ -1,9 +1,6 @@
package com.mingliqiye.utils.file; package com.mingliqiye.utils.file;
import com.mingliqiye.utils.string.StringUtil; import com.mingliqiye.utils.string.StringUtil;
import lombok.Getter;
import lombok.Setter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -15,6 +12,8 @@ import java.nio.file.Paths;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import lombok.Getter;
import lombok.Setter;
/** /**
* 文件工具类提供常用的文件操作方法 * 文件工具类提供常用的文件操作方法
@ -23,322 +22,322 @@ import java.util.List;
*/ */
public class FileUtil { public class FileUtil {
/** /**
* 默认字符集 * 默认字符集
*/ */
@Getter @Getter
@Setter @Setter
private static Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; private static Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
/** /**
* 读取文件内容为字符串 * 读取文件内容为字符串
* *
* @param filePath 文件路径 * @param filePath 文件路径
* @return 文件内容字符串 * @return 文件内容字符串
* @throws IOException 读取文件时发生错误 * @throws IOException 读取文件时发生错误
*/ */
public static String readFileToString(String filePath) throws IOException { public static String readFileToString(String filePath) throws IOException {
return readFileToString(filePath, DEFAULT_CHARSET); return readFileToString(filePath, DEFAULT_CHARSET);
} }
/** /**
* 读取文件内容为字符串 * 读取文件内容为字符串
* *
* @param filePath 文件路径 * @param filePath 文件路径
* @param charset 字符集 * @param charset 字符集
* @return 文件内容字符串 * @return 文件内容字符串
* @throws IOException 读取文件时发生错误 * @throws IOException 读取文件时发生错误
*/ */
public static String readFileToString(String filePath, Charset charset) public static String readFileToString(String filePath, Charset charset)
throws IOException { throws IOException {
Path path = Paths.get(filePath); Path path = Paths.get(filePath);
byte[] bytes = Files.readAllBytes(path); byte[] bytes = Files.readAllBytes(path);
return new String(bytes, charset); return new String(bytes, charset);
} }
/** /**
* 将字符串写入文件 * 将字符串写入文件
* *
* @param filePath 文件路径 * @param filePath 文件路径
* @param content 要写入的内容 * @param content 要写入的内容
* @throws IOException 写入文件时发生错误 * @throws IOException 写入文件时发生错误
*/ */
public static void writeStringToFile(String filePath, String content) public static void writeStringToFile(String filePath, String content)
throws IOException { throws IOException {
writeStringToFile(filePath, content, DEFAULT_CHARSET); writeStringToFile(filePath, content, DEFAULT_CHARSET);
} }
/** /**
* 将字符串写入文件 * 将字符串写入文件
* *
* @param filePath 文件路径 * @param filePath 文件路径
* @param content 要写入的内容 * @param content 要写入的内容
* @param charset 字符集 * @param charset 字符集
* @throws IOException 写入文件时发生错误 * @throws IOException 写入文件时发生错误
*/ */
public static void writeStringToFile( public static void writeStringToFile(
String filePath, String filePath,
String content, String content,
Charset charset Charset charset
) throws IOException { ) throws IOException {
Path path = Paths.get(filePath); Path path = Paths.get(filePath);
Files.createDirectories(path.getParent()); Files.createDirectories(path.getParent());
Files.write(path, content.getBytes(charset)); Files.write(path, content.getBytes(charset));
} }
/** /**
* 读取文件内容为字符串列表按行分割 * 读取文件内容为字符串列表按行分割
* *
* @param filePath 文件路径 * @param filePath 文件路径
* @return 文件内容按行分割的字符串列表 * @return 文件内容按行分割的字符串列表
* @throws IOException 读取文件时发生错误 * @throws IOException 读取文件时发生错误
*/ */
public static List<String> readLines(String filePath) throws IOException { public static List<String> readLines(String filePath) throws IOException {
return readLines(filePath, DEFAULT_CHARSET); return readLines(filePath, DEFAULT_CHARSET);
} }
/** /**
* 读取文件内容为字符串列表按行分割 * 读取文件内容为字符串列表按行分割
* *
* @param filePath 文件路径 * @param filePath 文件路径
* @param charset 字符集 * @param charset 字符集
* @return 文件内容按行分割的字符串列表 * @return 文件内容按行分割的字符串列表
* @throws IOException 读取文件时发生错误 * @throws IOException 读取文件时发生错误
*/ */
public static List<String> readLines(String filePath, Charset charset) public static List<String> readLines(String filePath, Charset charset)
throws IOException { throws IOException {
Path path = Paths.get(filePath); Path path = Paths.get(filePath);
return Files.readAllLines(path, charset); return Files.readAllLines(path, charset);
} }
/** /**
* 将字符串列表写入文件每行一个元素 * 将字符串列表写入文件每行一个元素
* *
* @param filePath 文件路径 * @param filePath 文件路径
* @param lines 要写入的行内容列表 * @param lines 要写入的行内容列表
* @throws IOException 写入文件时发生错误 * @throws IOException 写入文件时发生错误
*/ */
public static void writeLines(String filePath, List<String> lines) public static void writeLines(String filePath, List<String> lines)
throws IOException { throws IOException {
writeLines(filePath, lines, DEFAULT_CHARSET); writeLines(filePath, lines, DEFAULT_CHARSET);
} }
/** /**
* 将字符串列表写入文件每行一个元素 * 将字符串列表写入文件每行一个元素
* *
* @param filePath 文件路径 * @param filePath 文件路径
* @param lines 要写入的行内容列表 * @param lines 要写入的行内容列表
* @param charset 字符集 * @param charset 字符集
* @throws IOException 写入文件时发生错误 * @throws IOException 写入文件时发生错误
*/ */
public static void writeLines( public static void writeLines(
String filePath, String filePath,
List<String> lines, List<String> lines,
Charset charset Charset charset
) throws IOException { ) throws IOException {
Path path = Paths.get(filePath); Path path = Paths.get(filePath);
Files.createDirectories(path.getParent()); Files.createDirectories(path.getParent());
Files.write(path, lines, charset); Files.write(path, lines, charset);
} }
/** /**
* 复制文件 * 复制文件
* *
* @param sourcePath 源文件路径 * @param sourcePath 源文件路径
* @param targetPath 目标文件路径 * @param targetPath 目标文件路径
* @throws IOException 复制文件时发生错误 * @throws IOException 复制文件时发生错误
*/ */
public static void copyFile(String sourcePath, String targetPath) public static void copyFile(String sourcePath, String targetPath)
throws IOException { throws IOException {
Path source = Paths.get(sourcePath); Path source = Paths.get(sourcePath);
Path target = Paths.get(targetPath); Path target = Paths.get(targetPath);
Files.createDirectories(target.getParent()); Files.createDirectories(target.getParent());
Files.copy(source, target); Files.copy(source, target);
} }
/** /**
* 删除文件 * 删除文件
* *
* @param filePath 文件路径 * @param filePath 文件路径
* @return 如果文件删除成功返回true否则返回false * @return 如果文件删除成功返回true否则返回false
*/ */
public static boolean deleteFile(String filePath) { public static boolean deleteFile(String filePath) {
try { try {
Path path = Paths.get(filePath); Path path = Paths.get(filePath);
return Files.deleteIfExists(path); return Files.deleteIfExists(path);
} catch (IOException e) { } catch (IOException e) {
return false; return false;
} }
} }
/** /**
* 检查文件是否存在 * 检查文件是否存在
* *
* @param filePath 文件路径 * @param filePath 文件路径
* @return 如果文件存在返回true否则返回false * @return 如果文件存在返回true否则返回false
*/ */
public static boolean exists(String filePath) { public static boolean exists(String filePath) {
Path path = Paths.get(filePath); Path path = Paths.get(filePath);
return Files.exists(path); return Files.exists(path);
} }
/** /**
* 获取文件大小 * 获取文件大小
* *
* @param filePath 文件路径 * @param filePath 文件路径
* @return 文件大小字节如果文件不存在返回-1 * @return 文件大小字节如果文件不存在返回-1
*/ */
public static long getFileSize(String filePath) { public static long getFileSize(String filePath) {
try { try {
Path path = Paths.get(filePath); Path path = Paths.get(filePath);
return Files.size(path); return Files.size(path);
} catch (IOException e) { } catch (IOException e) {
return -1; return -1;
} }
} }
/** /**
* 创建目录 * 创建目录
* *
* @param dirPath 目录路径 * @param dirPath 目录路径
* @return 如果目录创建成功返回true否则返回false * @return 如果目录创建成功返回true否则返回false
*/ */
public static boolean createDirectory(String dirPath) { public static boolean createDirectory(String dirPath) {
try { try {
Path path = Paths.get(dirPath); Path path = Paths.get(dirPath);
Files.createDirectories(path); Files.createDirectories(path);
return true; return true;
} catch (IOException e) { } catch (IOException e) {
return false; return false;
} }
} }
/** /**
* 获取文件扩展名 * 获取文件扩展名
* *
* @param fileName 文件名 * @param fileName 文件名
* @return 文件扩展名不包含点号如果无扩展名返回空字符串 * @return 文件扩展名不包含点号如果无扩展名返回空字符串
*/ */
public static String getFileExtension(String fileName) { public static String getFileExtension(String fileName) {
if (StringUtil.isEmpty(fileName)) { if (StringUtil.isEmpty(fileName)) {
return ""; return "";
} }
int lastDotIndex = fileName.lastIndexOf('.'); int lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex == -1 || lastDotIndex == fileName.length() - 1) { if (lastDotIndex == -1 || lastDotIndex == fileName.length() - 1) {
return ""; return "";
} }
return fileName.substring(lastDotIndex + 1); return fileName.substring(lastDotIndex + 1);
} }
/** /**
* 获取不带扩展名的文件名 * 获取不带扩展名的文件名
* *
* @param fileName 文件名 * @param fileName 文件名
* @return 不带扩展名的文件名 * @return 不带扩展名的文件名
*/ */
public static String getFileNameWithoutExtension(String fileName) { public static String getFileNameWithoutExtension(String fileName) {
if (StringUtil.isEmpty(fileName)) { if (StringUtil.isEmpty(fileName)) {
return ""; return "";
} }
int lastDotIndex = fileName.lastIndexOf('.'); int lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex == -1) { if (lastDotIndex == -1) {
return fileName; return fileName;
} }
return fileName.substring(0, lastDotIndex); return fileName.substring(0, lastDotIndex);
} }
/** /**
* 读取文件内容为字节数组 * 读取文件内容为字节数组
* *
* @param filePath 文件路径 * @param filePath 文件路径
* @return 文件内容的字节数组 * @return 文件内容的字节数组
* @throws IOException 读取文件时发生错误 * @throws IOException 读取文件时发生错误
*/ */
public static byte[] readFileToByteArray(String filePath) public static byte[] readFileToByteArray(String filePath)
throws IOException { throws IOException {
Path path = Paths.get(filePath); Path path = Paths.get(filePath);
return Files.readAllBytes(path); return Files.readAllBytes(path);
} }
/** /**
* 将字节数组写入文件 * 将字节数组写入文件
* *
* @param filePath 文件路径 * @param filePath 文件路径
* @param data 要写入的字节数据 * @param data 要写入的字节数据
* @throws IOException 写入文件时发生错误 * @throws IOException 写入文件时发生错误
*/ */
public static void writeByteArrayToFile(String filePath, byte[] data) public static void writeByteArrayToFile(String filePath, byte[] data)
throws IOException { throws IOException {
Path path = Paths.get(filePath); Path path = Paths.get(filePath);
Files.createDirectories(path.getParent()); Files.createDirectories(path.getParent());
Files.write(path, data); Files.write(path, data);
} }
/** /**
* 将字节数组追加到文件末尾 * 将字节数组追加到文件末尾
* *
* @param filePath 文件路径 * @param filePath 文件路径
* @param data 要追加的字节数据 * @param data 要追加的字节数据
* @throws IOException 追加数据时发生错误 * @throws IOException 追加数据时发生错误
*/ */
public static void appendByteArrayToFile(String filePath, byte[] data) public static void appendByteArrayToFile(String filePath, byte[] data)
throws IOException { throws IOException {
Path path = Paths.get(filePath); Path path = Paths.get(filePath);
Files.createDirectories(path.getParent()); Files.createDirectories(path.getParent());
Files.write( Files.write(
path, path,
data, data,
StandardOpenOption.CREATE, StandardOpenOption.CREATE,
StandardOpenOption.APPEND StandardOpenOption.APPEND
); );
} }
/** /**
* 分块读取大文件为字节数组列表 * 分块读取大文件为字节数组列表
* *
* @param filePath 文件路径 * @param filePath 文件路径
* @param chunkSize 每块大小字节 * @param chunkSize 每块大小字节
* @return 文件内容按指定大小分割的字节数组列表 * @return 文件内容按指定大小分割的字节数组列表
* @throws IOException 读取文件时发生错误 * @throws IOException 读取文件时发生错误
*/ */
public static List<byte[]> readFileToByteArrayChunks( public static List<byte[]> readFileToByteArrayChunks(
String filePath, String filePath,
int chunkSize int chunkSize
) throws IOException { ) throws IOException {
List<byte[]> chunks = new ArrayList<>(); List<byte[]> chunks = new ArrayList<>();
Path path = Paths.get(filePath); Path path = Paths.get(filePath);
try (InputStream inputStream = Files.newInputStream(path)) { try (InputStream inputStream = Files.newInputStream(path)) {
byte[] buffer = new byte[chunkSize]; byte[] buffer = new byte[chunkSize];
int bytesRead; int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) { while ((bytesRead = inputStream.read(buffer)) != -1) {
byte[] chunk = new byte[bytesRead]; byte[] chunk = new byte[bytesRead];
System.arraycopy(buffer, 0, chunk, 0, bytesRead); System.arraycopy(buffer, 0, chunk, 0, bytesRead);
chunks.add(chunk); chunks.add(chunk);
} }
} }
return chunks; return chunks;
} }
/** /**
* 将字节数组列表写入文件 * 将字节数组列表写入文件
* *
* @param filePath 文件路径 * @param filePath 文件路径
* @param chunks 字节数组列表 * @param chunks 字节数组列表
* @throws IOException 写入文件时发生错误 * @throws IOException 写入文件时发生错误
*/ */
public static void writeByteArrayChunksToFile( public static void writeByteArrayChunksToFile(
String filePath, String filePath,
List<byte[]> chunks List<byte[]> chunks
) throws IOException { ) throws IOException {
Path path = Paths.get(filePath); Path path = Paths.get(filePath);
Files.createDirectories(path.getParent()); Files.createDirectories(path.getParent());
try (OutputStream outputStream = Files.newOutputStream(path)) { try (OutputStream outputStream = Files.newOutputStream(path)) {
for (byte[] chunk : chunks) { for (byte[] chunk : chunks) {
outputStream.write(chunk); outputStream.write(chunk);
} }
} }
} }
} }

View File

@ -9,63 +9,63 @@ import java.util.concurrent.*;
*/ */
public class Debouncer { public class Debouncer {
private final ScheduledExecutorService scheduler = private final ScheduledExecutorService scheduler =
Executors.newSingleThreadScheduledExecutor(); Executors.newSingleThreadScheduledExecutor();
private final ConcurrentHashMap<Object, Future<?>> delayedMap = private final ConcurrentHashMap<Object, Future<?>> delayedMap =
new ConcurrentHashMap<>(); new ConcurrentHashMap<>();
private final long delay; private final long delay;
/** /**
* 构造函数创建一个防抖器实例 * 构造函数创建一个防抖器实例
* *
* @param delay 延迟时间 * @param delay 延迟时间
* @param unit 时间单位 * @param unit 时间单位
*/ */
public Debouncer(long delay, TimeUnit unit) { public Debouncer(long delay, TimeUnit unit) {
this.delay = unit.toMillis(delay); this.delay = unit.toMillis(delay);
} }
/** /**
* 执行防抖操作如果在指定延迟时间内再次调用相同key的任务则取消之前的任务并重新计时 * 执行防抖操作如果在指定延迟时间内再次调用相同key的任务则取消之前的任务并重新计时
* *
* @param key 任务的唯一标识符用于区分不同任务 * @param key 任务的唯一标识符用于区分不同任务
* @param task 要执行的任务 * @param task 要执行的任务
*/ */
public void debounce(final Object key, final Runnable task) { public void debounce(final Object key, final Runnable task) {
// 提交新任务并获取之前可能存在的任务 // 提交新任务并获取之前可能存在的任务
final Future<?> prev = delayedMap.put( final Future<?> prev = delayedMap.put(
key, key,
scheduler.schedule( scheduler.schedule(
() -> { () -> {
try { try {
task.run(); task.run();
} finally { } finally {
// 任务执行完成后从映射中移除 // 任务执行完成后从映射中移除
delayedMap.remove(key); delayedMap.remove(key);
} }
}, },
delay, delay,
TimeUnit.MILLISECONDS TimeUnit.MILLISECONDS
) )
); );
// 如果之前存在任务则取消它 // 如果之前存在任务则取消它
if (prev != null) { if (prev != null) {
prev.cancel(true); prev.cancel(true);
} }
} }
/** /**
* 关闭防抖器取消所有待执行的任务并关闭调度器 * 关闭防抖器取消所有待执行的任务并关闭调度器
*/ */
public void shutdown() { public void shutdown() {
// 先取消所有延迟任务 // 先取消所有延迟任务
for (Future<?> future : delayedMap.values()) { for (Future<?> future : delayedMap.values()) {
future.cancel(true); future.cancel(true);
} }
delayedMap.clear(); delayedMap.clear();
// 再关闭调度器 // 再关闭调度器
scheduler.shutdownNow(); scheduler.shutdownNow();
} }
} }

View File

@ -1,14 +1,13 @@
package com.mingliqiye.utils.hash; package com.mingliqiye.utils.hash;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.mindrot.jbcrypt.BCrypt;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.Security; import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.mindrot.jbcrypt.BCrypt;
/** /**
* 提供常用的哈希计算工具方法包括文件哈希值计算BCrypt 加密等 * 提供常用的哈希计算工具方法包括文件哈希值计算BCrypt 加密等
@ -17,77 +16,77 @@ import java.security.Security;
*/ */
public class HashUtils { public class HashUtils {
static { static {
Security.addProvider(new BouncyCastleProvider()); Security.addProvider(new BouncyCastleProvider());
} }
/** /**
* 计算指定文件的哈希值 * 计算指定文件的哈希值
* *
* @param file 要计算哈希值的文件对象 * @param file 要计算哈希值的文件对象
* @param algorithm 使用的哈希算法名称 SHA-256MD5 * @param algorithm 使用的哈希算法名称 SHA-256MD5
* @return 文件的十六进制格式哈希值字符串 * @return 文件的十六进制格式哈希值字符串
* @throws IOException 当文件不存在或读取过程中发生 I/O 错误时抛出 * @throws IOException 当文件不存在或读取过程中发生 I/O 错误时抛出
* @throws NoSuchAlgorithmException 当指定的哈希算法不可用时抛出 * @throws NoSuchAlgorithmException 当指定的哈希算法不可用时抛出
*/ */
public static String calculateFileHash(File file, String algorithm) public static String calculateFileHash(File file, String algorithm)
throws IOException, NoSuchAlgorithmException { throws IOException, NoSuchAlgorithmException {
// 检查文件是否存在 // 检查文件是否存在
if (!file.exists()) { if (!file.exists()) {
throw new IOException("File not found: " + file.getAbsolutePath()); throw new IOException("File not found: " + file.getAbsolutePath());
} }
MessageDigest digest = MessageDigest.getInstance(algorithm); MessageDigest digest = MessageDigest.getInstance(algorithm);
try (FileInputStream fis = new FileInputStream(file)) { try (FileInputStream fis = new FileInputStream(file)) {
byte[] buffer = new byte[8192]; // 8KB 缓冲区 byte[] buffer = new byte[8192]; // 8KB 缓冲区
int bytesRead; int bytesRead;
// 分块读取文件内容并更新摘要 // 分块读取文件内容并更新摘要
while ((bytesRead = fis.read(buffer)) != -1) { while ((bytesRead = fis.read(buffer)) != -1) {
digest.update(buffer, 0, bytesRead); digest.update(buffer, 0, bytesRead);
} }
} }
return bytesToHex(digest.digest()); return bytesToHex(digest.digest());
} }
/** /**
* 将字节数组转换为十六进制字符串表示 * 将字节数组转换为十六进制字符串表示
* *
* @param bytes 输入的字节数组 * @param bytes 输入的字节数组
* @return 对应的十六进制字符串 * @return 对应的十六进制字符串
*/ */
private static String bytesToHex(byte[] bytes) { private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder(2 * bytes.length); StringBuilder hexString = new StringBuilder(2 * bytes.length);
for (byte b : bytes) { for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b); String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) { if (hex.length() == 1) {
hexString.append('0'); hexString.append('0');
} }
hexString.append(hex); hexString.append(hex);
} }
return hexString.toString(); return hexString.toString();
} }
/** /**
* 使用 BCrypt 算法对字符串进行加密 * 使用 BCrypt 算法对字符串进行加密
* *
* @param string 需要加密的明文字符串 * @param string 需要加密的明文字符串
* @return 加密后的 BCrypt 哈希字符串 * @return 加密后的 BCrypt 哈希字符串
*/ */
public static String bcrypt(String string) { public static String bcrypt(String string) {
return BCrypt.hashpw(string, BCrypt.gensalt()); return BCrypt.hashpw(string, BCrypt.gensalt());
} }
/** /**
* 验证给定字符串与 BCrypt 哈希是否匹配 * 验证给定字符串与 BCrypt 哈希是否匹配
* *
* @param string 明文字符串 * @param string 明文字符串
* @param bcrypted 已经使用 BCrypt 加密的哈希字符串 * @param bcrypted 已经使用 BCrypt 加密的哈希字符串
* @return 如果匹配返回 true否则返回 false * @return 如果匹配返回 true否则返回 false
*/ */
public static boolean checkBcrypt(String string, String bcrypted) { public static boolean checkBcrypt(String string, String bcrypted) {
return BCrypt.checkpw(string, bcrypted); return BCrypt.checkpw(string, bcrypted);
} }
} }

View File

@ -11,37 +11,37 @@ import lombok.ToString;
@Getter @Getter
public class Response<T> { public class Response<T> {
private final String time = DateTime.now().format( private final String time = DateTime.now().format(
Formatter.STANDARD_DATETIME_MILLISECOUND7 Formatter.STANDARD_DATETIME_MILLISECOUND7
); );
private String message; private String message;
private T data; private T data;
private int statusCode; private int statusCode;
public Response(String message, T data, int statusCode) { public Response(String message, T data, int statusCode) {
this.message = message; this.message = message;
this.data = data; this.data = data;
this.statusCode = statusCode; this.statusCode = statusCode;
} }
public static <T> Response<T> ok(T data) { public static <T> Response<T> ok(T data) {
return new Response<>("操作成功", data, 200); return new Response<>("操作成功", data, 200);
} }
public Response<T> setMessage(String message) { public Response<T> setMessage(String message) {
this.message = message; this.message = message;
return this; return this;
} }
public Response<T> setData(T data) { public Response<T> setData(T data) {
this.data = data; this.data = data;
return Response.ok(getData()) return Response.ok(getData())
.setMessage(getMessage()) .setMessage(getMessage())
.setStatusCode(getStatusCode()); .setStatusCode(getStatusCode());
} }
public Response<T> setStatusCode(int statusCode) { public Response<T> setStatusCode(int statusCode) {
this.statusCode = statusCode; this.statusCode = statusCode;
return this; return this;
} }
} }

View File

@ -5,6 +5,6 @@ import lombok.Data;
@Data @Data
public class Description { public class Description {
private String text; private String text;
private Extra[] extra; private Extra[] extra;
} }

View File

@ -5,8 +5,8 @@ import lombok.Data;
@Data @Data
public class Extra { public class Extra {
private String text; private String text;
private String color; private String color;
private Boolean bold; private Boolean bold;
private Boolean italic; private Boolean italic;
} }

View File

@ -5,11 +5,11 @@ import lombok.Data;
@Data @Data
public class MinecraftServerStatus { public class MinecraftServerStatus {
private Description description; private Description description;
private Players players; private Players players;
private Version version; private Version version;
private String favicon; private String favicon;
private boolean enforcesSecureChat; private boolean enforcesSecureChat;
private boolean previewsChat; private boolean previewsChat;
private String jsonData; private String jsonData;
} }

View File

@ -5,6 +5,6 @@ import lombok.Data;
@Data @Data
public class PlayerSample { public class PlayerSample {
private String name; private String name;
private String id; private String id;
} }

View File

@ -5,7 +5,7 @@ import lombok.Data;
@Data @Data
public class Players { public class Players {
private int max; private int max;
private int online; private int online;
private PlayerSample[] sample; private PlayerSample[] sample;
} }

View File

@ -2,7 +2,6 @@ package com.mingliqiye.utils.minecraft.slp;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.mingliqiye.utils.network.NetworkEndpoint; import com.mingliqiye.utils.network.NetworkEndpoint;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
@ -17,182 +16,182 @@ import java.nio.ByteOrder;
*/ */
public class SLP { public class SLP {
private static final ObjectMapper objectMapper = new ObjectMapper(); private static final ObjectMapper objectMapper = new ObjectMapper();
/** /**
* int32 值截断为无符号 short2 字节并按大端序写入字节数组 * int32 值截断为无符号 short2 字节并按大端序写入字节数组
* *
* @param value 需要转换的整数int32 * @param value 需要转换的整数int32
* @return 包含两个字节的数组表示无符号 short * @return 包含两个字节的数组表示无符号 short
*/ */
public static byte[] toUnsignedShort(int value) { public static byte[] toUnsignedShort(int value) {
byte[] array = new byte[2]; byte[] array = new byte[2];
ByteBuffer.wrap(array, 0, 2) ByteBuffer.wrap(array, 0, 2)
.order(ByteOrder.BIG_ENDIAN) .order(ByteOrder.BIG_ENDIAN)
.putShort((short) (value & 0xFFFF)); .putShort((short) (value & 0xFFFF));
return array; return array;
} }
/** /**
* 构造 Minecraft 握手包数据 * 构造 Minecraft 握手包数据
* 握手包用于初始化客户端与服务器之间的连接 * 握手包用于初始化客户端与服务器之间的连接
* *
* @param serverIP 服务器 IP 地址或域名 * @param serverIP 服务器 IP 地址或域名
* @param serverPort 服务器端口号 * @param serverPort 服务器端口号
* @param type 连接类型通常为 1 表示获取状态 * @param type 连接类型通常为 1 表示获取状态
* @return 握手包的完整字节数组 * @return 握手包的完整字节数组
* @throws IOException 如果构造过程中发生 IO 错误 * @throws IOException 如果构造过程中发生 IO 错误
*/ */
public static byte[] getHandshakePack( public static byte[] getHandshakePack(
String serverIP, String serverIP,
int serverPort, int serverPort,
int type int type
) throws IOException { ) throws IOException {
ByteArrayOutputStream pack = new ByteArrayOutputStream(); ByteArrayOutputStream pack = new ByteArrayOutputStream();
ByteArrayOutputStream byteArrayOutputStream = ByteArrayOutputStream byteArrayOutputStream =
new ByteArrayOutputStream(); new ByteArrayOutputStream();
pack.write(0x00); // 握手包标识符 pack.write(0x00); // 握手包标识符
pack.write(toVarInt(1156)); // 协议版本号示例值 pack.write(toVarInt(1156)); // 协议版本号示例值
byte[] sip = serverIP.getBytes(); byte[] sip = serverIP.getBytes();
pack.write(toVarInt(sip.length)); // 服务器地址长度 pack.write(toVarInt(sip.length)); // 服务器地址长度
pack.write(sip); // 服务器地址 pack.write(sip); // 服务器地址
pack.write(toUnsignedShort(serverPort)); // 服务器端口 pack.write(toUnsignedShort(serverPort)); // 服务器端口
pack.write(toVarInt(type)); // 下一阶段类型1 表示状态请求 pack.write(toVarInt(type)); // 下一阶段类型1 表示状态请求
byteArrayOutputStream.write(toVarInt(pack.size())); // 包长度前缀 byteArrayOutputStream.write(toVarInt(pack.size())); // 包长度前缀
byteArrayOutputStream.write(pack.toByteArray()); byteArrayOutputStream.write(pack.toByteArray());
return byteArrayOutputStream.toByteArray(); return byteArrayOutputStream.toByteArray();
} }
/** /**
* 获取状态请求包的固定字节表示 * 获取状态请求包的固定字节表示
* 此包用于向服务器请求当前状态信息 * 此包用于向服务器请求当前状态信息
* *
* @return 状态请求包的字节数组 * @return 状态请求包的字节数组
*/ */
public static byte[] getStatusPack() { public static byte[] getStatusPack() {
return new byte[] { 0x01, 0x00 }; return new byte[] { 0x01, 0x00 };
} }
/** /**
* 从输入流中读取服务器返回的状态 JSON 数据并解析为 MinecraftServerStatus 实体对象 * 从输入流中读取服务器返回的状态 JSON 数据并解析为 MinecraftServerStatus 实体对象
* *
* @param inputStream 输入流包含服务器响应的数据 * @param inputStream 输入流包含服务器响应的数据
* @return 解析后的 MinecraftServerStatus 对象 * @return 解析后的 MinecraftServerStatus 对象
* @throws IOException 如果读取过程中发生 IO 错误 * @throws IOException 如果读取过程中发生 IO 错误
*/ */
public static MinecraftServerStatus getStatusJsonEntity( public static MinecraftServerStatus getStatusJsonEntity(
DataInputStream inputStream DataInputStream inputStream
) throws IOException { ) throws IOException {
readVarInt(inputStream); // 忽略第一个 VarInt包长度 readVarInt(inputStream); // 忽略第一个 VarInt包长度
inputStream.readByte(); // 忽略包标识符 inputStream.readByte(); // 忽略包标识符
int lengthjson = readVarInt(inputStream); // 读取 JSON 数据长度 int lengthjson = readVarInt(inputStream); // 读取 JSON 数据长度
byte[] data = new byte[lengthjson]; byte[] data = new byte[lengthjson];
inputStream.readFully(data); // 读取完整的 JSON 数据 inputStream.readFully(data); // 读取完整的 JSON 数据
MinecraftServerStatus serverStatus = objectMapper.readValue( MinecraftServerStatus serverStatus = objectMapper.readValue(
data, data,
MinecraftServerStatus.class MinecraftServerStatus.class
); );
serverStatus.setJsonData(new String(data)); // 设置原始 JSON 字符串 serverStatus.setJsonData(new String(data)); // 设置原始 JSON 字符串
return serverStatus; return serverStatus;
} }
/** /**
* 从输入流中读取一个 VarInt 类型的整数最多 5 个字节 * 从输入流中读取一个 VarInt 类型的整数最多 5 个字节
* *
* @param in 输入流 * @param in 输入流
* @return 解码后的整数值 * @return 解码后的整数值
* @throws IOException 如果读取过程中发生 IO 错误 * @throws IOException 如果读取过程中发生 IO 错误
*/ */
public static int readVarInt(DataInputStream in) throws IOException { public static int readVarInt(DataInputStream in) throws IOException {
int value = 0; int value = 0;
int length = 0; int length = 0;
byte currentByte; byte currentByte;
do { do {
currentByte = in.readByte(); currentByte = in.readByte();
value |= (currentByte & 0x7F) << (length * 7); value |= (currentByte & 0x7F) << (length * 7);
length += 1; length += 1;
if (length > 5) { if (length > 5) {
throw new RuntimeException("VarInt too long"); throw new RuntimeException("VarInt too long");
} }
} while ((currentByte & 0x80) != 0); } while ((currentByte & 0x80) != 0);
return value; return value;
} }
/** /**
* 将一个 int32 整数编码为 VarInt 格式的字节数组1 5 个字节 * 将一个 int32 整数编码为 VarInt 格式的字节数组1 5 个字节
* *
* @param value 需要编码的整数 * @param value 需要编码的整数
* @return 编码后的 VarInt 字节数组 * @return 编码后的 VarInt 字节数组
*/ */
public static byte[] toVarInt(int value) { public static byte[] toVarInt(int value) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream();
while (true) { while (true) {
if ((value & 0xFFFFFF80) == 0) { if ((value & 0xFFFFFF80) == 0) {
buffer.write(value); // 最后一个字节 buffer.write(value); // 最后一个字节
break; break;
} }
buffer.write((value & 0x7F) | 0x80); // 写入带继续位的字节 buffer.write((value & 0x7F) | 0x80); // 写入带继续位的字节
value >>>= 7; // 右移 7 位继续处理 value >>>= 7; // 右移 7 位继续处理
} }
return buffer.toByteArray(); return buffer.toByteArray();
} }
/** /**
* 创建一个新的 Socket 连接到指定的网络端点并设置超时时间 * 创建一个新的 Socket 连接到指定的网络端点并设置超时时间
* *
* @param networkEndpoint 目标网络端点包括主机和端口 * @param networkEndpoint 目标网络端点包括主机和端口
* @return 已连接的 Socket 实例 * @return 已连接的 Socket 实例
* @throws IOException 如果连接失败或发生 IO 错误 * @throws IOException 如果连接失败或发生 IO 错误
*/ */
public static Socket getNewConnect(NetworkEndpoint networkEndpoint) public static Socket getNewConnect(NetworkEndpoint networkEndpoint)
throws IOException { throws IOException {
Socket socket = new Socket(); Socket socket = new Socket();
socket.setSoTimeout(5000); // 设置读取超时时间为 5 socket.setSoTimeout(5000); // 设置读取超时时间为 5
socket.connect(networkEndpoint.toInetSocketAddress()); // 执行连接操作 socket.connect(networkEndpoint.toInetSocketAddress()); // 执行连接操作
return socket; return socket;
} }
/** /**
* 使用 "host:port" 格式的字符串连接到 Minecraft 服务器并获取其状态信息 * 使用 "host:port" 格式的字符串连接到 Minecraft 服务器并获取其状态信息
* *
* @param s 域名或 IP 地址加端口号组成的字符串例如 "127.0.0.1:25565" * @param s 域名或 IP 地址加端口号组成的字符串例如 "127.0.0.1:25565"
* @return 服务器状态实体对象 * @return 服务器状态实体对象
* @throws IOException 如果连接失败或发生 IO 错误 * @throws IOException 如果连接失败或发生 IO 错误
*/ */
public static MinecraftServerStatus getServerStatus(String s) public static MinecraftServerStatus getServerStatus(String s)
throws IOException { throws IOException {
return getServerStatus(NetworkEndpoint.of(s)); return getServerStatus(NetworkEndpoint.of(s));
} }
/** /**
* 使用指定的主机名和端口号连接到 Minecraft 服务器并获取其状态信息 * 使用指定的主机名和端口号连接到 Minecraft 服务器并获取其状态信息
* *
* @param s 主机名或 IP 地址 * @param s 主机名或 IP 地址
* @param i 端口号 * @param i 端口号
* @return 服务器状态实体对象 * @return 服务器状态实体对象
* @throws IOException 如果连接失败或发生 IO 错误 * @throws IOException 如果连接失败或发生 IO 错误
*/ */
public static MinecraftServerStatus getServerStatus(String s, Integer i) public static MinecraftServerStatus getServerStatus(String s, Integer i)
throws IOException { throws IOException {
return getServerStatus(NetworkEndpoint.of(s, i)); return getServerStatus(NetworkEndpoint.of(s, i));
} }
/** /**
* 使用 NetworkEndpoint 实例连接到 Minecraft 服务器并获取其状态信息 * 使用 NetworkEndpoint 实例连接到 Minecraft 服务器并获取其状态信息
* *
* @param e 网络端点实例包含主机和端口信息 * @param e 网络端点实例包含主机和端口信息
* @return 服务器状态实体对象 * @return 服务器状态实体对象
* @throws IOException 如果连接失败或发生 IO 错误 * @throws IOException 如果连接失败或发生 IO 错误
* @see NetworkEndpoint * @see NetworkEndpoint
*/ */
public static MinecraftServerStatus getServerStatus(NetworkEndpoint e) public static MinecraftServerStatus getServerStatus(NetworkEndpoint e)
throws IOException { throws IOException {
Socket socket = getNewConnect(e); // 建立 TCP 连接 Socket socket = getNewConnect(e); // 建立 TCP 连接
OutputStream out = socket.getOutputStream(); // 获取输出流发送数据 OutputStream out = socket.getOutputStream(); // 获取输出流发送数据
DataInputStream in = new DataInputStream(socket.getInputStream()); // 获取输入流接收数据 DataInputStream in = new DataInputStream(socket.getInputStream()); // 获取输入流接收数据
out.write(getHandshakePack(e.getHost(), e.getPort(), 1)); // 发送握手包 out.write(getHandshakePack(e.getHost(), e.getPort(), 1)); // 发送握手包
out.write(getStatusPack()); // 发送状态请求包 out.write(getStatusPack()); // 发送状态请求包
return getStatusJsonEntity(in); // 读取并解析服务器响应 return getStatusJsonEntity(in); // 读取并解析服务器响应
} }
} }

View File

@ -5,6 +5,6 @@ import lombok.Data;
@Data @Data
public class Version { public class Version {
private String name; private String name;
private int protocol; private int protocol;
} }

View File

@ -2,9 +2,9 @@ package com.mingliqiye.utils.network;
public class NetWorkUtil { public class NetWorkUtil {
public static void main(String[] args) { public static void main(String[] args) {
System.out.println(NetworkEndpoint.of("127.0.0.1", 25565)); System.out.println(NetworkEndpoint.of("127.0.0.1", 25565));
System.out.println(NetworkEndpoint.of("127.0.0.1:25565")); System.out.println(NetworkEndpoint.of("127.0.0.1:25565"));
System.out.println(NetworkEndpoint.of("127.0.0.1:25565")); System.out.println(NetworkEndpoint.of("127.0.0.1:25565"));
} }
} }

View File

@ -1,13 +1,12 @@
package com.mingliqiye.utils.network; package com.mingliqiye.utils.network;
import com.mingliqiye.utils.string.StringUtil; import com.mingliqiye.utils.string.StringUtil;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import java.io.Serializable; import java.io.Serializable;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
/** /**
* 网络地址类用于表示一个网络地址IP或域名并提供相关操作 * 网络地址类用于表示一个网络地址IP或域名并提供相关操作
@ -17,180 +16,180 @@ import java.util.regex.Pattern;
*/ */
public class NetworkAddress implements Serializable { public class NetworkAddress implements Serializable {
/** /**
* IPv6标识 * IPv6标识
*/ */
public static int IPV6 = 6; public static int IPV6 = 6;
/** /**
* IPv4标识 * IPv4标识
*/ */
public static int IPV4 = 4; public static int IPV4 = 4;
/** /**
* IPv4地址正则表达式 * IPv4地址正则表达式
*/ */
static String IPV4REG = static String IPV4REG =
"^((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2" + "^((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2" +
"(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}$"; "(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}$";
/** /**
* 编译后的IPv4地址匹配模式 * 编译后的IPv4地址匹配模式
*/ */
private static final Pattern IPV4_PATTERN = Pattern.compile(IPV4REG); private static final Pattern IPV4_PATTERN = Pattern.compile(IPV4REG);
/** /**
* IPv6地址正则表达式 * IPv6地址正则表达式
*/ */
static String IPV6REG = static String IPV6REG =
"^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|" + "^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|" +
"^(::([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})$" + "^(::([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})$" +
"|" + "|" +
"^(::)$|" + "^(::)$|" +
"^([0-9a-fA-F]{1,4}::([0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4})$|" + "^([0-9a-fA-F]{1,4}::([0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4})$|" +
"^(([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})$|" + "^(([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})$|" +
"^(([0-9a-fA-F]{1,4}:){6}(([0-9]{1,3}\\.){3}[0-9]{1,3}))$|" + "^(([0-9a-fA-F]{1,4}:){6}(([0-9]{1,3}\\.){3}[0-9]{1,3}))$|" +
"^::([fF]{4}:)?(([0-9]{1,3}\\.){3}[0-9]{1,3})$"; "^::([fF]{4}:)?(([0-9]{1,3}\\.){3}[0-9]{1,3})$";
/** /**
* 编译后的IPv6地址匹配模式 * 编译后的IPv6地址匹配模式
*/ */
private static final Pattern IPV6_PATTERN = Pattern.compile(IPV6REG); private static final Pattern IPV6_PATTERN = Pattern.compile(IPV6REG);
/** /**
* IP地址类型4 表示 IPv46 表示 IPv6 * IP地址类型4 表示 IPv46 表示 IPv6
*/ */
@Getter @Getter
private int IPv; private int IPv;
/** /**
* IP地址字符串 * IP地址字符串
*/ */
@Getter @Getter
private String ip; private String ip;
/** /**
* 域名如果输入的是域名 * 域名如果输入的是域名
*/ */
private String domain; private String domain;
/** /**
* 标识是否是域名解析来的IP * 标识是否是域名解析来的IP
*/ */
private boolean isdom; private boolean isdom;
/** /**
* 构造方法根据传入的字符串判断是IP地址还是域名并进行相应处理 * 构造方法根据传入的字符串判断是IP地址还是域名并进行相应处理
* *
* @param domip 可能是IP地址或域名的字符串 * @param domip 可能是IP地址或域名的字符串
*/ */
NetworkAddress(String domip) { NetworkAddress(String domip) {
try { try {
// 尝试将输入识别为IP地址 // 尝试将输入识别为IP地址
IPv = testIp(domip); IPv = testIp(domip);
ip = domip; ip = domip;
} catch (NetworkException e) { } catch (NetworkException e) {
try { try {
// 如果不是有效IP则尝试作为域名解析 // 如果不是有效IP则尝试作为域名解析
String ips = getHostIp(domip); String ips = getHostIp(domip);
IPv = testIp(ips); IPv = testIp(ips);
ip = ips; ip = ips;
isdom = true; isdom = true;
domain = domip; domain = domip;
} catch (UnknownHostException ex) { } catch (UnknownHostException ex) {
throw new NetworkException(ex); throw new NetworkException(ex);
} }
} }
} }
/** /**
* 静态工厂方法创建 NetworkAddress 实例 * 静态工厂方法创建 NetworkAddress 实例
* *
* @param domip 可能是IP地址或域名的字符串 * @param domip 可能是IP地址或域名的字符串
* @return 新建的 NetworkAddress 实例 * @return 新建的 NetworkAddress 实例
*/ */
public static NetworkAddress of(String domip) { public static NetworkAddress of(String domip) {
return new NetworkAddress(domip); return new NetworkAddress(domip);
} }
/** /**
* 静态工厂方法通过 InetAddress 创建 NetworkAddress 实例 * 静态工厂方法通过 InetAddress 创建 NetworkAddress 实例
* *
* @param inetAddress InetAddress 对象 * @param inetAddress InetAddress 对象
* @return 新建的 NetworkAddress 实例 * @return 新建的 NetworkAddress 实例
*/ */
public static NetworkAddress of(InetAddress inetAddress) { public static NetworkAddress of(InetAddress inetAddress) {
return new NetworkAddress(inetAddress.getHostAddress()); return new NetworkAddress(inetAddress.getHostAddress());
} }
/** /**
* 从DNS服务器解析域名获取对应的IP地址 * 从DNS服务器解析域名获取对应的IP地址
* *
* @param domain 域名 * @param domain 域名
* @return 解析出的第一个IP地址 * @return 解析出的第一个IP地址
* @throws UnknownHostException 如果域名无法解析 * @throws UnknownHostException 如果域名无法解析
*/ */
public static String getHostIp(@NotNull String domain) public static String getHostIp(@NotNull String domain)
throws UnknownHostException { throws UnknownHostException {
InetAddress[] addresses = InetAddress.getAllByName(domain.trim()); InetAddress[] addresses = InetAddress.getAllByName(domain.trim());
return addresses[0].getHostAddress(); return addresses[0].getHostAddress();
} }
/** /**
* 检测给定字符串是否为有效的IPv4或IPv6地址 * 检测给定字符串是否为有效的IPv4或IPv6地址
* *
* @param ip 要检测的IP地址字符串 * @param ip 要检测的IP地址字符串
* @return 4 表示IPv46 表示IPv6 * @return 4 表示IPv46 表示IPv6
* @throws NetworkException 如果IP格式无效 * @throws NetworkException 如果IP格式无效
*/ */
public static int testIp(String ip) { public static int testIp(String ip) {
if (ip == null) { if (ip == null) {
throw new NetworkException("IP地址不能为null"); throw new NetworkException("IP地址不能为null");
} }
String trimmedIp = ip.trim(); String trimmedIp = ip.trim();
// 判断是否匹配IPv4格式 // 判断是否匹配IPv4格式
if (IPV4_PATTERN.matcher(trimmedIp).matches()) { if (IPV4_PATTERN.matcher(trimmedIp).matches()) {
return IPV4; return IPV4;
} }
// 判断是否匹配IPv6格式 // 判断是否匹配IPv6格式
if (IPV6_PATTERN.matcher(trimmedIp).matches()) { if (IPV6_PATTERN.matcher(trimmedIp).matches()) {
return IPV6; return IPV6;
} }
// 不符合任一格式时抛出异常 // 不符合任一格式时抛出异常
throw new NetworkException( throw new NetworkException(
StringUtil.format("[{}] 不是有效的IPv4或IPv6地址", ip) StringUtil.format("[{}] 不是有效的IPv4或IPv6地址", ip)
); );
} }
/** /**
* 将当前 NetworkAddress 转换为 InetAddress 对象 * 将当前 NetworkAddress 转换为 InetAddress 对象
* *
* @return InetAddress 对象 * @return InetAddress 对象
*/ */
public InetAddress toInetAddress() { public InetAddress toInetAddress() {
try { try {
return InetAddress.getByName(ip != null ? ip : domain); return InetAddress.getByName(ip != null ? ip : domain);
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
/** /**
* 返回 NetworkAddress 的字符串表示形式 * 返回 NetworkAddress 的字符串表示形式
* *
* @return 字符串表示 * @return 字符串表示
*/ */
public String toString() { public String toString() {
return isdom return isdom
? StringUtil.format( ? StringUtil.format(
"NetworkAddress(IP='{}',type='{}'," + "domain='{}')", "NetworkAddress(IP='{}',type='{}'," + "domain='{}')",
ip, ip,
IPv, IPv,
domain domain
) )
: StringUtil.format("NetworkAddress(IP='{}',type='{}')", ip, IPv); : StringUtil.format("NetworkAddress(IP='{}',type='{}')", ip, IPv);
} }
} }

View File

@ -1,10 +1,9 @@
package com.mingliqiye.utils.network; package com.mingliqiye.utils.network;
import com.mingliqiye.utils.string.StringUtil; import com.mingliqiye.utils.string.StringUtil;
import lombok.Getter;
import java.io.Serializable; import java.io.Serializable;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import lombok.Getter;
/** /**
* IP和端口聚集类用于封装网络地址与端口信息 * IP和端口聚集类用于封装网络地址与端口信息
@ -15,128 +14,128 @@ import java.net.InetSocketAddress;
*/ */
public class NetworkEndpoint implements Serializable { public class NetworkEndpoint implements Serializable {
@Getter @Getter
private final NetworkAddress networkAddress; private final NetworkAddress networkAddress;
@Getter @Getter
private final NetworkPort networkPort; private final NetworkPort networkPort;
/** /**
* 构造函数使用指定的网络地址和端口创建NetworkEndpoint实例 * 构造函数使用指定的网络地址和端口创建NetworkEndpoint实例
* *
* @param networkAddress 网络地址对象 * @param networkAddress 网络地址对象
* @param networkPort 网络端口对象 * @param networkPort 网络端口对象
* @see NetworkAddress * @see NetworkAddress
* @see NetworkPort * @see NetworkPort
*/ */
private NetworkEndpoint( private NetworkEndpoint(
NetworkAddress networkAddress, NetworkAddress networkAddress,
NetworkPort networkPort NetworkPort networkPort
) { ) {
this.networkAddress = networkAddress; this.networkAddress = networkAddress;
this.networkPort = networkPort; this.networkPort = networkPort;
} }
/** /**
* 根据给定的InetSocketAddress对象创建NetworkEndpoint实例 * 根据给定的InetSocketAddress对象创建NetworkEndpoint实例
* *
* @param address InetSocketAddress对象 * @param address InetSocketAddress对象
* @return 新建的NetworkEndpoint实例 * @return 新建的NetworkEndpoint实例
* @see InetSocketAddress * @see InetSocketAddress
*/ */
public static NetworkEndpoint of(InetSocketAddress address) { public static NetworkEndpoint of(InetSocketAddress address) {
return new NetworkEndpoint( return new NetworkEndpoint(
new NetworkAddress(address.getHostString()), new NetworkAddress(address.getHostString()),
new NetworkPort(address.getPort()) new NetworkPort(address.getPort())
); );
} }
/** /**
* 根据主机名或IP字符串和端口号创建NetworkEndpoint实例 * 根据主机名或IP字符串和端口号创建NetworkEndpoint实例
* *
* @param s 主机名或IP地址字符串 * @param s 主机名或IP地址字符串
* @param i 端口号 * @param i 端口号
* @return 新建的NetworkEndpoint实例 * @return 新建的NetworkEndpoint实例
*/ */
public static NetworkEndpoint of(String s, Integer i) { public static NetworkEndpoint of(String s, Integer i) {
NetworkAddress networkAddress = new NetworkAddress(s); NetworkAddress networkAddress = new NetworkAddress(s);
NetworkPort networkPort = new NetworkPort(i); NetworkPort networkPort = new NetworkPort(i);
return new NetworkEndpoint(networkAddress, networkPort); return new NetworkEndpoint(networkAddress, networkPort);
} }
/** /**
* 根据"host:port"格式的字符串创建NetworkEndpoint实例 * 根据"host:port"格式的字符串创建NetworkEndpoint实例
* 例如"127.0.0.1:8080" * 例如"127.0.0.1:8080"
* *
* @param s "host:port"格式的字符串 * @param s "host:port"格式的字符串
* @return 新建的NetworkEndpoint实例 * @return 新建的NetworkEndpoint实例
*/ */
public static NetworkEndpoint of(String s) { public static NetworkEndpoint of(String s) {
// 查找最后一个冒号的位置以支持IPv6地址中的冒号 // 查找最后一个冒号的位置以支持IPv6地址中的冒号
int lastColonIndex = s.lastIndexOf(':'); int lastColonIndex = s.lastIndexOf(':');
return of( return of(
s.substring(0, lastColonIndex), s.substring(0, lastColonIndex),
Integer.parseInt(s.substring(lastColonIndex + 1)) Integer.parseInt(s.substring(lastColonIndex + 1))
); );
} }
/** /**
* 将当前NetworkEndpoint转换为InetSocketAddress对象 * 将当前NetworkEndpoint转换为InetSocketAddress对象
* *
* @return 对应的InetSocketAddress对象 * @return 对应的InetSocketAddress对象
* @see InetSocketAddress * @see InetSocketAddress
*/ */
public InetSocketAddress toInetSocketAddress() { public InetSocketAddress toInetSocketAddress() {
return new InetSocketAddress( return new InetSocketAddress(
networkAddress.toInetAddress(), networkAddress.toInetAddress(),
networkPort.getPort() networkPort.getPort()
); );
} }
/** /**
* 将当前NetworkEndpoint转换为"host:port"格式的字符串 * 将当前NetworkEndpoint转换为"host:port"格式的字符串
* 例如"127.0.0.1:25563" * 例如"127.0.0.1:25563"
* *
* @return 格式化后的字符串 * @return 格式化后的字符串
*/ */
public String toHostPortString() { public String toHostPortString() {
return StringUtil.format( return StringUtil.format(
"{}:{}", "{}:{}",
networkAddress.getIp(), networkAddress.getIp(),
networkPort.getPort() networkPort.getPort()
); );
} }
/** /**
* 返回NetworkEndpoint的详细字符串表示形式 * 返回NetworkEndpoint的详细字符串表示形式
* 格式NetworkEndpoint(IP=...,Port=...,Endpoint=...) * 格式NetworkEndpoint(IP=...,Port=...,Endpoint=...)
* *
* @return 包含详细信息的字符串 * @return 包含详细信息的字符串
*/ */
public String toString() { public String toString() {
return StringUtil.format( return StringUtil.format(
"NetworkEndpoint(IP={},Port={},Endpoint={})", "NetworkEndpoint(IP={},Port={},Endpoint={})",
networkAddress.getIp(), networkAddress.getIp(),
networkPort.getPort(), networkPort.getPort(),
toHostPortString() toHostPortString()
); );
} }
/** /**
* 获取主机名或IP地址字符串 * 获取主机名或IP地址字符串
* *
* @return 主机名或IP地址 * @return 主机名或IP地址
*/ */
public String getHost() { public String getHost() {
return networkAddress.getIp(); return networkAddress.getIp();
} }
/** /**
* 获取端口号 * 获取端口号
* *
* @return 端口号 * @return 端口号
*/ */
public Integer getPort() { public Integer getPort() {
return networkPort.getPort(); return networkPort.getPort();
} }
} }

View File

@ -7,21 +7,21 @@ package com.mingliqiye.utils.network;
*/ */
public class NetworkException extends RuntimeException { public class NetworkException extends RuntimeException {
/** /**
* 构造一个带有指定详细消息的网络异常 * 构造一个带有指定详细消息的网络异常
* *
* @param message 异常的详细消息 * @param message 异常的详细消息
*/ */
public NetworkException(String message) { public NetworkException(String message) {
super(message); super(message);
} }
/** /**
* 构造一个网络异常指定原因异常 * 构造一个网络异常指定原因异常
* *
* @param e 导致此异常的原因异常 * @param e 导致此异常的原因异常
*/ */
public NetworkException(Exception e) { public NetworkException(Exception e) {
super(e); super(e);
} }
} }

View File

@ -1,9 +1,8 @@
package com.mingliqiye.utils.network; package com.mingliqiye.utils.network;
import com.mingliqiye.utils.string.StringUtil; import com.mingliqiye.utils.string.StringUtil;
import lombok.Getter;
import java.io.Serializable; import java.io.Serializable;
import lombok.Getter;
/** /**
* 网络端口类 * 网络端口类
@ -12,31 +11,31 @@ import java.io.Serializable;
*/ */
public class NetworkPort implements Serializable { public class NetworkPort implements Serializable {
@Getter @Getter
private final int port; private final int port;
/** /**
* 构造函数创建一个网络端口对象 * 构造函数创建一个网络端口对象
* *
* @param port 端口号必须在0-65535范围内 * @param port 端口号必须在0-65535范围内
*/ */
public NetworkPort(int port) { public NetworkPort(int port) {
testPort(port); testPort(port);
this.port = port; this.port = port;
} }
/** /**
* 验证端口号是否合法 * 验证端口号是否合法
* *
* @param port 待验证的端口号 * @param port 待验证的端口号
* @throws NetworkException 当端口号不在合法范围(0-65535)内时抛出异常 * @throws NetworkException 当端口号不在合法范围(0-65535)内时抛出异常
*/ */
public static void testPort(int port) { public static void testPort(int port) {
// 验证端口号范围是否在0-65535之间 // 验证端口号范围是否在0-65535之间
if (!(0 <= port && 65535 >= port)) { if (!(0 <= port && 65535 >= port)) {
throw new NetworkException( throw new NetworkException(
StringUtil.format("{} 不是正确的端口号", port) StringUtil.format("{} 不是正确的端口号", port)
); );
} }
} }
} }

View File

@ -1,7 +1,6 @@
package com.mingliqiye.utils.string; package com.mingliqiye.utils.string;
import com.mingliqiye.utils.collection.Lists; import com.mingliqiye.utils.collection.Lists;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -12,203 +11,203 @@ import java.util.List;
*/ */
public class StringUtil { public class StringUtil {
/** /**
* 将对象转换为字符串表示形式 * 将对象转换为字符串表示形式
* *
* @param obj 需要转换的对象可以为null * @param obj 需要转换的对象可以为null
* @return 如果对象为null则返回空字符串否则返回对象的字符串表示 * @return 如果对象为null则返回空字符串否则返回对象的字符串表示
*/ */
public static String toString(Object obj) { public static String toString(Object obj) {
// 如果对象为null返回空字符串否则调用对象的toString方法 // 如果对象为null返回空字符串否则调用对象的toString方法
return obj == null ? "" : obj.toString(); return obj == null ? "" : obj.toString();
} }
/** /**
* 格式化字符串将格式字符串中的占位符{}替换为对应的参数值<br> * 格式化字符串将格式字符串中的占位符{}替换为对应的参数值<br>
* 示例输出 {} StringUtil.format("{},{},{}", "666", "{}", "777") - "666,{},777"<br> * 示例输出 {} StringUtil.format("{},{},{}", "666", "{}", "777") - "666,{},777"<br>
* 示例 StringUtil.format("{},{},{},{}", "666", "{}", "777") - "666,{},777,"<br> * 示例 StringUtil.format("{},{},{},{}", "666", "{}", "777") - "666,{},777,"<br>
* 没有实际{} 会替换为 "" 空字符串 * 没有实际{} 会替换为 "" 空字符串
* *
* @param format 格式字符串使用{}作为占位符如果为null则返回null * @param format 格式字符串使用{}作为占位符如果为null则返回null
* @param args 用于替换占位符的参数数组 * @param args 用于替换占位符的参数数组
* @return 格式化后的字符串 * @return 格式化后的字符串
*/ */
public static String format(String format, Object... args) { public static String format(String format, Object... args) {
if (format == null) { if (format == null) {
return null; return null;
} }
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
int placeholderCount = 0; int placeholderCount = 0;
int lastIndex = 0; int lastIndex = 0;
int len = format.length(); int len = format.length();
for (int i = 0; i < len - 1; i++) { for (int i = 0; i < len - 1; i++) {
if (format.charAt(i) == '{' && format.charAt(i + 1) == '}') { if (format.charAt(i) == '{' && format.charAt(i + 1) == '}') {
// 添加前面的部分 // 添加前面的部分
sb.append(format, lastIndex, i); sb.append(format, lastIndex, i);
// 替换为 MessageFormat 占位符 {index} // 替换为 MessageFormat 占位符 {index}
sb.append('{').append(placeholderCount).append('}'); sb.append('{').append(placeholderCount).append('}');
placeholderCount++; placeholderCount++;
i++; // 跳过 '}' i++; // 跳过 '}'
lastIndex = i + 1; lastIndex = i + 1;
} }
} }
// 添加剩余部分 // 添加剩余部分
sb.append(format.substring(lastIndex)); sb.append(format.substring(lastIndex));
// 构造实际参数数组 // 构造实际参数数组
Object[] actualArgs; Object[] actualArgs;
if (args.length < placeholderCount) { if (args.length < placeholderCount) {
actualArgs = new String[placeholderCount]; actualArgs = new String[placeholderCount];
System.arraycopy(args, 0, actualArgs, 0, args.length); System.arraycopy(args, 0, actualArgs, 0, args.length);
for (int i = args.length; i < placeholderCount; i++) { for (int i = args.length; i < placeholderCount; i++) {
actualArgs[i] = ""; actualArgs[i] = "";
} }
} else { } else {
actualArgs = args; actualArgs = args;
} }
// 如果没有占位符直接返回格式化后的字符串 // 如果没有占位符直接返回格式化后的字符串
if (placeholderCount == 0) { if (placeholderCount == 0) {
return sb.toString(); return sb.toString();
} }
try { try {
return MessageFormat.format( return MessageFormat.format(
sb.toString(), sb.toString(),
(Object[]) Lists.toStringList(actualArgs) (Object[]) Lists.toStringList(actualArgs)
); );
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
// 返回原始格式化字符串或抛出自定义异常视业务需求而定 // 返回原始格式化字符串或抛出自定义异常视业务需求而定
return sb.toString(); return sb.toString();
} }
} }
/** /**
* 判断字符串是否为空 * 判断字符串是否为空
* *
* @param str 待检查的字符串 * @param str 待检查的字符串
* @return 如果字符串为null或空字符串则返回true否则返回false * @return 如果字符串为null或空字符串则返回true否则返回false
*/ */
public static boolean isEmpty(String str) { public static boolean isEmpty(String str) {
return str == null || str.isEmpty(); return str == null || str.isEmpty();
} }
/** /**
* 使用指定的分隔符将多个对象连接成一个字符串 * 使用指定的分隔符将多个对象连接成一个字符串
* *
* @param spec 用作分隔符的字符串 * @param spec 用作分隔符的字符串
* @param objects 要连接的对象数组 * @param objects 要连接的对象数组
* @return 使用指定分隔符连接后的字符串 * @return 使用指定分隔符连接后的字符串
*/ */
public static String joinOf(String spec, String... objects) { public static String joinOf(String spec, String... objects) {
return join(spec, Arrays.asList(objects)); return join(spec, Arrays.asList(objects));
} }
/** /**
* 将字符串按照指定分隔符分割成字符串列表并移除列表开头的空字符串元素 * 将字符串按照指定分隔符分割成字符串列表并移除列表开头的空字符串元素
* *
* @param str 待分割的字符串 * @param str 待分割的字符串
* @param separator 用作分割符的字符串 * @param separator 用作分割符的字符串
* @return 分割后的字符串列表不包含开头的空字符串元素 * @return 分割后的字符串列表不包含开头的空字符串元素
*/ */
public static List<String> split(String str, String separator) { public static List<String> split(String str, String separator) {
List<String> data = new ArrayList<>( List<String> data = new ArrayList<>(
Arrays.asList(str.split(separator)) Arrays.asList(str.split(separator))
); );
// 移除列表开头的所有空字符串元素 // 移除列表开头的所有空字符串元素
while (!data.isEmpty() && data.get(0).isEmpty()) { while (!data.isEmpty() && data.get(0).isEmpty()) {
data.remove(0); data.remove(0);
} }
return data; return data;
} }
/** /**
* 将列表中的元素按照指定分隔符连接成字符串 * 将列表中的元素按照指定分隔符连接成字符串
* *
* @param <P> 列表元素的类型 * @param <P> 列表元素的类型
* @param separator 分隔符用于连接各个元素 * @param separator 分隔符用于连接各个元素
* @param list 待连接的元素列表 * @param list 待连接的元素列表
* @param fun 转换函数用于将列表元素转换为字符串如果为null则使用toString()方法 * @param fun 转换函数用于将列表元素转换为字符串如果为null则使用toString()方法
* @return 连接后的字符串如果列表为空或null则返回空字符串 * @return 连接后的字符串如果列表为空或null则返回空字符串
*/ */
public static <P> String join( public static <P> String join(
String separator, String separator,
List<P> list, List<P> list,
PRFunction<P, String> fun PRFunction<P, String> fun
) { ) {
// 处理空列表情况 // 处理空列表情况
if (list == null || list.isEmpty()) { if (list == null || list.isEmpty()) {
return ""; return "";
} }
// 构建结果字符串 // 构建结果字符串
StringBuilder sb = StringUtil.stringBuilder(list.size() * 16); StringBuilder sb = StringUtil.stringBuilder(list.size() * 16);
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
P item = list.get(i); P item = list.get(i);
// 将元素转换为字符串 // 将元素转换为字符串
String itemStr = fun == null String itemStr = fun == null
? (item == null ? "null" : item.toString()) ? (item == null ? "null" : item.toString())
: fun.call(item); : fun.call(item);
// 第一个元素直接添加其他元素先添加分隔符再添加元素 // 第一个元素直接添加其他元素先添加分隔符再添加元素
if (i == 0) { if (i == 0) {
sb.append(itemStr); sb.append(itemStr);
} else { } else {
sb.append(separator).append(itemStr); sb.append(separator).append(itemStr);
} }
} }
return sb.toString(); return sb.toString();
} }
/** /**
* 使用指定分隔符连接字符串列表 * 使用指定分隔符连接字符串列表
* *
* @param separator 分隔符不能为null * @param separator 分隔符不能为null
* @param list 字符串列表不能为null * @param list 字符串列表不能为null
* @return 连接后的字符串 * @return 连接后的字符串
* @throws IllegalArgumentException 当separator或list为null时抛出 * @throws IllegalArgumentException 当separator或list为null时抛出
*/ */
public static String join(String separator, List<String> list) { public static String join(String separator, List<String> list) {
if (separator == null) { if (separator == null) {
throw new IllegalArgumentException("Separator cannot be null"); throw new IllegalArgumentException("Separator cannot be null");
} }
if (list == null) { if (list == null) {
throw new IllegalArgumentException("List cannot be null"); throw new IllegalArgumentException("List cannot be null");
} }
return join(separator, list, null); return join(separator, list, null);
} }
/** /**
* 创建一个新的StringBuilder实例 * 创建一个新的StringBuilder实例
* *
* @param i 指定StringBuilder的初始容量 * @param i 指定StringBuilder的初始容量
* @return 返回一个新的StringBuilder对象其初始容量为指定的大小 * @return 返回一个新的StringBuilder对象其初始容量为指定的大小
*/ */
public static StringBuilder stringBuilder(int i) { public static StringBuilder stringBuilder(int i) {
return new StringBuilder(i); return new StringBuilder(i);
} }
/** /**
* PRFunction接口表示一个接收参数P并返回结果R的函数式接口 * PRFunction接口表示一个接收参数P并返回结果R的函数式接口
* <p> * <p>
* 该接口使用@FunctionalInterface注解标记表明它是一个函数式接口 * 该接口使用@FunctionalInterface注解标记表明它是一个函数式接口
* 只包含一个抽象方法call可用于Lambda表达式和方法引用 * 只包含一个抽象方法call可用于Lambda表达式和方法引用
* </p> * </p>
* *
* @param <P> 函数接收的参数类型 * @param <P> 函数接收的参数类型
* @param <R> 函数返回的结果类型 * @param <R> 函数返回的结果类型
*/ */
@FunctionalInterface @FunctionalInterface
public interface PRFunction<P, R> { public interface PRFunction<P, R> {
/** /**
* 执行函数调用 * 执行函数调用
* *
* @param p 输入参数 * @param p 输入参数
* @return 函数执行结果 * @return 函数执行结果
*/ */
R call(P p); R call(P p);
} }
} }

View File

@ -1,7 +1,6 @@
package com.mingliqiye.utils.system; package com.mingliqiye.utils.system;
import com.mingliqiye.utils.collection.Lists; import com.mingliqiye.utils.collection.Lists;
import java.net.Inet4Address; import java.net.Inet4Address;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.NetworkInterface; import java.net.NetworkInterface;
@ -17,182 +16,182 @@ import java.util.List;
*/ */
public class SystemUtil { public class SystemUtil {
private static final String osName = System.getProperties().getProperty( private static final String osName = System.getProperties().getProperty(
"os.name" "os.name"
); );
/** /**
* 判断当前操作系统是否为Windows系统 * 判断当前操作系统是否为Windows系统
* *
* @return 如果是Windows系统返回true否则返回false * @return 如果是Windows系统返回true否则返回false
*/ */
public static boolean isWindows() { public static boolean isWindows() {
return osName != null && osName.startsWith("Windows"); return osName != null && osName.startsWith("Windows");
} }
/** /**
* 判断当前操作系统是否为Mac系统 * 判断当前操作系统是否为Mac系统
* *
* @return 如果是Mac系统返回true否则返回false * @return 如果是Mac系统返回true否则返回false
*/ */
public static boolean isMac() { public static boolean isMac() {
return osName != null && osName.startsWith("Mac"); return osName != null && osName.startsWith("Mac");
} }
/** /**
* 判断当前操作系统是否为Unix/Linux系统 * 判断当前操作系统是否为Unix/Linux系统
* *
* @return 如果是Unix/Linux系统返回true否则返回false * @return 如果是Unix/Linux系统返回true否则返回false
*/ */
public static boolean isUnix() { public static boolean isUnix() {
if (osName == null) { if (osName == null) {
return false; return false;
} }
return ( return (
osName.startsWith("Linux") || osName.startsWith("Linux") ||
osName.startsWith("AIX") || osName.startsWith("AIX") ||
osName.startsWith("SunOS") osName.startsWith("SunOS")
); );
} }
/** /**
* 获取JDK版本号 * 获取JDK版本号
* *
* @return JDK版本号字符串 * @return JDK版本号字符串
*/ */
public static String getJdkVersion() { public static String getJdkVersion() {
return System.getProperty("java.specification.version"); return System.getProperty("java.specification.version");
} }
/** /**
* 获取Java版本号的整数形式 * 获取Java版本号的整数形式
* *
* @return Java版本号的整数形式81117等 * @return Java版本号的整数形式81117等
*/ */
public static Integer getJavaVersionAsInteger() { public static Integer getJavaVersionAsInteger() {
String version = getJdkVersion(); String version = getJdkVersion();
if (version == null || version.isEmpty()) { if (version == null || version.isEmpty()) {
throw new IllegalStateException( throw new IllegalStateException(
"Unable to determine Java version from property 'java.specification.version'" "Unable to determine Java version from property 'java.specification.version'"
); );
} }
String uversion; String uversion;
if (version.startsWith("1.")) { if (version.startsWith("1.")) {
if (version.length() < 3) { if (version.length() < 3) {
throw new IllegalStateException( throw new IllegalStateException(
"Invalid Java version format: " + version "Invalid Java version format: " + version
); );
} }
uversion = version.substring(2, 3); uversion = version.substring(2, 3);
} else { } else {
if (version.length() < 2) { if (version.length() < 2) {
throw new IllegalStateException( throw new IllegalStateException(
"Invalid Java version format: " + version "Invalid Java version format: " + version
); );
} }
uversion = version.substring(0, 2); uversion = version.substring(0, 2);
} }
return Integer.parseInt(uversion); return Integer.parseInt(uversion);
} }
/** /**
* 判断当前JDK版本是否大于8 * 判断当前JDK版本是否大于8
* *
* @return 如果JDK版本大于8返回true否则返回false * @return 如果JDK版本大于8返回true否则返回false
*/ */
public static boolean isJdk8Plus() { public static boolean isJdk8Plus() {
return getJavaVersionAsInteger() > 8; return getJavaVersionAsInteger() > 8;
} }
/** /**
* 获取本地IP地址数组 * 获取本地IP地址数组
* *
* @return 本地IP地址字符串数组 * @return 本地IP地址字符串数组
* @throws RuntimeException 当获取网络接口信息失败时抛出 * @throws RuntimeException 当获取网络接口信息失败时抛出
*/ */
public static String[] getLocalIps() { public static String[] getLocalIps() {
try { try {
List<String> ipList = new ArrayList<>(); List<String> ipList = new ArrayList<>();
Enumeration<NetworkInterface> interfaces = Enumeration<NetworkInterface> interfaces =
NetworkInterface.getNetworkInterfaces(); NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) { while (interfaces.hasMoreElements()) {
NetworkInterface networkInterface = interfaces.nextElement(); NetworkInterface networkInterface = interfaces.nextElement();
// 跳过回环接口和虚拟接口 // 跳过回环接口和虚拟接口
if ( if (
networkInterface.isLoopback() || networkInterface.isLoopback() ||
networkInterface.isVirtual() || networkInterface.isVirtual() ||
!networkInterface.isUp() !networkInterface.isUp()
) { ) {
continue; continue;
} }
Enumeration<InetAddress> addresses = Enumeration<InetAddress> addresses =
networkInterface.getInetAddresses(); networkInterface.getInetAddresses();
while (addresses.hasMoreElements()) { while (addresses.hasMoreElements()) {
InetAddress address = addresses.nextElement(); InetAddress address = addresses.nextElement();
// 只获取IPv4地址 // 只获取IPv4地址
if (address instanceof Inet4Address) { if (address instanceof Inet4Address) {
ipList.add(address.getHostAddress()); ipList.add(address.getHostAddress());
} }
} }
} }
return ipList.toArray(new String[0]); return ipList.toArray(new String[0]);
} catch (SocketException e) { } catch (SocketException e) {
throw new RuntimeException("Failed to get local IP addresses", e); throw new RuntimeException("Failed to get local IP addresses", e);
} }
} }
/** /**
* 获取本地IP地址列表 * 获取本地IP地址列表
* *
* @return 本地IP地址的字符串列表 * @return 本地IP地址的字符串列表
*/ */
public static List<String> getLocalIpsByList() { public static List<String> getLocalIpsByList() {
return Lists.newArrayList(getLocalIps()); return Lists.newArrayList(getLocalIps());
} }
/** /**
* 获取本地回环地址 * 获取本地回环地址
* *
* @return 回环地址字符串通常为"127.0.0.1" * @return 回环地址字符串通常为"127.0.0.1"
*/ */
public static String[] getLoopbackIps() { public static String[] getLoopbackIps() {
List<String> strings = new ArrayList<>(3); List<String> strings = new ArrayList<>(3);
try { try {
Enumeration<NetworkInterface> interfaces = Enumeration<NetworkInterface> interfaces =
NetworkInterface.getNetworkInterfaces(); NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) { while (interfaces.hasMoreElements()) {
NetworkInterface networkInterface = interfaces.nextElement(); NetworkInterface networkInterface = interfaces.nextElement();
// 只处理回环接口 // 只处理回环接口
if (networkInterface.isLoopback() && networkInterface.isUp()) { if (networkInterface.isLoopback() && networkInterface.isUp()) {
Enumeration<InetAddress> addresses = Enumeration<InetAddress> addresses =
networkInterface.getInetAddresses(); networkInterface.getInetAddresses();
while (addresses.hasMoreElements()) { while (addresses.hasMoreElements()) {
InetAddress address = addresses.nextElement(); InetAddress address = addresses.nextElement();
strings.add(address.getHostAddress()); strings.add(address.getHostAddress());
} }
} }
} }
return strings.toArray(new String[0]); return strings.toArray(new String[0]);
} catch (SocketException e) { } catch (SocketException e) {
// 可考虑添加日志记录 // 可考虑添加日志记录
return new String[] { "127.0.0.1" }; return new String[] { "127.0.0.1" };
} }
} }
/** /**
* 获取本地回环地址IP列表 * 获取本地回环地址IP列表
* *
* @return 本地回环地址IP字符串列表的副本 * @return 本地回环地址IP字符串列表的副本
*/ */
public static List<String> getLoopbackIpsByList() { public static List<String> getLoopbackIpsByList() {
// 将本地回环地址IP数组转换为列表并返回 // 将本地回环地址IP数组转换为列表并返回
return Lists.newArrayList(getLoopbackIps()); return Lists.newArrayList(getLoopbackIps());
} }
} }

View File

@ -1,14 +1,13 @@
package com.mingliqiye.utils.time; package com.mingliqiye.utils.time;
import lombok.Getter;
import lombok.Setter;
import lombok.var;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Date; import java.util.Date;
import lombok.Getter;
import lombok.Setter;
import lombok.var;
/** /**
* 时间工具类用于处理日期时间的转换格式化等操作 * 时间工具类用于处理日期时间的转换格式化等操作
@ -19,396 +18,396 @@ import java.util.Date;
*/ */
public final class DateTime { public final class DateTime {
@Getter @Getter
@Setter @Setter
private ZoneId zoneId = ZoneId.systemDefault(); private ZoneId zoneId = ZoneId.systemDefault();
@Getter @Getter
private LocalDateTime localDateTime; private LocalDateTime localDateTime;
/** /**
* 私有构造函数使用指定的 LocalDateTime 初始化实例 * 私有构造函数使用指定的 LocalDateTime 初始化实例
* *
* @param time LocalDateTime 对象 * @param time LocalDateTime 对象
*/ */
private DateTime(LocalDateTime time) { private DateTime(LocalDateTime time) {
setLocalDateTime(time); setLocalDateTime(time);
} }
/** /**
* 私有构造函数使用当前系统时间初始化实例 * 私有构造函数使用当前系统时间初始化实例
*/ */
private DateTime() { private DateTime() {
setLocalDateTime(LocalDateTime.now()); setLocalDateTime(LocalDateTime.now());
} }
/** /**
* 获取当前时间的 DateTime 实例 * 获取当前时间的 DateTime 实例
* *
* @return 返回当前时间的 DateTime 实例 * @return 返回当前时间的 DateTime 实例
*/ */
public static DateTime now() { public static DateTime now() {
return new DateTime(); return new DateTime();
} }
/** /**
* Date 对象转换为 DateTime 实例 * Date 对象转换为 DateTime 实例
* *
* @param zoneId 时区信息 * @param zoneId 时区信息
* @param date Date 对象 * @param date Date 对象
* @return 返回对应的 DateTime 实例 * @return 返回对应的 DateTime 实例
*/ */
public static DateTime of(Date date, ZoneId zoneId) { public static DateTime of(Date date, ZoneId zoneId) {
return new DateTime(date.toInstant().atZone(zoneId).toLocalDateTime()); return new DateTime(date.toInstant().atZone(zoneId).toLocalDateTime());
} }
/** /**
* Date 对象转换为 DateTime 实例使用系统默认时区 * Date 对象转换为 DateTime 实例使用系统默认时区
* *
* @param date Date 对象 * @param date Date 对象
* @return 返回对应的 DateTime 实例 * @return 返回对应的 DateTime 实例
*/ */
public static DateTime of(Date date) { public static DateTime of(Date date) {
return new DateTime( return new DateTime(
date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime() date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()
); );
} }
/** /**
* 解析时间字符串并生成 DateTime 实例 * 解析时间字符串并生成 DateTime 实例
* *
* @param timestr 时间字符串 * @param timestr 时间字符串
* @param formatter 格式化模板 * @param formatter 格式化模板
* @param fillZero 是否补零到模板长度 * @param fillZero 是否补零到模板长度
* @return 返回解析后的 DateTime 实例 * @return 返回解析后的 DateTime 实例
*/ */
public static DateTime parse( public static DateTime parse(
String timestr, String timestr,
String formatter, String formatter,
boolean fillZero boolean fillZero
) { ) {
return new DateTime( return new DateTime(
LocalDateTime.parse( LocalDateTime.parse(
fillZero ? getFillZeroByLen(timestr, formatter) : timestr, fillZero ? getFillZeroByLen(timestr, formatter) : timestr,
DateTimeFormatter.ofPattern(formatter) DateTimeFormatter.ofPattern(formatter)
) )
); );
} }
/** /**
* 使用 Formatter 枚举解析时间字符串并生成 DateTime 实例 * 使用 Formatter 枚举解析时间字符串并生成 DateTime 实例
* *
* @param timestr 时间字符串 * @param timestr 时间字符串
* @param formatter 格式化模板枚举 * @param formatter 格式化模板枚举
* @param fillZero 是否补零到模板长度 * @param fillZero 是否补零到模板长度
* @return 返回解析后的 DateTime 实例 * @return 返回解析后的 DateTime 实例
*/ */
public static DateTime parse( public static DateTime parse(
String timestr, String timestr,
Formatter formatter, Formatter formatter,
boolean fillZero boolean fillZero
) { ) {
return parse(timestr, formatter.getValue(), fillZero); return parse(timestr, formatter.getValue(), fillZero);
} }
/** /**
* 使用 Formatter 枚举解析时间字符串并生成 DateTime 实例默认不补零 * 使用 Formatter 枚举解析时间字符串并生成 DateTime 实例默认不补零
* *
* @param timestr 时间字符串 * @param timestr 时间字符串
* @param formatter 格式化模板枚举 * @param formatter 格式化模板枚举
* @return 返回解析后的 DateTime 实例 * @return 返回解析后的 DateTime 实例
*/ */
public static DateTime parse(String timestr, Formatter formatter) { public static DateTime parse(String timestr, Formatter formatter) {
return parse(timestr, formatter.getValue()); return parse(timestr, formatter.getValue());
} }
/** /**
* 解析时间字符串并生成 DateTime 实例默认不补零 * 解析时间字符串并生成 DateTime 实例默认不补零
* *
* @param timestr 时间字符串 * @param timestr 时间字符串
* @param formatter 格式化模板 * @param formatter 格式化模板
* @return 返回解析后的 DateTime 实例 * @return 返回解析后的 DateTime 实例
*/ */
public static DateTime parse(String timestr, String formatter) { public static DateTime parse(String timestr, String formatter) {
return parse(timestr, formatter, false); return parse(timestr, formatter, false);
} }
/** /**
* 补零处理时间字符串以匹配格式化模板长度 * 补零处理时间字符串以匹配格式化模板长度
* *
* @param dstr 原始时间字符串 * @param dstr 原始时间字符串
* @param formats 格式化模板 * @param formats 格式化模板
* @return 补零后的时间字符串 * @return 补零后的时间字符串
*/ */
private static String getFillZeroByLen(String dstr, String formats) { private static String getFillZeroByLen(String dstr, String formats) {
if (dstr.length() == formats.length()) { if (dstr.length() == formats.length()) {
return dstr; return dstr;
} }
if (formats.length() > dstr.length()) { if (formats.length() > dstr.length()) {
if (dstr.length() == 19) { if (dstr.length() == 19) {
dstr += "."; dstr += ".";
} }
var sb = new StringBuilder(dstr); var sb = new StringBuilder(dstr);
for (int i = 0; i < formats.length() - dstr.length(); i++) { for (int i = 0; i < formats.length() - dstr.length(); i++) {
sb.append("0"); sb.append("0");
} }
return sb.toString(); return sb.toString();
} }
throw new IllegalArgumentException( throw new IllegalArgumentException(
String.format( String.format(
"Text: '%s' len %s < %s %s", "Text: '%s' len %s < %s %s",
dstr, dstr,
dstr.length(), dstr.length(),
formats, formats,
formats.length() formats.length()
) )
); );
} }
/** /**
* 根据年日创建 DateTime 实例 * 根据年日创建 DateTime 实例
* *
* @param year 年份 * @param year 年份
* @param month 月份 (1-12) * @param month 月份 (1-12)
* @param day 日期 (1-31) * @param day 日期 (1-31)
* @return 返回指定日期的 DateTime 实例时间部分为 00:00:00 * @return 返回指定日期的 DateTime 实例时间部分为 00:00:00
*/ */
public static DateTime of(int year, int month, int day) { public static DateTime of(int year, int month, int day) {
return new DateTime(LocalDateTime.of(year, month, day, 0, 0)); return new DateTime(LocalDateTime.of(year, month, day, 0, 0));
} }
/** /**
* 根据年分创建 DateTime 实例 * 根据年分创建 DateTime 实例
* *
* @param year 年份 * @param year 年份
* @param month 月份 (1-12) * @param month 月份 (1-12)
* @param day 日期 (1-31) * @param day 日期 (1-31)
* @param hour 小时 (0-23) * @param hour 小时 (0-23)
* @param minute 分钟 (0-59) * @param minute 分钟 (0-59)
* @return 返回指定日期时间的 DateTime 实例秒部分为 00 * @return 返回指定日期时间的 DateTime 实例秒部分为 00
*/ */
public static DateTime of( public static DateTime of(
int year, int year,
int month, int month,
int day, int day,
int hour, int hour,
int minute int minute
) { ) {
return new DateTime(LocalDateTime.of(year, month, day, hour, minute)); return new DateTime(LocalDateTime.of(year, month, day, hour, minute));
} }
/** /**
* 根据年秒创建 DateTime 实例 * 根据年秒创建 DateTime 实例
* *
* @param year 年份 * @param year 年份
* @param month 月份 (1-12) * @param month 月份 (1-12)
* @param day 日期 (1-31) * @param day 日期 (1-31)
* @param hour 小时 (0-23) * @param hour 小时 (0-23)
* @param minute 分钟 (0-59) * @param minute 分钟 (0-59)
* @param second (0-59) * @param second (0-59)
* @return 返回指定日期时间的 DateTime 实例 * @return 返回指定日期时间的 DateTime 实例
*/ */
public static DateTime of( public static DateTime of(
int year, int year,
int month, int month,
int day, int day,
int hour, int hour,
int minute, int minute,
int second int second
) { ) {
return new DateTime( return new DateTime(
LocalDateTime.of(year, month, day, hour, minute, second) LocalDateTime.of(year, month, day, hour, minute, second)
); );
} }
/** /**
* 根据年纳秒创建 DateTime 实例 * 根据年纳秒创建 DateTime 实例
* *
* @param year 年份 * @param year 年份
* @param month 月份 (1-12) * @param month 月份 (1-12)
* @param day 日期 (1-31) * @param day 日期 (1-31)
* @param hour 小时 (0-23) * @param hour 小时 (0-23)
* @param minute 分钟 (0-59) * @param minute 分钟 (0-59)
* @param second (0-59) * @param second (0-59)
* @param nano 纳秒 (0-999,999,999) * @param nano 纳秒 (0-999,999,999)
* @return 返回指定日期时间的 DateTime 实例 * @return 返回指定日期时间的 DateTime 实例
*/ */
public static DateTime of( public static DateTime of(
int year, int year,
int month, int month,
int day, int day,
int hour, int hour,
int minute, int minute,
int second, int second,
int nano int nano
) { ) {
return new DateTime( return new DateTime(
LocalDateTime.of(year, month, day, hour, minute, second, nano) LocalDateTime.of(year, month, day, hour, minute, second, nano)
); );
} }
/** /**
* 根据毫秒时间戳创建 DateTime 实例 * 根据毫秒时间戳创建 DateTime 实例
* *
* @param epochMilli 毫秒时间戳 * @param epochMilli 毫秒时间戳
* @return 返回对应时间的 DateTime 实例 * @return 返回对应时间的 DateTime 实例
*/ */
public static DateTime of(long epochMilli) { public static DateTime of(long epochMilli) {
return new DateTime( return new DateTime(
Instant.ofEpochMilli(epochMilli) Instant.ofEpochMilli(epochMilli)
.atZone(ZoneId.systemDefault()) .atZone(ZoneId.systemDefault())
.toLocalDateTime() .toLocalDateTime()
); );
} }
/** /**
* 根据毫秒时间戳和时区创建 DateTime 实例 * 根据毫秒时间戳和时区创建 DateTime 实例
* *
* @param epochMilli 毫秒时间戳 * @param epochMilli 毫秒时间戳
* @param zoneId 时区信息 * @param zoneId 时区信息
* @return 返回对应时间的 DateTime 实例 * @return 返回对应时间的 DateTime 实例
*/ */
public static DateTime of(long epochMilli, ZoneId zoneId) { public static DateTime of(long epochMilli, ZoneId zoneId) {
return new DateTime( return new DateTime(
Instant.ofEpochMilli(epochMilli).atZone(zoneId).toLocalDateTime() Instant.ofEpochMilli(epochMilli).atZone(zoneId).toLocalDateTime()
); );
} }
/** /**
* 设置 LocalDateTime 实例 * 设置 LocalDateTime 实例
* *
* @param localDateTime LocalDateTime 对象 * @param localDateTime LocalDateTime 对象
*/ */
public void setLocalDateTime(LocalDateTime localDateTime) { public void setLocalDateTime(LocalDateTime localDateTime) {
this.localDateTime = localDateTime; this.localDateTime = localDateTime;
} }
/** /**
* 将当前 DateTime 转换为 Date 对象 * 将当前 DateTime 转换为 Date 对象
* *
* @return 返回对应的 Date 对象 * @return 返回对应的 Date 对象
*/ */
public Date toDate() { public Date toDate() {
return Date.from(localDateTime.atZone(getZoneId()).toInstant()); return Date.from(localDateTime.atZone(getZoneId()).toInstant());
} }
/** /**
* 获取当前 DateTime 中的 LocalDateTime 实例 * 获取当前 DateTime 中的 LocalDateTime 实例
* *
* @return 返回 LocalDateTime 对象 * @return 返回 LocalDateTime 对象
*/ */
public LocalDateTime toLocalDateTime() { public LocalDateTime toLocalDateTime() {
return localDateTime; return localDateTime;
} }
/** /**
* 在当前时间基础上增加指定的时间偏移量 * 在当前时间基础上增加指定的时间偏移量
* *
* @param dateTimeOffset 时间偏移对象 * @param dateTimeOffset 时间偏移对象
* @return 返回修改后的 DateTime 实例 * @return 返回修改后的 DateTime 实例
*/ */
public DateTime add(DateTimeOffset dateTimeOffset) { public DateTime add(DateTimeOffset dateTimeOffset) {
this.localDateTime = this.localDateTime.plus( this.localDateTime = this.localDateTime.plus(
dateTimeOffset.getOffset(), dateTimeOffset.getOffset(),
dateTimeOffset.getOffsetType() dateTimeOffset.getOffsetType()
); );
return this; return this;
} }
/** /**
* 在当前时间基础上减少指定的时间偏移量 * 在当前时间基础上减少指定的时间偏移量
* *
* @param dateTimeOffset 时间偏移对象 * @param dateTimeOffset 时间偏移对象
* @return 返回修改后的 DateTime 实例 * @return 返回修改后的 DateTime 实例
*/ */
public DateTime sub(DateTimeOffset dateTimeOffset) { public DateTime sub(DateTimeOffset dateTimeOffset) {
this.localDateTime = this.localDateTime.plus( this.localDateTime = this.localDateTime.plus(
-dateTimeOffset.getOffset(), -dateTimeOffset.getOffset(),
dateTimeOffset.getOffsetType() dateTimeOffset.getOffsetType()
); );
return this; return this;
} }
/** /**
* 使用指定格式化模板将当前时间格式化为字符串 * 使用指定格式化模板将当前时间格式化为字符串
* *
* @param formatter 格式化模板 * @param formatter 格式化模板
* @return 返回格式化后的时间字符串 * @return 返回格式化后的时间字符串
*/ */
public String format(String formatter) { public String format(String formatter) {
return format(formatter, false); return format(formatter, false);
} }
/** /**
* 使用 Formatter 枚举将当前时间格式化为字符串 * 使用 Formatter 枚举将当前时间格式化为字符串
* *
* @param formatter 格式化模板枚举 * @param formatter 格式化模板枚举
* @return 返回格式化后的时间字符串 * @return 返回格式化后的时间字符串
*/ */
public String format(Formatter formatter) { public String format(Formatter formatter) {
return format(formatter.getValue()); return format(formatter.getValue());
} }
/** /**
* 使用指定格式化模板将当前时间格式化为字符串并可选择是否去除末尾多余的零 * 使用指定格式化模板将当前时间格式化为字符串并可选择是否去除末尾多余的零
* *
* @param formatter 格式化模板 * @param formatter 格式化模板
* @param repcZero 是否去除末尾多余的零 * @param repcZero 是否去除末尾多余的零
* @return 返回格式化后的时间字符串 * @return 返回格式化后的时间字符串
*/ */
public String format(String formatter, boolean repcZero) { public String format(String formatter, boolean repcZero) {
var formatted = DateTimeFormatter.ofPattern(formatter).format( var formatted = DateTimeFormatter.ofPattern(formatter).format(
toLocalDateTime() toLocalDateTime()
); );
if (repcZero) { if (repcZero) {
// 处理小数点后多余的0 // 处理小数点后多余的0
formatted = formatted.replaceAll("(\\.\\d*?)0+\\b", "$1"); formatted = formatted.replaceAll("(\\.\\d*?)0+\\b", "$1");
formatted = formatted.replaceAll("\\.$", ""); formatted = formatted.replaceAll("\\.$", "");
} }
return formatted; return formatted;
} }
/** /**
* 使用 Formatter 枚举将当前时间格式化为字符串并可选择是否去除末尾多余的零 * 使用 Formatter 枚举将当前时间格式化为字符串并可选择是否去除末尾多余的零
* *
* @param formatter 格式化模板枚举 * @param formatter 格式化模板枚举
* @param repcZero 是否去除末尾多余的零 * @param repcZero 是否去除末尾多余的零
* @return 返回格式化后的时间字符串 * @return 返回格式化后的时间字符串
*/ */
public String format(Formatter formatter, boolean repcZero) { public String format(Formatter formatter, boolean repcZero) {
return format(formatter.getValue(), repcZero); return format(formatter.getValue(), repcZero);
} }
/** /**
* 返回当前时间的标准字符串表示形式 * 返回当前时间的标准字符串表示形式
* *
* @return 返回标准格式的时间字符串 * @return 返回标准格式的时间字符串
*/ */
@Override @Override
public String toString() { public String toString() {
return String.format( return String.format(
"DateTime(%s)", "DateTime(%s)",
format(Formatter.STANDARD_DATETIME_MILLISECOUND7, true) format(Formatter.STANDARD_DATETIME_MILLISECOUND7, true)
); );
} }
/** /**
* 比较当前DateTime对象与指定对象是否相等 * 比较当前DateTime对象与指定对象是否相等
* *
* @param obj 要比较的对象 * @param obj 要比较的对象
* @return 如果对象相等则返回true否则返回false * @return 如果对象相等则返回true否则返回false
*/ */
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
// 检查对象类型是否为DateTime // 检查对象类型是否为DateTime
if (obj instanceof DateTime) { if (obj instanceof DateTime) {
// 比较两个DateTime对象转换为LocalDateTime后的值 // 比较两个DateTime对象转换为LocalDateTime后的值
return toLocalDateTime().equals(((DateTime) obj).toLocalDateTime()); return toLocalDateTime().equals(((DateTime) obj).toLocalDateTime());
} }
return false; return false;
} }
public Instant toInstant() { public Instant toInstant() {
return localDateTime.atZone(zoneId).toInstant(); return localDateTime.atZone(zoneId).toInstant();
} }
} }

View File

@ -11,33 +11,33 @@ import lombok.Getter;
@Getter @Getter
public class DateTimeOffset { public class DateTimeOffset {
private final ChronoUnit offsetType; private final ChronoUnit offsetType;
private final Long offset; private final Long offset;
private DateTimeOffset(ChronoUnit offsetType, Long offset) { private DateTimeOffset(ChronoUnit offsetType, Long offset) {
this.offsetType = offsetType; this.offsetType = offsetType;
this.offset = offset; this.offset = offset;
} }
/** /**
* 创建一个新的DateTimeOffset实例 * 创建一个新的DateTimeOffset实例
* *
* @param offsetType 偏移量的单位类型指定偏移量的计算单位 * @param offsetType 偏移量的单位类型指定偏移量的计算单位
* @param offset 偏移量的数值可以为正数负数或零 * @param offset 偏移量的数值可以为正数负数或零
* @return 返回一个新的DateTimeOffset对象包含指定的偏移量信息 * @return 返回一个新的DateTimeOffset对象包含指定的偏移量信息
*/ */
public static DateTimeOffset of(ChronoUnit offsetType, Long offset) { public static DateTimeOffset of(ChronoUnit offsetType, Long offset) {
return new DateTimeOffset(offsetType, offset); return new DateTimeOffset(offsetType, offset);
} }
/** /**
* 创建一个 DateTimeOffset 实例 * 创建一个 DateTimeOffset 实例
* *
* @param offset 偏移量数值 * @param offset 偏移量数值
* @param offsetType 偏移量的时间单位类型 * @param offsetType 偏移量的时间单位类型
* @return 返回一个新的 DateTimeOffset 实例 * @return 返回一个新的 DateTimeOffset 实例
*/ */
public static DateTimeOffset of(Long offset, ChronoUnit offsetType) { public static DateTimeOffset of(Long offset, ChronoUnit offsetType) {
return new DateTimeOffset(offsetType, offset); return new DateTimeOffset(offsetType, offset);
} }
} }

View File

@ -6,38 +6,38 @@ package com.mingliqiye.utils.time;
* @author MingLiPro * @author MingLiPro
*/ */
public interface DateTimeUnit { public interface DateTimeUnit {
// 时间单位常量 // 时间单位常量
String YEAR = "year"; String YEAR = "year";
String MONTH = "month"; String MONTH = "month";
String WEEK = "week"; String WEEK = "week";
String DAY = "day"; String DAY = "day";
String HOUR = "hour"; String HOUR = "hour";
String MINUTE = "minute"; String MINUTE = "minute";
String SECOND = "second"; String SECOND = "second";
String MILLISECOND = "millisecond"; String MILLISECOND = "millisecond";
String MICROSECOND = "microsecond"; String MICROSECOND = "microsecond";
String NANOSECOND = "nanosecond"; String NANOSECOND = "nanosecond";
// 时间单位缩写 // 时间单位缩写
String YEAR_ABBR = "y"; String YEAR_ABBR = "y";
String MONTH_ABBR = "M"; String MONTH_ABBR = "M";
String WEEK_ABBR = "w"; String WEEK_ABBR = "w";
String DAY_ABBR = "d"; String DAY_ABBR = "d";
String HOUR_ABBR = "h"; String HOUR_ABBR = "h";
String MINUTE_ABBR = "m"; String MINUTE_ABBR = "m";
String SECOND_ABBR = "s"; String SECOND_ABBR = "s";
String MILLISECOND_ABBR = "ms"; String MILLISECOND_ABBR = "ms";
String MICROSECOND_ABBR = "μs"; String MICROSECOND_ABBR = "μs";
String NANOSECOND_ABBR = "ns"; String NANOSECOND_ABBR = "ns";
// 时间单位转换系数毫秒为基准 // 时间单位转换系数毫秒为基准
long MILLIS_PER_SECOND = 1000L; long MILLIS_PER_SECOND = 1000L;
long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;
long MILLIS_PER_WEEK = 7 * MILLIS_PER_DAY; long MILLIS_PER_WEEK = 7 * MILLIS_PER_DAY;
// 月份和年的毫秒数仅为近似值 // 月份和年的毫秒数仅为近似值
long MILLIS_PER_MONTH = 30 * MILLIS_PER_DAY; // 近似值 long MILLIS_PER_MONTH = 30 * MILLIS_PER_DAY; // 近似值
long MILLIS_PER_YEAR = 365 * MILLIS_PER_DAY; // 近似值 long MILLIS_PER_YEAR = 365 * MILLIS_PER_DAY; // 近似值
} }

View File

@ -10,84 +10,84 @@ import lombok.Getter;
* </p> * </p>
*/ */
public enum Formatter { public enum Formatter {
/** /**
* 标准日期时间格式yyyy-MM-dd HH:mm:ss * 标准日期时间格式yyyy-MM-dd HH:mm:ss
*/ */
STANDARD_DATETIME("yyyy-MM-dd HH:mm:ss"), STANDARD_DATETIME("yyyy-MM-dd HH:mm:ss"),
/** /**
* 标准日期时间格式(7位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSSS * 标准日期时间格式(7位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSSS
*/ */
STANDARD_DATETIME_MILLISECOUND7("yyyy-MM-dd HH:mm:ss.SSSSSSS"), STANDARD_DATETIME_MILLISECOUND7("yyyy-MM-dd HH:mm:ss.SSSSSSS"),
/** /**
* 标准日期时间格式(6位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSS * 标准日期时间格式(6位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSS
*/ */
STANDARD_DATETIME_MILLISECOUND6("yyyy-MM-dd HH:mm:ss.SSSSSS"), STANDARD_DATETIME_MILLISECOUND6("yyyy-MM-dd HH:mm:ss.SSSSSS"),
/** /**
* 标准日期时间格式(5位毫秒)yyyy-MM-dd HH:mm:ss.SSSSS * 标准日期时间格式(5位毫秒)yyyy-MM-dd HH:mm:ss.SSSSS
*/ */
STANDARD_DATETIME_MILLISECOUND5("yyyy-MM-dd HH:mm:ss.SSSSS"), STANDARD_DATETIME_MILLISECOUND5("yyyy-MM-dd HH:mm:ss.SSSSS"),
/** /**
* 标准日期时间格式(4位毫秒)yyyy-MM-dd HH:mm:ss.SSSS * 标准日期时间格式(4位毫秒)yyyy-MM-dd HH:mm:ss.SSSS
*/ */
STANDARD_DATETIME_MILLISECOUND4("yyyy-MM-dd HH:mm:ss.SSSS"), STANDARD_DATETIME_MILLISECOUND4("yyyy-MM-dd HH:mm:ss.SSSS"),
/** /**
* 标准日期时间格式(3位毫秒)yyyy-MM-dd HH:mm:ss.SSS * 标准日期时间格式(3位毫秒)yyyy-MM-dd HH:mm:ss.SSS
*/ */
STANDARD_DATETIME_MILLISECOUND3("yyyy-MM-dd HH:mm:ss.SSS"), STANDARD_DATETIME_MILLISECOUND3("yyyy-MM-dd HH:mm:ss.SSS"),
/** /**
* 标准日期时间格式(2位毫秒)yyyy-MM-dd HH:mm:ss.SS * 标准日期时间格式(2位毫秒)yyyy-MM-dd HH:mm:ss.SS
*/ */
STANDARD_DATETIME_MILLISECOUND2("yyyy-MM-dd HH:mm:ss.SS"), STANDARD_DATETIME_MILLISECOUND2("yyyy-MM-dd HH:mm:ss.SS"),
/** /**
* 标准日期时间格式(1位毫秒)yyyy-MM-dd HH:mm:ss.S * 标准日期时间格式(1位毫秒)yyyy-MM-dd HH:mm:ss.S
*/ */
STANDARD_DATETIME_MILLISECOUND1("yyyy-MM-dd HH:mm:ss.S"), STANDARD_DATETIME_MILLISECOUND1("yyyy-MM-dd HH:mm:ss.S"),
/** /**
* 标准ISO格式yyyy-MM-dd'T'HH:mm:ss.SSS'Z' * 标准ISO格式yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
*/ */
STANDARD_ISO("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"), STANDARD_ISO("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),
/** /**
* 标准日期时间秒格式yyyy-MM-dd HH:mm:ss * 标准日期时间秒格式yyyy-MM-dd HH:mm:ss
*/ */
STANDARD_DATETIME_SECOUND("yyyy-MM-dd HH:mm:ss"), STANDARD_DATETIME_SECOUND("yyyy-MM-dd HH:mm:ss"),
/** /**
* 标准日期格式yyyy-MM-dd * 标准日期格式yyyy-MM-dd
*/ */
STANDARD_DATE("yyyy-MM-dd"), STANDARD_DATE("yyyy-MM-dd"),
/** /**
* ISO8601格式yyyy-MM-dd'T'HH:mm:ss.SSS'000' * ISO8601格式yyyy-MM-dd'T'HH:mm:ss.SSS'000'
*/ */
ISO8601("yyyy-MM-dd'T'HH:mm:ss.SSS'000'"), ISO8601("yyyy-MM-dd'T'HH:mm:ss.SSS'000'"),
/** /**
* 紧凑型日期时间格式yyyyMMddHHmmss * 紧凑型日期时间格式yyyyMMddHHmmss
*/ */
COMPACT_DATETIME("yyyyMMddHHmmss"); COMPACT_DATETIME("yyyyMMddHHmmss");
@Getter @Getter
private final String value; private final String value;
@Getter @Getter
private final int len; private final int len;
/** /**
* 构造函数 * 构造函数
* *
* @param value 格式化模式字符串 * @param value 格式化模式字符串
*/ */
Formatter(String value) { Formatter(String value) {
this.value = value; this.value = value;
this.len = value.length(); this.len = value.length();
} }
} }

View File

@ -11,7 +11,6 @@ import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.mingliqiye.utils.time.DateTime; import com.mingliqiye.utils.time.DateTime;
import com.mingliqiye.utils.time.Formatter; import com.mingliqiye.utils.time.Formatter;
import java.io.IOException; import java.io.IOException;
/** /**
@ -21,168 +20,168 @@ import java.io.IOException;
*/ */
public class Jackson { public class Jackson {
/** /**
* yyyy-MM-dd HH:mm:ss.SSSSSSS 的反序列化适配器 * yyyy-MM-dd HH:mm:ss.SSSSSSS 的反序列化适配器
* <p> * <p>
* JSON 字符串按照指定格式解析为 DateTime 对象 * JSON 字符串按照指定格式解析为 DateTime 对象
*/ */
public static class DateTimeJsonDeserializerM7 public static class DateTimeJsonDeserializerM7
extends DateTimeJsonDeserializer { extends DateTimeJsonDeserializer {
/** /**
* 获取当前使用的日期时间格式化器 * 获取当前使用的日期时间格式化器
* *
* @return 返回标准的 7 位毫秒时间格式化器 * @return 返回标准的 7 位毫秒时间格式化器
*/ */
@Override @Override
public Formatter getFormatter() { public Formatter getFormatter() {
return Formatter.STANDARD_DATETIME_MILLISECOUND7; return Formatter.STANDARD_DATETIME_MILLISECOUND7;
} }
} }
/** /**
* 默认日期时间反序列化器 * 默认日期时间反序列化器
* <p> * <p>
* 提供基础的日期时间反序列化功能支持自定义格式化器 * 提供基础的日期时间反序列化功能支持自定义格式化器
*/ */
public static class DateTimeJsonDeserializer public static class DateTimeJsonDeserializer
extends JsonDeserializer<DateTime> { extends JsonDeserializer<DateTime> {
/** /**
* 获取当前使用的日期时间格式化器 * 获取当前使用的日期时间格式化器
* *
* @return 返回标准的日期时间格式化器 * @return 返回标准的日期时间格式化器
*/ */
public Formatter getFormatter() { public Formatter getFormatter() {
return Formatter.STANDARD_DATETIME; return Formatter.STANDARD_DATETIME;
} }
/** /**
* 获取格式化器对应的字符串表达式 * 获取格式化器对应的字符串表达式
* *
* @return 格式化器的字符串值 * @return 格式化器的字符串值
*/ */
public String getFormatterString() { public String getFormatterString() {
return getFormatter().getValue(); return getFormatter().getValue();
} }
/** /**
* 反序列化方法 JSON 解析为 DateTime 对象 * 反序列化方法 JSON 解析为 DateTime 对象
* *
* @param p JSON 解析器对象 * @param p JSON 解析器对象
* @param ctxt 反序列化上下文 * @param ctxt 反序列化上下文
* @return 解析后的 DateTime 对象若输入为 NaN 则返回 null * @return 解析后的 DateTime 对象若输入为 NaN 则返回 null
* @throws IOException 当解析过程中发生 IO 异常时抛出 * @throws IOException 当解析过程中发生 IO 异常时抛出
*/ */
@Override @Override
public DateTime deserialize(JsonParser p, DeserializationContext ctxt) public DateTime deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException { throws IOException {
// 如果是 NaN 值则返回 null // 如果是 NaN 值则返回 null
if (p.isNaN()) { if (p.isNaN()) {
return null; return null;
} }
// 使用指定格式将字符串解析为 DateTime 对象 // 使用指定格式将字符串解析为 DateTime 对象
return DateTime.parse( return DateTime.parse(
p.getValueAsString(), p.getValueAsString(),
getFormatterString(), getFormatterString(),
true true
); );
} }
} }
/** /**
* yyyy-MM-dd HH:mm:ss.SSSSSSS 的序列化适配器 * yyyy-MM-dd HH:mm:ss.SSSSSSS 的序列化适配器
* <p> * <p>
* DateTime 对象按指定格式转换为 JSON 字符串 * DateTime 对象按指定格式转换为 JSON 字符串
*/ */
public static class DateTimeJsonSerializerM7 public static class DateTimeJsonSerializerM7
extends DateTimeJsonSerializer { extends DateTimeJsonSerializer {
/** /**
* 获取当前使用的日期时间格式化器 * 获取当前使用的日期时间格式化器
* *
* @return 返回标准的 7 位毫秒时间格式化器 * @return 返回标准的 7 位毫秒时间格式化器
*/ */
@Override @Override
public Formatter getFormatter() { public Formatter getFormatter() {
return Formatter.STANDARD_DATETIME_MILLISECOUND7; return Formatter.STANDARD_DATETIME_MILLISECOUND7;
} }
} }
/** /**
* 默认日期时间序列化器 * 默认日期时间序列化器
* <p> * <p>
* 提供基础的日期时间序列化功能支持自定义格式化器 * 提供基础的日期时间序列化功能支持自定义格式化器
*/ */
public static class DateTimeJsonSerializer public static class DateTimeJsonSerializer
extends JsonSerializer<DateTime> { extends JsonSerializer<DateTime> {
/** /**
* 获取当前使用的日期时间格式化器 * 获取当前使用的日期时间格式化器
* *
* @return 返回标准的日期时间格式化器 * @return 返回标准的日期时间格式化器
*/ */
public Formatter getFormatter() { public Formatter getFormatter() {
return Formatter.STANDARD_DATETIME; return Formatter.STANDARD_DATETIME;
} }
/** /**
* 获取格式化器对应的字符串表达式 * 获取格式化器对应的字符串表达式
* *
* @return 格式化器的字符串值 * @return 格式化器的字符串值
*/ */
public String getFormatterString() { public String getFormatterString() {
return getFormatter().getValue(); return getFormatter().getValue();
} }
/** /**
* 序列化方法 DateTime 对象写入 JSON 生成器 * 序列化方法 DateTime 对象写入 JSON 生成器
* *
* @param value 要序列化的 DateTime 对象 * @param value 要序列化的 DateTime 对象
* @param gen JSON 生成器 * @param gen JSON 生成器
* @param serializers 序列化提供者 * @param serializers 序列化提供者
* @throws IOException 当写入过程中发生 IO 异常时抛出 * @throws IOException 当写入过程中发生 IO 异常时抛出
*/ */
@Override @Override
public void serialize( public void serialize(
DateTime value, DateTime value,
JsonGenerator gen, JsonGenerator gen,
SerializerProvider serializers SerializerProvider serializers
) throws IOException { ) throws IOException {
// 若值为 null则直接写入 null // 若值为 null则直接写入 null
if (value == null) { if (value == null) {
gen.writeNull(); gen.writeNull();
return; return;
} }
// 按照指定格式将 DateTime 写入为字符串 // 按照指定格式将 DateTime 写入为字符串
gen.writeString(value.format(getFormatterString(), true)); gen.writeString(value.format(getFormatterString(), true));
} }
/** /**
* 带类型信息的序列化方法用于支持多态类型处理 * 带类型信息的序列化方法用于支持多态类型处理
* *
* @param value 要序列化的 DateTime 对象 * @param value 要序列化的 DateTime 对象
* @param gen JSON 生成器 * @param gen JSON 生成器
* @param serializers 序列化提供者 * @param serializers 序列化提供者
* @param typeSer 类型序列化器 * @param typeSer 类型序列化器
* @throws IOException 当写入过程中发生 IO 异常时抛出 * @throws IOException 当写入过程中发生 IO 异常时抛出
*/ */
@Override @Override
public void serializeWithType( public void serializeWithType(
DateTime value, DateTime value,
JsonGenerator gen, JsonGenerator gen,
SerializerProvider serializers, SerializerProvider serializers,
TypeSerializer typeSer TypeSerializer typeSer
) throws IOException { ) throws IOException {
// 写入类型前缀 // 写入类型前缀
WritableTypeId typeId = typeSer.writeTypePrefix( WritableTypeId typeId = typeSer.writeTypePrefix(
gen, gen,
typeSer.typeId(value, JsonToken.VALUE_STRING) typeSer.typeId(value, JsonToken.VALUE_STRING)
); );
// 执行实际序列化 // 执行实际序列化
serialize(value, gen, serializers); serialize(value, gen, serializers);
// 写入类型后缀 // 写入类型后缀
typeSer.writeTypeSuffix(gen, typeId); typeSer.writeTypeSuffix(gen, typeId);
} }
} }
} }

View File

@ -2,13 +2,12 @@ package com.mingliqiye.utils.time.typehandlers;
import com.mingliqiye.utils.time.DateTime; import com.mingliqiye.utils.time.DateTime;
import com.mingliqiye.utils.time.Formatter; import com.mingliqiye.utils.time.Formatter;
import java.sql.*;
import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes; import org.apache.ibatis.type.MappedTypes;
import java.sql.*;
/** /**
* DateTime类型处理器类 * DateTime类型处理器类
* 用于在MyBatis中处理DateTime类型与数据库VARCHAR类型之间的转换 * 用于在MyBatis中处理DateTime类型与数据库VARCHAR类型之间的转换
@ -17,91 +16,91 @@ import java.sql.*;
@MappedJdbcTypes(JdbcType.VARCHAR) @MappedJdbcTypes(JdbcType.VARCHAR)
public class DateTimeTypeHandler extends BaseTypeHandler<DateTime> { public class DateTimeTypeHandler extends BaseTypeHandler<DateTime> {
/** /**
* 设置非空参数值 * 设置非空参数值
* 将DateTime对象转换为Timestamp并设置到PreparedStatement中 * 将DateTime对象转换为Timestamp并设置到PreparedStatement中
* *
* @param ps PreparedStatement对象 * @param ps PreparedStatement对象
* @param i 参数索引位置 * @param i 参数索引位置
* @param parameter DateTime参数值 * @param parameter DateTime参数值
* @param jdbcType JDBC类型 * @param jdbcType JDBC类型
* @throws SQLException SQL异常 * @throws SQLException SQL异常
*/ */
@Override @Override
public void setNonNullParameter( public void setNonNullParameter(
PreparedStatement ps, PreparedStatement ps,
int i, int i,
DateTime parameter, DateTime parameter,
JdbcType jdbcType JdbcType jdbcType
) throws SQLException { ) throws SQLException {
ps.setTimestamp(i, Timestamp.valueOf(parameter.getLocalDateTime())); ps.setTimestamp(i, Timestamp.valueOf(parameter.getLocalDateTime()));
} }
/** /**
* 从ResultSet中获取可为空的结果值 * 从ResultSet中获取可为空的结果值
* 根据列名获取字符串值并解析为DateTime对象 * 根据列名获取字符串值并解析为DateTime对象
* *
* @param rs ResultSet对象 * @param rs ResultSet对象
* @param columnName 列名 * @param columnName 列名
* @return DateTime对象如果值为null则返回null * @return DateTime对象如果值为null则返回null
* @throws SQLException SQL异常 * @throws SQLException SQL异常
*/ */
@Override @Override
public DateTime getNullableResult(ResultSet rs, String columnName) public DateTime getNullableResult(ResultSet rs, String columnName)
throws SQLException { throws SQLException {
return parse(rs.getString(columnName)); return parse(rs.getString(columnName));
} }
/** /**
* 从ResultSet中获取可为空的结果值 * 从ResultSet中获取可为空的结果值
* 根据列索引获取字符串值并解析为DateTime对象 * 根据列索引获取字符串值并解析为DateTime对象
* *
* @param rs ResultSet对象 * @param rs ResultSet对象
* @param columnIndex 列索引 * @param columnIndex 列索引
* @return DateTime对象如果值为null则返回null * @return DateTime对象如果值为null则返回null
* @throws SQLException SQL异常 * @throws SQLException SQL异常
*/ */
@Override @Override
public DateTime getNullableResult(ResultSet rs, int columnIndex) public DateTime getNullableResult(ResultSet rs, int columnIndex)
throws SQLException { throws SQLException {
return parse(rs.getString(columnIndex)); return parse(rs.getString(columnIndex));
} }
/** /**
* 从CallableStatement中获取可为空的结果值 * 从CallableStatement中获取可为空的结果值
* 根据列索引获取字符串值并解析为DateTime对象 * 根据列索引获取字符串值并解析为DateTime对象
* *
* @param cs CallableStatement对象 * @param cs CallableStatement对象
* @param columnIndex 列索引 * @param columnIndex 列索引
* @return DateTime对象如果值为null则返回null * @return DateTime对象如果值为null则返回null
* @throws SQLException SQL异常 * @throws SQLException SQL异常
*/ */
@Override @Override
public DateTime getNullableResult(CallableStatement cs, int columnIndex) public DateTime getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException { throws SQLException {
return parse(cs.getString(columnIndex)); return parse(cs.getString(columnIndex));
} }
/** /**
* 解析字符串为DateTime对象 * 解析字符串为DateTime对象
* *
* @param s 待解析的字符串 * @param s 待解析的字符串
* @return DateTime对象如果字符串为null则返回null * @return DateTime对象如果字符串为null则返回null
*/ */
public DateTime parse(String s) { public DateTime parse(String s) {
if (s == null) { if (s == null) {
return null; return null;
} }
return DateTime.parse(s, Formatter.STANDARD_DATETIME_MILLISECOUND7); return DateTime.parse(s, Formatter.STANDARD_DATETIME_MILLISECOUND7);
} }
/** /**
* 格式化DateTime对象为字符串 * 格式化DateTime对象为字符串
* *
* @param t DateTime对象 * @param t DateTime对象
* @return 格式化后的字符串 * @return 格式化后的字符串
*/ */
public String format(DateTime t) { public String format(DateTime t) {
return t.format(Formatter.STANDARD_DATETIME_MILLISECOUND7); return t.format(Formatter.STANDARD_DATETIME_MILLISECOUND7);
} }
} }

View File

@ -0,0 +1,66 @@
package com.mingliqiye.utils.uuid;
import lombok.var;
/**
* MySQL UUID格式与标准UUID格式相互转换工具类
* <p>
* MySQL使用不同的字节顺序存储UUID此类提供了在MySQL格式和标准UUID格式之间转换的方法
* @author MingLiPro
*/
public class MysqlUUIDv1 {
/**
* 将MySQL格式的UUID转换为标准UUID格式
*
* @param uuid MySQL格式的UUID字节数组长度必须为16字节
* @return 标准UUID格式的字节数组长度为16字节
*/
public static byte[] mysqlToUuid(byte[] uuid) {
var reuuid = new byte[16];
// 转换时间戳低位部分
reuuid[4] = uuid[0];
reuuid[5] = uuid[1];
reuuid[6] = uuid[2];
reuuid[7] = uuid[3];
// 转换时间戳中位部分
reuuid[2] = uuid[4];
reuuid[3] = uuid[5];
// 转换时间戳高位部分
reuuid[0] = uuid[6];
reuuid[1] = uuid[7];
// 复制时钟序列和节点标识部分
System.arraycopy(uuid, 8, reuuid, 8, 8);
return reuuid;
}
/**
* 将标准UUID格式转换为MySQL格式的UUID
*
* @param uuid 标准UUID格式的字节数组长度必须为16字节
* @return MySQL格式的UUID字节数组长度为16字节
*/
public static byte[] uuidToMysql(byte[] uuid) {
var reuuid = new byte[16];
// 转换时间戳高位部分
reuuid[6] = uuid[0];
reuuid[7] = uuid[1];
// 转换时间戳中位部分
reuuid[4] = uuid[2];
reuuid[5] = uuid[3];
// 转换时间戳低位部分
reuuid[0] = uuid[4];
reuuid[1] = uuid[5];
reuuid[2] = uuid[6];
reuuid[3] = uuid[7];
// 复制时钟序列和节点标识部分
System.arraycopy(uuid, 8, reuuid, 8, 8);
return reuuid;
}
}

View File

@ -4,8 +4,7 @@ import com.github.f4b6a3.uuid.UuidCreator;
import com.mingliqiye.utils.string.StringUtil; import com.mingliqiye.utils.string.StringUtil;
import com.mingliqiye.utils.time.DateTime; import com.mingliqiye.utils.time.DateTime;
import com.mingliqiye.utils.time.DateTimeOffset; import com.mingliqiye.utils.time.DateTimeOffset;
import com.mingliqiye.utils.time.Formatter; import lombok.Data;
import lombok.Setter;
import java.io.Serializable; import java.io.Serializable;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -19,234 +18,237 @@ import java.util.Objects;
* *
* @author MingLiPro * @author MingLiPro
*/ */
@Setter @Data
public class UUID implements Serializable { public class UUID implements Serializable {
/** /**
* 内部封装的 java.util.UUID 实例 * 内部封装的 java.util.UUID 实例
*/ */
private java.util.UUID uuid; private java.util.UUID uuid;
/** /**
* 构造一个由指定高位和低位组成的 UUID * 构造一个由指定高位和低位组成的 UUID
* *
* @param msb 高64位 * @param msb 高64位
* @param lsb 低64位 * @param lsb 低64位
*/ */
public UUID(long msb, long lsb) { public UUID(long msb, long lsb) {
uuid = new java.util.UUID(msb, lsb); uuid = new java.util.UUID(msb, lsb);
} }
/** /**
* 构造一个基于当前时间的时间戳型 UUID版本1 * 构造一个基于当前时间的时间戳型 UUID版本1
*/ */
public UUID() { public UUID() {
uuid = UuidCreator.getTimeBased(); uuid = UuidCreator.getTimeBased();
} }
/** /**
* 使用给定的 java.util.UUID 对象构造一个新的 UUID 实例 * 使用给定的 java.util.UUID 对象构造一个新的 UUID 实例
* *
* @param uuid java.util.UUID 实例 * @param uuid java.util.UUID 实例
*/ */
public UUID(java.util.UUID uuid) { public UUID(java.util.UUID uuid) {
this.uuid = uuid; this.uuid = uuid;
} }
/** /**
* 根据字符串表示的 UUID 构造一个新的 UUID 实例 * 根据字符串表示的 UUID 构造一个新的 UUID 实例
* *
* @param uuid 字符串形式的 UUID * @param uuid 字符串形式的 UUID
*/ */
public UUID(String uuid) { public UUID(String uuid) {
this.uuid = java.util.UUID.fromString(uuid); this.uuid = java.util.UUID.fromString(uuid);
} }
/** /**
* 将字节数组转换为 UUID 实例 * 将字节数组转换为 UUID 实例
* *
* @param bytes 表示 UUID 16 字节数据 * @param bytes 表示 UUID 16 字节数据
* @return 新建的 UUID 实例 * @return 新建的 UUID 实例
*/ */
public static UUID of(byte[] bytes) { public static UUID of(byte[] bytes) {
ByteBuffer bb = ByteBuffer.wrap(bytes); if (bytes == null) {
long msb = bb.getLong(); return null;
long lsb = bb.getLong(); }
return new UUID(msb, lsb); ByteBuffer bb = ByteBuffer.wrap(bytes);
} long msb = bb.getLong();
long lsb = bb.getLong();
return new UUID(msb, lsb);
}
/** /**
* 将字符串解析为 UUID 实例如果解析失败则抛出 UUIDException * 将字符串解析为 UUID 实例如果解析失败则抛出 UUIDException
* *
* @param data UUID 字符串 * @param data UUID 字符串
* @return 解析后的 UUID 实例 * @return 解析后的 UUID 实例
* @throws UUIDException 如果解析失败 * @throws UUIDException 如果解析失败
*/ */
public static UUID ofString(String data) { public static UUID of(String data) {
try { if (data == null) {
java.util.UUID uuid1 = java.util.UUID.fromString(data); return null;
UUID uuid = new UUID(); }
uuid.setUuid(uuid1); try {
return uuid; java.util.UUID uuid1 = java.util.UUID.fromString(data);
} catch (Exception e) { UUID uuid = new UUID();
throw new UUIDException(e.getMessage(), e); uuid.setUuid(uuid1);
} return uuid;
} } catch (Exception e) {
throw new UUIDException(e.getMessage(), e);
}
}
/** /**
* UUID 转换为 16 字节的字节数组 * UUID 转换为 16 字节的字节数组
* *
* @return 表示该 UUID 的字节数组 * @return 表示该 UUID 的字节数组
*/ */
public byte[] toBytes() { public byte[] toBytes() {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]); if (this.uuid == null) {
bb.putLong(uuid.getMostSignificantBits()); return null;
bb.putLong(uuid.getLeastSignificantBits()); }
return bb.array(); ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
} bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return bb.array();
}
/** /**
* 获取内部封装的 java.util.UUID 实例 * 获取内部封装的 java.util.UUID 实例
* *
* @return java.util.UUID 实例 * @return java.util.UUID 实例
*/ */
public java.util.UUID GetUUID() { public java.util.UUID GetUUID() {
return uuid; return uuid;
} }
/** /**
* UUID 转换为字符串表示默认使用小写格式 * UUID 转换为字符串表示默认使用小写格式
* *
* @return UUID 字符串 * @return UUID 字符串
*/ */
public String toUUIDString() { public String toUUIDString() {
return toUUIDString(false); return toUUIDString(false);
} }
/** /**
* UUID 转换为字符串表示并可选择是否使用大写 * UUID 转换为字符串表示并可选择是否使用大写
* *
* @param u 是否使用大写格式 * @param u 是否使用大写格式
* @return UUID 字符串 * @return UUID 字符串
* @throws UUIDException 如果 uuid null * @throws UUIDException 如果 uuid null
*/ */
public String toUUIDString(boolean u) { public String toUUIDString(boolean u) {
if (uuid == null) { if (uuid == null) {
throw new UUIDException("uuid is null : NullPointerException"); throw new UUIDException("uuid is null : NullPointerException");
} }
if (u) { if (u) {
return uuid.toString().toUpperCase(Locale.ROOT); return uuid.toString().toUpperCase(Locale.ROOT);
} }
return uuid.toString(); return uuid.toString();
} }
/** /**
* 计算此 UUID 的哈希码 * 计算此 UUID 的哈希码
* *
* @return 哈希码值 * @return 哈希码值
*/ */
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(uuid); return Objects.hash(uuid);
} }
/** /**
* 判断两个 UUID 是否相等 * 判断两个 UUID 是否相等
* *
* @param o 比较对象 * @param o 比较对象
* @return 如果相等返回 true否则返回 false * @return 如果相等返回 true否则返回 false
*/ */
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
if (o == null || getClass() != o.getClass()) { if (o == null || getClass() != o.getClass()) {
return false; return false;
} }
UUID uuid = (UUID) o; UUID uuid = (UUID) o;
return Objects.equals(this.uuid, uuid.uuid); return Objects.equals(this.uuid, uuid.uuid);
} }
/** /**
* 返回此 UUID 的字符串表示包含版本信息和时间戳如果是版本1 * 返回此 UUID 的字符串表示包含版本信息和时间戳如果是版本1
* *
* @return UUID 的详细字符串表示 * @return UUID 的详细字符串表示
*/ */
@Override @Override
public String toString() { public String toString() {
if (uuid == null) { if (uuid == null) {
return "UUID(null)"; return "UUID(null)";
} }
if (uuid.version() == 1) { return StringUtil.format(
return StringUtil.format( "UUID(uuid={},version={})",
"UUID(uuid={},time={},mac={},version={})", toUUIDString(true),
toUUIDString(true), uuid.version()
getDateTime().format(Formatter.STANDARD_DATETIME), );
extractMACFromUUID(), }
uuid.version()
);
}
return StringUtil.format(
"UUID(uuid={},version={})",
toUUIDString(true),
uuid.version()
);
}
/** /**
* 从时间戳型 UUID 中提取时间戳并转换为 DateTime 对象 * 从时间戳型 UUID 中提取时间戳并转换为 DateTime 对象
* *
* @return 对应的 DateTime 对象如果 uuid null则返回 null * @return 对应的 DateTime 对象如果 uuid null则返回 null
*/ */
public DateTime getDateTime() { public DateTime getDateTime() {
if (uuid == null) { if (uuid == null) {
return null; return null;
} }
return DateTime.of(uuid.timestamp() / 10_000).add( if (uuid.version() != 1) {
DateTimeOffset.of(-141427L, ChronoUnit.DAYS) return null;
); }
} return DateTime.of(uuid.timestamp() / 10_000).add(
DateTimeOffset.of(-141427L, ChronoUnit.DAYS)
);
}
/** /**
* 从时间戳型 UUID 中提取 MAC 地址默认使用冒号分隔符 * 从时间戳型 UUID 中提取 MAC 地址默认使用冒号分隔符
* *
* @return MAC 地址字符串 * @return MAC 地址字符串
* @throws UUIDException 如果 uuid null * @throws UUIDException 如果 uuid null
*/ */
public String extractMACFromUUID() { public String extractMACFromUUID() {
return extractMACFromUUID(null); return extractMACFromUUID(null);
} }
/** /**
* 从时间戳型 UUID 中提取 MAC 地址并允许自定义分隔符 * 从时间戳型 UUID 中提取 MAC 地址并允许自定义分隔符
* *
* @param spec 分隔符字符默认为 ":" * @param spec 分隔符字符默认为 ":"
* @return MAC 地址字符串 * @return MAC 地址字符串
* @throws UUIDException 如果 uuid null * @throws UUIDException 如果 uuid null
*/ */
public String extractMACFromUUID(String spec) { public String extractMACFromUUID(String spec) {
if (uuid == null) { if (uuid == null) {
throw new UUIDException("uuid is null : NullPointerException"); throw new UUIDException("uuid is null : NullPointerException");
} }
if (spec == null) { if (spec == null) {
spec = ":"; spec = ":";
} }
long leastSigBits = uuid.getLeastSignificantBits(); long leastSigBits = uuid.getLeastSignificantBits();
long macLong = leastSigBits & 0xFFFFFFFFFFFFL; long macLong = leastSigBits & 0xFFFFFFFFFFFFL;
byte[] macBytes = new byte[6]; byte[] macBytes = new byte[6];
// 提取 MAC 地址的每个字节 // 提取 MAC 地址的每个字节
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
macBytes[5 - i] = (byte) (macLong >> (8 * i)); macBytes[5 - i] = (byte) (macLong >> (8 * i));
} }
StringBuilder mac = new StringBuilder(); StringBuilder mac = new StringBuilder();
// 构造 MAC 地址字符串 // 构造 MAC 地址字符串
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
mac.append(String.format("%02X", macBytes[i])); mac.append(String.format("%02X", macBytes[i]));
if (i < 5) { if (i < 5) {
mac.append(spec); mac.append(spec);
} }
} }
return mac.toString(); return mac.toString();
} }
} }

View File

@ -2,11 +2,11 @@ package com.mingliqiye.utils.uuid;
public class UUIDException extends RuntimeException { public class UUIDException extends RuntimeException {
public UUIDException(String message) { public UUIDException(String message) {
super(message); super(message);
} }
public UUIDException(String message, Throwable cause) { public UUIDException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }
} }

View File

@ -11,7 +11,6 @@ import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.mingliqiye.utils.uuid.UUID; import com.mingliqiye.utils.uuid.UUID;
import com.mingliqiye.utils.uuid.UUIDException; import com.mingliqiye.utils.uuid.UUIDException;
import java.io.IOException; import java.io.IOException;
/** /**
@ -21,89 +20,89 @@ import java.io.IOException;
*/ */
public class Jackson { public class Jackson {
/** /**
* UUID 反序列化器 * UUID 反序列化器
* <p> * <p>
* JSON 字符串反序列化为自定义 UUID 对象 * JSON 字符串反序列化为自定义 UUID 对象
*/ */
public static class UUIDJsonDeserializer extends JsonDeserializer<UUID> { public static class UUIDJsonDeserializer extends JsonDeserializer<UUID> {
/** /**
* 反序列化方法 JSON 解析为 UUID 对象 * 反序列化方法 JSON 解析为 UUID 对象
* *
* @param p JSON 解析器对象 * @param p JSON 解析器对象
* @param ctxt 反序列化上下文 * @param ctxt 反序列化上下文
* @return 解析后的 UUID 对象若输入为 NaN 则返回 null * @return 解析后的 UUID 对象若输入为 NaN 则返回 null
* @throws IOException 当解析过程中发生 IO 异常时抛出 * @throws IOException 当解析过程中发生 IO 异常时抛出
*/ */
@Override @Override
public UUID deserialize(JsonParser p, DeserializationContext ctxt) public UUID deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException { throws IOException {
// 如果是 NaN 值则返回 null // 如果是 NaN 值则返回 null
if (p.isNaN()) { if (p.isNaN()) {
return null; return null;
} }
// 使用指定字符串值创建新的 UUID 对象 // 使用指定字符串值创建新的 UUID 对象
return new UUID(p.getValueAsString()); return new UUID(p.getValueAsString());
} }
} }
/** /**
* UUID 序列化器 * UUID 序列化器
* <p> * <p>
* 将自定义 UUID 对象序列化为 JSON 字符串 * 将自定义 UUID 对象序列化为 JSON 字符串
*/ */
public static class UUIDJsonSerializer extends JsonSerializer<UUID> { public static class UUIDJsonSerializer extends JsonSerializer<UUID> {
/** /**
* 序列化方法 UUID 对象写入 JSON 生成器 * 序列化方法 UUID 对象写入 JSON 生成器
* *
* @param uuid 要序列化的 UUID 对象 * @param uuid 要序列化的 UUID 对象
* @param jsonGenerator JSON 生成器 * @param jsonGenerator JSON 生成器
* @param serializerProvider 序列化提供者 * @param serializerProvider 序列化提供者
* @throws UUIDException UUID 处理过程中发生异常时抛出 * @throws UUIDException UUID 处理过程中发生异常时抛出
* @throws IOException 当写入过程中发生 IO 异常时抛出 * @throws IOException 当写入过程中发生 IO 异常时抛出
*/ */
@Override @Override
public void serialize( public void serialize(
UUID uuid, UUID uuid,
JsonGenerator jsonGenerator, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider SerializerProvider serializerProvider
) throws UUIDException, IOException { ) throws UUIDException, IOException {
// 若值为 null则直接写入 null // 若值为 null则直接写入 null
if (uuid == null) { if (uuid == null) {
jsonGenerator.writeNull(); jsonGenerator.writeNull();
return; return;
} }
// UUID 写入为字符串 // UUID 写入为字符串
jsonGenerator.writeString(uuid.toUUIDString()); jsonGenerator.writeString(uuid.toUUIDString());
} }
/** /**
* 带类型信息的序列化方法用于支持多态类型处理 * 带类型信息的序列化方法用于支持多态类型处理
* *
* @param value 要序列化的 UUID 对象 * @param value 要序列化的 UUID 对象
* @param gen JSON 生成器 * @param gen JSON 生成器
* @param serializers 序列化提供者 * @param serializers 序列化提供者
* @param typeSer 类型序列化器 * @param typeSer 类型序列化器
* @throws IOException 当写入过程中发生 IO 异常时抛出 * @throws IOException 当写入过程中发生 IO 异常时抛出
*/ */
@Override @Override
public void serializeWithType( public void serializeWithType(
UUID value, UUID value,
JsonGenerator gen, JsonGenerator gen,
SerializerProvider serializers, SerializerProvider serializers,
TypeSerializer typeSer TypeSerializer typeSer
) throws IOException { ) throws IOException {
// 写入类型前缀 // 写入类型前缀
WritableTypeId typeId = typeSer.writeTypePrefix( WritableTypeId typeId = typeSer.writeTypePrefix(
gen, gen,
typeSer.typeId(value, JsonToken.VALUE_STRING) typeSer.typeId(value, JsonToken.VALUE_STRING)
); );
// 执行实际序列化 // 执行实际序列化
serialize(value, gen, serializers); serialize(value, gen, serializers);
// 写入类型后缀 // 写入类型后缀
typeSer.writeTypeSuffix(gen, typeId); typeSer.writeTypeSuffix(gen, typeId);
} }
} }
} }

View File

@ -6,7 +6,6 @@ import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes; import org.apache.ibatis.type.MappedTypes;
import java.nio.ByteBuffer;
import java.sql.CallableStatement; import java.sql.CallableStatement;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
@ -22,90 +21,64 @@ import java.sql.SQLException;
@MappedJdbcTypes(JdbcType.BINARY) @MappedJdbcTypes(JdbcType.BINARY)
public class UUIDBinaryTypeHandler extends BaseTypeHandler<UUID> { public class UUIDBinaryTypeHandler extends BaseTypeHandler<UUID> {
/** /**
* UUID 对象转换为二进制字节数组 * 设置非空参数到 PreparedStatement
* *
* @param uuid 要转换的 UUID 对象 * @param ps PreparedStatement 对象
* @return 包含 UUID 数据的 16 字节二进制数组 * @param i 参数在 SQL 语句中的位置索引
*/ * @param parameter 要设置的 UUID 参数值
public static byte[] UUID_TO_BIN(UUID uuid) { * @param jdbcType JDBC 类型信息
ByteBuffer bb = ByteBuffer.wrap(new byte[16]); * @throws SQLException 当数据库操作发生错误时抛出
bb.putLong(uuid.GetUUID().getMostSignificantBits()); */
bb.putLong(uuid.GetUUID().getLeastSignificantBits()); @Override
return bb.array(); public void setNonNullParameter(
} PreparedStatement ps,
int i,
UUID parameter,
JdbcType jdbcType
) throws SQLException {
ps.setBytes(i, UUIDConverter.UUID_TO_BIN(parameter));
}
/** /**
* 将二进制字节数组转换为 UUID 对象 * ResultSet 中根据列名获取可为空的 UUID 结果
* *
* @param bytes 包含 UUID 数据的二进制字节数组 * @param rs ResultSet 对象
* @return 转换后的 UUID 对象如果输入为 null 则返回 null * @param columnName 数据库列名
*/ * @return 转换后的 UUID 对象可能为 null
public static UUID BIN_TO_UUID(byte[] bytes) { * @throws SQLException 当数据库操作发生错误时抛出
if (bytes == null) { */
return null; @Override
} public UUID getNullableResult(ResultSet rs, String columnName)
return UUID.of(bytes); throws SQLException {
} return UUIDConverter.BIN_TO_UUID(rs.getBytes(columnName));
}
/** /**
* 设置非空参数到 PreparedStatement * ResultSet 中根据列索引获取可为空的 UUID 结果
* *
* @param ps PreparedStatement 对象 * @param rs ResultSet 对象
* @param i 参数在 SQL 语句中的位置索引 * @param columnIndex 数据库列索引
* @param parameter 要设置的 UUID 参数值 * @return 转换后的 UUID 对象可能为 null
* @param jdbcType JDBC 类型信息 * @throws SQLException 当数据库操作发生错误时抛出
* @throws SQLException 当数据库操作发生错误时抛出 */
*/ @Override
@Override public UUID getNullableResult(ResultSet rs, int columnIndex)
public void setNonNullParameter( throws SQLException {
PreparedStatement ps, return UUIDConverter.BIN_TO_UUID(rs.getBytes(columnIndex));
int i, }
UUID parameter,
JdbcType jdbcType
) throws SQLException {
ps.setBytes(i, UUID_TO_BIN(parameter));
}
/** /**
* ResultSet 中根据列名获取可为空的 UUID 结果 * CallableStatement 中根据参数索引获取可为空的 UUID 结果
* *
* @param rs ResultSet 对象 * @param cs CallableStatement 对象
* @param columnName 数据库列名 * @param columnIndex 参数索引
* @return 转换后的 UUID 对象可能为 null * @return 转换后的 UUID 对象可能为 null
* @throws SQLException 当数据库操作发生错误时抛出 * @throws SQLException 当数据库操作发生错误时抛出
*/ */
@Override @Override
public UUID getNullableResult(ResultSet rs, String columnName) public UUID getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException { throws SQLException {
return BIN_TO_UUID(rs.getBytes(columnName)); return UUIDConverter.BIN_TO_UUID(cs.getBytes(columnIndex));
} }
/**
* ResultSet 中根据列索引获取可为空的 UUID 结果
*
* @param rs ResultSet 对象
* @param columnIndex 数据库列索引
* @return 转换后的 UUID 对象可能为 null
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public UUID getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return BIN_TO_UUID(rs.getBytes(columnIndex));
}
/**
* CallableStatement 中根据参数索引获取可为空的 UUID 结果
*
* @param cs CallableStatement 对象
* @param columnIndex 参数索引
* @return 转换后的 UUID 对象可能为 null
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public UUID getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return BIN_TO_UUID(cs.getBytes(columnIndex));
}
} }

View File

@ -0,0 +1,31 @@
package com.mingliqiye.utils.uuid.typehandlers;
import com.mingliqiye.utils.uuid.MysqlUUIDv1;
import com.mingliqiye.utils.uuid.UUID;
public class UUIDConverter {
public static String UUID_TO_STR(UUID uuid) {
return uuid.toUUIDString();
}
public static UUID STR_TO_UUID(String string) {
return UUID.of(string);
}
public static byte[] UUID_TO_BIN(UUID uuid) {
return uuid.toBytes();
}
public static UUID BIN_TO_UUID(byte[] bytes) {
return UUID.of(bytes);
}
public static byte[] MYSQL_UUID_TO_BIN(UUID uuid) {
return MysqlUUIDv1.uuidToMysql(uuid.toBytes());
}
public static UUID BIN_TO_MYSQL_UUID(byte[] bytes) {
return UUID.of(MysqlUUIDv1.mysqlToUuid(bytes));
}
}

View File

@ -0,0 +1,83 @@
package com.mingliqiye.utils.uuid.typehandlers;
import com.mingliqiye.utils.uuid.UUID;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
/**
* UUIDBinaryTypeHandler 类用于处理 UUID 类型与数据库 BINARY 类型之间的转换
* 该类继承自 BaseTypeHandler专门处理 UUID 对象的序列化和反序列化
*
* @author MingLiPro
*/
@MappedTypes({ UUID.class })
@MappedJdbcTypes(JdbcType.VARCHAR)
public class UUIDStringTypeHandler extends BaseTypeHandler<UUID> {
/**
* 设置非空参数到 PreparedStatement
*
* @param ps PreparedStatement 对象
* @param i 参数在 SQL 语句中的位置索引
* @param parameter 要设置的 UUID 参数值
* @param jdbcType JDBC 类型信息
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public void setNonNullParameter(
PreparedStatement ps,
int i,
UUID parameter,
JdbcType jdbcType
) throws SQLException {
ps.setString(i, UUIDConverter.UUID_TO_STR(parameter));
}
/**
* ResultSet 中根据列名获取可为空的 UUID 结果
*
* @param rs ResultSet 对象
* @param columnName 数据库列名
* @return 转换后的 UUID 对象可能为 null
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public UUID getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return UUIDConverter.STR_TO_UUID(rs.getString(columnName));
}
/**
* ResultSet 中根据列索引获取可为空的 UUID 结果
*
* @param rs ResultSet 对象
* @param columnIndex 数据库列索引
* @return 转换后的 UUID 对象可能为 null
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public UUID getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return UUIDConverter.STR_TO_UUID(rs.getString(columnIndex));
}
/**
* CallableStatement 中根据参数索引获取可为空的 UUID 结果
*
* @param cs CallableStatement 对象
* @param columnIndex 参数索引
* @return 转换后的 UUID 对象可能为 null
* @throws SQLException 当数据库操作发生错误时抛出
*/
@Override
public UUID getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return UUIDConverter.STR_TO_UUID(cs.getString(columnIndex));
}
}