, String> PRIMITIVE_CLASS_NAME_MAP = new BiMap<>(new HashMap<>(9, 1));
+
+ static {
+ PRIMITIVE_CLASS_DESC_MAP.put(void.class, JVM_VOID);
+ PRIMITIVE_CLASS_NAME_MAP.put(void.class, "void");
+ PRIMITIVE_CLASS_DESC_MAP.put(boolean.class, JVM_BOOLEAN);
+ PRIMITIVE_CLASS_NAME_MAP.put(boolean.class, "boolean");
+ PRIMITIVE_CLASS_DESC_MAP.put(byte.class, JVM_BYTE);
+ PRIMITIVE_CLASS_NAME_MAP.put(byte.class, "byte");
+ PRIMITIVE_CLASS_DESC_MAP.put(char.class, JVM_CHAR);
+ PRIMITIVE_CLASS_NAME_MAP.put(char.class, "char");
+ PRIMITIVE_CLASS_DESC_MAP.put(double.class, JVM_DOUBLE);
+ PRIMITIVE_CLASS_NAME_MAP.put(double.class, "double");
+ PRIMITIVE_CLASS_DESC_MAP.put(float.class, JVM_FLOAT);
+ PRIMITIVE_CLASS_NAME_MAP.put(float.class, "float");
+ PRIMITIVE_CLASS_DESC_MAP.put(int.class, JVM_INT);
+ PRIMITIVE_CLASS_NAME_MAP.put(int.class, "int");
+ PRIMITIVE_CLASS_DESC_MAP.put(long.class, JVM_LONG);
+ PRIMITIVE_CLASS_NAME_MAP.put(long.class, "long");
+ PRIMITIVE_CLASS_DESC_MAP.put(short.class, JVM_SHORT);
+ PRIMITIVE_CLASS_NAME_MAP.put(short.class, "short");
+ }
+
+ /**
+ * Class描述转Class
+ * {@code
+ * "[Z" => boolean[].class
+ * "[[Ljava/util/Map;" => java.util.Map[][].class
+ * }
+ *
+ * @param desc 类描述
+ * @return Class
+ * @throws UtilException 类没有找到
+ */
+ public static Class> descToClass(final String desc) throws UtilException {
+ return descToClass(desc, true, null);
+ }
+
+ /**
+ * Class描述转Class
+ * {@code
+ * "[Z" => boolean[].class
+ * "[[Ljava/util/Map;" => java.util.Map[][].class
+ * }
+ *
+ * @param desc 类描述
+ * @param isInitialized 是否初始化类
+ * @param cl {@link ClassLoader}
+ * @return Class
+ * @throws UtilException 类没有找到
+ */
+ public static Class> descToClass(String desc, final boolean isInitialized, final ClassLoader cl) throws UtilException {
+ final char firstChar = desc.charAt(0);
+ final Class> clazz = PRIMITIVE_CLASS_DESC_MAP.getKey(firstChar);
+ if (null != clazz) {
+ return clazz;
+ }
+ switch (firstChar) {
+ case 'L':
+ // "Ljava/lang/Object;" ==> "java.lang.Object"
+ desc = desc.substring(1, desc.length() - 1).replace('/', '.');
+ break;
+ case '[':
+ // "[[Ljava/lang/Object;" ==> "[[Ljava.lang.Object;"
+ desc = desc.replace(CharUtil.SLASH, CharUtil.DOT);
+ break;
+ default:
+ throw new UtilException("Class not found for : " + desc);
+ }
+
+ return ClassUtil.forName(desc, isInitialized, cl);
+ }
+
+ /**
+ * get class desc.
+ * boolean[].class => "[Z"
+ * Object.class => "Ljava/lang/Object;"
+ *
+ * @param c class.
+ * @return desc.
+ */
+ public static String getDesc(Class> c) {
+ final StringBuilder ret = new StringBuilder();
+
+ while (c.isArray()) {
+ ret.append('[');
+ c = c.getComponentType();
+ }
+
+ if (c.isPrimitive()) {
+ final Character desc = PRIMITIVE_CLASS_DESC_MAP.get(c);
+ if (null != desc) {
+ ret.append(desc.charValue());
+ }
+ } else {
+ ret.append('L');
+ ret.append(c.getName().replace('.', '/'));
+ ret.append(';');
+ }
+ return ret.toString();
+ }
+
+ /**
+ * 获取方法或构造描述
+ * 方法:
+ * {@code
+ * int do(int arg1) => "do(I)I"
+ * void do(String arg1,boolean arg2) => "do(Ljava/lang/String;Z)V"
+ * }
+ * 构造:
+ *
+ * "()V", "(Ljava/lang/String;I)V"
+ *
+ *
+ * @param methodOrConstructor 方法或构造
+ * @return 描述
+ */
+ public static String getDesc(final Executable methodOrConstructor) {
+ final StringBuilder ret = new StringBuilder();
+ if (methodOrConstructor instanceof Method) {
+ ret.append(methodOrConstructor.getName());
+ }
+ ret.append('(');
+
+ // 参数
+ final Class>[] parameterTypes = methodOrConstructor.getParameterTypes();
+ for (final Class> parameterType : parameterTypes) {
+ ret.append(getDesc(parameterType));
+ }
+
+ // 返回类型或构造标记
+ ret.append(')');
+ if (methodOrConstructor instanceof Method) {
+ ret.append(getDesc(((Method) methodOrConstructor).getReturnType()));
+ } else {
+ ret.append('V');
+ }
+
+ return ret.toString();
+ }
+
+ /**
+ * 获取code base
+ *
+ * @param clazz 类
+ * @return code base
+ */
+ public static String getCodeBase(final Class> clazz) {
+ if (clazz == null) {
+ return null;
+ }
+ final ProtectionDomain domain = clazz.getProtectionDomain();
+ if (domain == null) {
+ return null;
+ }
+ final CodeSource source = domain.getCodeSource();
+ if (source == null) {
+ return null;
+ }
+ final URL location = source.getLocation();
+ if (location == null) {
+ return null;
+ }
+ return location.getFile();
+ }
+
+ /**
+ * name to class.
+ * "boolean" => boolean.class
+ * "java.util.Map[][]" => java.util.Map[][].class
+ *
+ * @param name name.
+ * @param isInitialized 是否初始化类
+ * @param cl ClassLoader instance.
+ * @return Class instance.
+ */
+ public static Class> nameToClass(String name, final boolean isInitialized, final ClassLoader cl) {
+ Assert.notNull(name, "Name must not be null");
+ // 去除尾部多余的"."和"/"
+ name = StrUtil.trim(name, StrTrimer.TrimMode.SUFFIX, (c) ->
+ CharUtil.SLASH == c || CharUtil.DOT == c);
+
+ int c = 0;
+ final int index = name.indexOf('[');
+ if (index > 0) {
+ c = (name.length() - index) / 2;
+ name = name.substring(0, index);
+ }
+
+ if (c > 0) {
+ final StringBuilder sb = new StringBuilder();
+ while (c-- > 0) {
+ sb.append('[');
+ }
+
+ final Class> clazz = PRIMITIVE_CLASS_NAME_MAP.getKey(name);
+ if (null != clazz) {
+ // 原始类型数组,根据name获取其描述
+ sb.append(PRIMITIVE_CLASS_DESC_MAP.get(clazz).charValue());
+ } else {
+ // 对象数组
+ // "java.lang.Object" ==> "Ljava.lang.Object;"
+ sb.append('L').append(name).append(';');
+ }
+ name = sb.toString();
+ } else {
+ final Class> clazz = PRIMITIVE_CLASS_NAME_MAP.getKey(name);
+ if (null != clazz) {
+ return clazz;
+ }
+ }
+
+ return ClassUtil.forName(name.replace(CharUtil.SLASH, CharUtil.DOT), isInitialized, cl);
+ }
+
+ /**
+ * 类名称转描述
+ *
+ * {@code
+ * java.util.Map[][] => "[[Ljava/util/Map;"
+ * }
+ *
+ * @param name 名称
+ * @return 描述
+ */
+ public static String nameToDesc(String name) {
+ final StringBuilder sb = new StringBuilder();
+ int c = 0;
+ final int index = name.indexOf('[');
+ if (index > 0) {
+ c = (name.length() - index) / 2;
+ name = name.substring(0, index);
+ }
+ while (c-- > 0) {
+ sb.append('[');
+ }
+
+ final Class> clazz = PRIMITIVE_CLASS_NAME_MAP.getKey(name);
+ if (null != clazz) {
+ // 原始类型数组,根据name获取其描述
+ sb.append(PRIMITIVE_CLASS_DESC_MAP.get(clazz).charValue());
+ } else {
+ // 对象数组
+ // "java.lang.Object" ==> "Ljava.lang.Object;"
+ sb.append('L').append(name.replace(CharUtil.DOT, CharUtil.SLASH)).append(';');
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * 类描述转名称
+ *
+ * {@code
+ * "[[I" => "int[][]"
+ * }
+ *
+ * @param desc 描述
+ * @return 名称
+ */
+ public static String descToName(final String desc) {
+ final StringBuilder sb = new StringBuilder();
+ int c = desc.lastIndexOf('[') + 1;
+ if (desc.length() == c + 1) {
+ final char descChar = desc.charAt(c);
+ final Class> clazz = PRIMITIVE_CLASS_DESC_MAP.getKey(descChar);
+ if (null != clazz) {
+ sb.append(PRIMITIVE_CLASS_NAME_MAP.get(clazz));
+ } else {
+ throw new UtilException("Unsupported primitive desc: {}", desc);
+ }
+ } else {
+ sb.append(desc.substring(c + 1, desc.length() - 1).replace(CharUtil.SLASH, CharUtil.DOT));
+ }
+ while (c-- > 0) {
+ sb.append("[]");
+ }
+ return sb.toString();
+ }
+}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/classloader/ClassLoaderUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/classloader/ClassLoaderUtil.java
index 228a4d8b1..302598b3e 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/classloader/ClassLoaderUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/classloader/ClassLoaderUtil.java
@@ -12,22 +12,11 @@
package org.dromara.hutool.core.classloader;
-import org.dromara.hutool.core.convert.BasicType;
import org.dromara.hutool.core.exceptions.UtilException;
-import org.dromara.hutool.core.lang.Assert;
-import org.dromara.hutool.core.map.SafeConcurrentHashMap;
-import org.dromara.hutool.core.text.CharPool;
-import org.dromara.hutool.core.text.StrTrimer;
-import org.dromara.hutool.core.text.StrUtil;
-import org.dromara.hutool.core.util.CharUtil;
import java.io.File;
-import java.lang.reflect.Array;
import java.security.AccessController;
import java.security.PrivilegedAction;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
/**
* {@link ClassLoader}工具类
@@ -39,48 +28,26 @@ import java.util.Map;
public class ClassLoaderUtil {
/**
- * 数组类的结尾符: "[]"
+ * 获取{@link ClassLoader}
+ * 获取顺序如下:
+ *
+ *
+ * 1、获取当前线程的ContextClassLoader
+ * 2、获取当前类对应的ClassLoader
+ * 3、获取系统ClassLoader({@link ClassLoader#getSystemClassLoader()})
+ *
+ *
+ * @return 类加载器
*/
- private static final String ARRAY_SUFFIX = "[]";
- /**
- * 内部数组类名前缀: "["
- */
- private static final String INTERNAL_ARRAY_PREFIX = "[";
- /**
- * 内部非原始类型类名前缀: "[L"
- */
- private static final String NON_PRIMITIVE_ARRAY_PREFIX = "[L";
- /**
- * 包名分界符: '.'
- */
- private static final char PACKAGE_SEPARATOR = CharUtil.DOT;
- /**
- * 内部类分界符: '$'
- */
- private static final char INNER_CLASS_SEPARATOR = '$';
-
- /**
- * 原始类型名和其class对应表,例如:int =》 int.class
- */
- private static final Map> PRIMITIVE_TYPE_NAME_MAP = new SafeConcurrentHashMap<>(32);
-
- static {
- final List> primitiveTypes = new ArrayList<>(32);
- // 加入原始类型
- primitiveTypes.addAll(BasicType.getPrimitiveSet());
- // 加入原始类型数组类型
- primitiveTypes.add(boolean[].class);
- primitiveTypes.add(byte[].class);
- primitiveTypes.add(char[].class);
- primitiveTypes.add(double[].class);
- primitiveTypes.add(float[].class);
- primitiveTypes.add(int[].class);
- primitiveTypes.add(long[].class);
- primitiveTypes.add(short[].class);
- primitiveTypes.add(void.class);
- for (final Class> primitiveType : primitiveTypes) {
- PRIMITIVE_TYPE_NAME_MAP.put(primitiveType.getName(), primitiveType);
+ public static ClassLoader getClassLoader() {
+ ClassLoader classLoader = getContextClassLoader();
+ if (classLoader == null) {
+ classLoader = ClassLoaderUtil.class.getClassLoader();
+ if (null == classLoader) {
+ classLoader = getSystemClassLoader();
+ }
}
+ return classLoader;
}
/**
@@ -116,32 +83,17 @@ public class ClassLoaderUtil {
}
}
-
/**
- * 获取{@link ClassLoader}
- * 获取顺序如下:
+ * 创建新的{@link JarClassLoader},并使用此Classloader加载目录下的class文件和jar文件
*
- *
- * 1、获取当前线程的ContextClassLoader
- * 2、获取当前类对应的ClassLoader
- * 3、获取系统ClassLoader({@link ClassLoader#getSystemClassLoader()})
- *
- *
- * @return 类加载器
+ * @param jarOrDir jar文件或者包含jar和class文件的目录
+ * @return {@link JarClassLoader}
+ * @since 4.4.2
*/
- public static ClassLoader getClassLoader() {
- ClassLoader classLoader = getContextClassLoader();
- if (classLoader == null) {
- classLoader = ClassLoaderUtil.class.getClassLoader();
- if (null == classLoader) {
- classLoader = getSystemClassLoader();
- }
- }
- return classLoader;
+ public static JarClassLoader getJarClassLoader(final File jarOrDir) {
+ return JarClassLoader.load(jarOrDir);
}
- // ----------------------------------------------------------------------------------- loadClass
-
/**
* 加载类,通过传入类的字符串,返回其对应的类名,使用默认ClassLoader并初始化类(调用static模块内容和初始化static属性)
* 扩展{@link Class#forName(String, boolean, ClassLoader)}方法,支持以下几类类名的加载:
@@ -201,48 +153,8 @@ public class ClassLoaderUtil {
* @throws UtilException 包装{@link ClassNotFoundException},没有类名对应的类时抛出此异常
*/
@SuppressWarnings("unchecked")
- public static Class loadClass(String name, final boolean isInitialized, ClassLoader classLoader) throws UtilException {
- Assert.notNull(name, "Name must not be null");
-
- // 自动将包名中的"/"替换为"."
- name = name.replace(CharPool.SLASH, CharPool.DOT);
- if (null == classLoader) {
- classLoader = getClassLoader();
- }
-
- Class> clazz = loadPrimitiveClass(name);
- if (clazz == null) {
- clazz = doLoadClass(name, isInitialized, classLoader);
- }
- return (Class) clazz;
- }
-
- /**
- * 加载原始类型的类。包括原始类型、原始类型数组和void
- *
- * @param name 原始类型名,比如 int
- * @return 原始类型类
- */
- public static Class> loadPrimitiveClass(String name) {
- Class> result = null;
- if (StrUtil.isNotBlank(name)) {
- name = name.trim();
- if (name.length() <= 8) {
- result = PRIMITIVE_TYPE_NAME_MAP.get(name);
- }
- }
- return result;
- }
-
- /**
- * 创建新的{@link JarClassLoader},并使用此Classloader加载目录下的class文件和jar文件
- *
- * @param jarOrDir jar文件或者包含jar和class文件的目录
- * @return {@link JarClassLoader}
- * @since 4.4.2
- */
- public static JarClassLoader getJarClassLoader(final File jarOrDir) {
- return JarClassLoader.load(jarOrDir);
+ public static Class loadClass(final String name, final boolean isInitialized, final ClassLoader classLoader) throws UtilException {
+ return (Class) ClassDescUtil.nameToClass(name, isInitialized, classLoader);
}
/**
@@ -292,95 +204,4 @@ public class ClassLoaderUtil {
return false;
}
}
-
- // ----------------------------------------------------------------------------------- Private method start
-
- /**
- * 加载非原始类类,无缓存
- *
- * @param name 类名
- * @param isInitialized 是否初始化
- * @param classLoader {@link ClassLoader},必须非空
- * @return 类
- */
- private static Class> doLoadClass(String name, final boolean isInitialized, final ClassLoader classLoader) {
- // 去除尾部多余的"."
- name = StrUtil.trim(name, StrTrimer.TrimMode.SUFFIX, (c) -> CharUtil.DOT == c);
- Class> clazz;
- if (name.endsWith(ARRAY_SUFFIX)) {
- // 对象数组"java.lang.String[]"风格
- final String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
- final Class> elementClass = loadClass(elementClassName, isInitialized, classLoader);
- clazz = Array.newInstance(elementClass, 0).getClass();
- } else if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) {
- // "[Ljava.lang.String;" 风格
- final String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1);
- final Class> elementClass = loadClass(elementName, isInitialized, classLoader);
- clazz = Array.newInstance(elementClass, 0).getClass();
- } else if (name.startsWith(INTERNAL_ARRAY_PREFIX)) {
- // "[[I" 或 "[[Ljava.lang.String;" 风格
- final String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length());
- final Class> elementClass = loadClass(elementName, isInitialized, classLoader);
- clazz = Array.newInstance(elementClass, 0).getClass();
- } else {
- // 加载普通类
- try {
- clazz = Class.forName(name, isInitialized, classLoader);
- } catch (final ClassNotFoundException ex) {
- // 尝试获取内部类,例如java.lang.Thread.State =》java.lang.Thread$State
- clazz = tryLoadInnerClass(name, classLoader, isInitialized);
- if (null == clazz) {
- throw new UtilException(ex);
- }
- }
- }
- return clazz;
- }
-
- /**
- * 尝试转换并加载内部类,例如java.lang.Thread.State =》java.lang.Thread$State
- *
- * @param name 类名
- * @param classLoader {@link ClassLoader},{@code null} 则使用系统默认ClassLoader
- * @param isInitialized 是否初始化类(调用static模块内容和初始化static属性)
- * @return 类名对应的类
- * @since 4.1.20
- */
- private static Class> tryLoadInnerClass(String name, final ClassLoader classLoader, final boolean isInitialized) {
- // 尝试获取内部类,例如java.lang.Thread.State =》java.lang.Thread$State
- int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
- Class> clazz = null;
- while (lastDotIndex > 0) {// 类与内部类的分隔符不能在第一位,因此>0
- if (!Character.isUpperCase(name.charAt(lastDotIndex + 1))) {
- // 类名必须大写,非大写的类名跳过
- break;
- }
- name = name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
- clazz = forName(name, isInitialized, classLoader);
- if (null != clazz) {
- break;
- }
-
- lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
- }
- return clazz;
- }
-
- /**
- * 加载指定名称的类
- *
- * @param name 类名
- * @param initialize 是否初始化
- * @param loader {@link ClassLoader}
- * @return 指定名称对应的类,如果不存在类,返回{@code null}
- */
- private static Class> forName(final String name, final boolean initialize, final ClassLoader loader) {
- try {
- return Class.forName(name, initialize, loader);
- } catch (final ClassNotFoundException ex2) {
- // 尝试获取内部类失败时,忽略之。
- return null;
- }
- }
- // ----------------------------------------------------------------------------------- Private method end
}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/ResourceUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/ResourceUtil.java
index e157a5a32..e1d217adc 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/ResourceUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/ResourceUtil.java
@@ -16,6 +16,7 @@ import org.dromara.hutool.core.collection.iter.EnumerationIter;
import org.dromara.hutool.core.collection.iter.IterUtil;
import org.dromara.hutool.core.io.file.FileUtil;
import org.dromara.hutool.core.io.IORuntimeException;
+import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.util.CharsetUtil;
import org.dromara.hutool.core.classloader.ClassLoaderUtil;
import org.dromara.hutool.core.text.StrUtil;
@@ -30,6 +31,7 @@ import java.net.URL;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.List;
+import java.util.Properties;
import java.util.function.Predicate;
/**
@@ -290,4 +292,49 @@ public class ResourceUtil {
}
return resources;
}
+
+ /**
+ * 加载配置文件内容到{@link Properties}中
+ * 需要注意的是,如果资源文件的扩展名是.xml,会调用{@link Properties#loadFromXML(InputStream)} 读取。
+ *
+ * @param properties {@link Properties}文件
+ * @param resource 资源
+ * @param charset 编码,对XML无效
+ */
+ public static void loadTo(final Properties properties, final Resource resource, final Charset charset) {
+ Assert.notNull(properties);
+ Assert.notNull(resource);
+ final String filename = resource.getName();
+ if (filename != null && StrUtil.endWithIgnoreCase(filename, ".xml")) {
+ // XML
+ try (final InputStream in = resource.getStream()) {
+ properties.loadFromXML(in);
+ } catch (final IOException e) {
+ throw new IORuntimeException(e);
+ }
+ } else {
+ // .properties
+ try (final BufferedReader reader = resource.getReader(
+ ObjUtil.defaultIfNull(charset, CharsetUtil.UTF_8))) {
+ properties.load(reader);
+ } catch (final IOException e) {
+ throw new IORuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * 加载指定名称的所有配置文件内容到{@link Properties}中
+ *
+ * @param properties {@link Properties}文件
+ * @param resourceName 资源名,可以是相对classpath的路径,也可以是绝对路径
+ * @param classLoader {@link ClassLoader},{@code null}表示使用默认的当前上下文ClassLoader
+ * @param charset 编码,对XML无效
+ */
+ public static void loadAllTo(final Properties properties, final String resourceName,
+ final ClassLoader classLoader, final Charset charset) {
+ for (final Resource resource : getResources(resourceName, classLoader)) {
+ loadTo(properties, resource, charset);
+ }
+ }
}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ClassUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ClassUtil.java
index 0151d3f7f..2be0a8d6a 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ClassUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ClassUtil.java
@@ -49,6 +49,15 @@ import java.util.function.Predicate;
*/
public class ClassUtil {
+ /**
+ * 包名分界符: '.'
+ */
+ private static final char PACKAGE_SEPARATOR = CharUtil.DOT;
+ /**
+ * 内部类分界符: '$'
+ */
+ private static final char INNER_CLASS_SEPARATOR = '$';
+
/**
* {@code null}安全的获取对象类型
*
@@ -726,6 +735,64 @@ public class ClassUtil {
return new ArrayList<>(interfacesFound);
}
+ /**
+ * 加载指定名称的类
+ *
+ * @param name 类名
+ * @param isInitialized 是否初始化
+ * @param loader {@link ClassLoader},{@code null}表示默认
+ * @return 指定名称对应的类,如果不存在类,返回{@code null
+ * @link Class#forName(String, boolean, ClassLoader)}
+ */
+ public static Class> forName(final String name, final boolean isInitialized, ClassLoader loader) {
+ if(null == loader){
+ loader = ClassLoaderUtil.getClassLoader();
+ }
+
+ // 加载普通类
+ try {
+ return Class.forName(name, isInitialized, loader);
+ } catch (final ClassNotFoundException ex) {
+ // 尝试获取内部类,例如java.lang.Thread.State =》java.lang.Thread$State
+ final Class> clazz = forNameInnerClass(name, isInitialized, loader);
+ if (null == clazz) {
+ throw new UtilException(ex);
+ }
+ return clazz;
+ }
+ }
+
+ /**
+ * 尝试转换并加载内部类,例如java.lang.Thread.State =》java.lang.Thread$State
+ *
+ * @param name 类名
+ * @param classLoader {@link ClassLoader},{@code null} 则使用系统默认ClassLoader
+ * @param isInitialized 是否初始化类(调用static模块内容和初始化static属性)
+ * @return 类名对应的类,未找到返回{@code null}
+ */
+ private static Class> forNameInnerClass(String name, final boolean isInitialized, final ClassLoader classLoader) {
+ // 尝试获取内部类,例如java.lang.Thread.State =》java.lang.Thread$State
+ int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
+ Class> clazz = null;
+ while (lastDotIndex > 0) {// 类与内部类的分隔符不能在第一位,因此>0
+ if (!Character.isUpperCase(name.charAt(lastDotIndex + 1))) {
+ // 类名必须大写,非大写的类名跳过
+ break;
+ }
+ name = name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
+ try {
+ clazz = Class.forName(name, isInitialized, classLoader);
+ break;
+ } catch (final ClassNotFoundException ignore) {
+ //ignore
+ }
+
+ // 继续向前替换.为$
+ lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
+ }
+ return clazz;
+ }
+
/**
* 获取指定类的的接口列表
*
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/spi/KVServiceLoader.java b/hutool-core/src/main/java/org/dromara/hutool/core/spi/KVServiceLoader.java
new file mode 100644
index 000000000..3beddce15
--- /dev/null
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/spi/KVServiceLoader.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2023 looly(loolly@aliyun.com)
+ * Hutool is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ */
+
+package org.dromara.hutool.core.spi;
+
+import org.dromara.hutool.core.classloader.ClassLoaderUtil;
+import org.dromara.hutool.core.io.resource.ResourceUtil;
+import org.dromara.hutool.core.text.StrUtil;
+
+import java.nio.charset.Charset;
+import java.util.Properties;
+
+public class KVServiceLoader implements ServiceLoader{
+
+ private final String pathPrefix;
+ private final Class serviceClass;
+ private final ClassLoader classLoader;
+ private final Charset charset;
+
+ private Properties serviceProperties;
+
+ public KVServiceLoader(final String pathPrefix, final Class serviceClass,
+ final ClassLoader classLoader, final Charset charset) {
+ this.pathPrefix = pathPrefix;
+ this.serviceClass = serviceClass;
+ this.classLoader = classLoader;
+ this.charset = charset;
+
+ load();
+ }
+
+ /**
+ * 加载或重新加载全部服务
+ * @return this
+ */
+ public KVServiceLoader load(){
+ final Properties properties = new Properties();
+ ResourceUtil.loadAllTo(
+ properties,
+ pathPrefix + serviceClass.getName(),
+ classLoader,
+ charset);
+ this.serviceProperties = properties;
+ return this;
+ }
+
+ public Class getServiceClass(final String serviceName){
+ final String serviceClassName = this.serviceProperties.getProperty(serviceName);
+ if(StrUtil.isNotBlank(serviceClassName)){
+ return ClassLoaderUtil.loadClass(serviceClassName);
+ }
+
+ return null;
+ }
+}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/spi/ServiceLoader.java b/hutool-core/src/main/java/org/dromara/hutool/core/spi/ServiceLoader.java
new file mode 100644
index 000000000..f6b5994e2
--- /dev/null
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/spi/ServiceLoader.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2023 looly(loolly@aliyun.com)
+ * Hutool is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ */
+
+package org.dromara.hutool.core.spi;
+
+/**
+ * SPI服务加载接口
+ * 用户实现此接口用于制定不同的服务加载方式
+ *
+ * @param 服务对象类型
+ * @author looly
+ */
+public interface ServiceLoader {
+}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/util/ServiceLoaderUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/spi/ServiceLoaderUtil.java
similarity index 97%
rename from hutool-core/src/main/java/org/dromara/hutool/core/util/ServiceLoaderUtil.java
rename to hutool-core/src/main/java/org/dromara/hutool/core/spi/ServiceLoaderUtil.java
index bdc44c20e..7d349c573 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/util/ServiceLoaderUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/spi/ServiceLoaderUtil.java
@@ -10,10 +10,11 @@
* See the Mulan PSL v2 for more details.
*/
-package org.dromara.hutool.core.util;
+package org.dromara.hutool.core.spi;
import org.dromara.hutool.core.classloader.ClassLoaderUtil;
import org.dromara.hutool.core.collection.ListUtil;
+import org.dromara.hutool.core.util.ObjUtil;
import java.util.Iterator;
import java.util.List;
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/spi/package-info.java b/hutool-core/src/main/java/org/dromara/hutool/core/spi/package-info.java
new file mode 100644
index 000000000..bc41e33de
--- /dev/null
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/spi/package-info.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2023 looly(loolly@aliyun.com)
+ * Hutool is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ */
+
+/**
+ * 服务提供接口SPI(Service Provider interface)机制相关封装
+ */
+package org.dromara.hutool.core.spi;
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanUtilTest.java
index cfb7bb121..f6f29faec 100644
--- a/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanUtilTest.java
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanUtilTest.java
@@ -608,7 +608,8 @@ public class BeanUtilTest {
o.setAge(123);
o.setOpenid("asd");
- @SuppressWarnings("unchecked") final CopyOptions copyOptions = CopyOptions.of().setIgnoreProperties(Person::getAge,Person::getOpenid);
+ @SuppressWarnings("unchecked")
+ final CopyOptions copyOptions = CopyOptions.of().setIgnoreProperties(Person::getAge,Person::getOpenid);
final Person n = new Person();
BeanUtil.copyProperties(o, n, copyOptions);
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/classloader/ClassDescTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/classloader/ClassDescTest.java
new file mode 100644
index 000000000..ac5d37927
--- /dev/null
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/classloader/ClassDescTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2023 looly(loolly@aliyun.com)
+ * Hutool is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ */
+
+package org.dromara.hutool.core.classloader;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * 来自:org.apache.dubbo.common.utils.ReflectUtilsTest
+ */
+public class ClassDescTest {
+
+ @Test
+ void testDesc2Class() {
+ assertEquals(void.class, ClassDescUtil.descToClass("V"));
+ assertEquals(boolean.class, ClassDescUtil.descToClass("Z"));
+ assertEquals(boolean[].class, ClassDescUtil.descToClass("[Z"));
+ assertEquals(byte.class, ClassDescUtil.descToClass("B"));
+ assertEquals(char.class, ClassDescUtil.descToClass("C"));
+ assertEquals(double.class, ClassDescUtil.descToClass("D"));
+ assertEquals(float.class, ClassDescUtil.descToClass("F"));
+ assertEquals(int.class, ClassDescUtil.descToClass("I"));
+ assertEquals(long.class, ClassDescUtil.descToClass("J"));
+ assertEquals(short.class, ClassDescUtil.descToClass("S"));
+ assertEquals(String.class, ClassDescUtil.descToClass("Ljava.lang.String;"));
+ assertEquals(int[][].class, ClassDescUtil.descToClass(ClassDescUtil.getDesc(int[][].class)));
+ assertEquals(ClassDescTest[].class, ClassDescUtil.descToClass(ClassDescUtil.getDesc(ClassDescTest[].class)));
+ }
+
+ @Test
+ void getDescTest() {
+ // getDesc
+ assertEquals("Z", ClassDescUtil.getDesc(boolean.class));
+ assertEquals("[[[I", ClassDescUtil.getDesc(int[][][].class));
+ assertEquals("[[Ljava/lang/Object;", ClassDescUtil.getDesc(Object[][].class));
+ }
+
+ @Test
+ void nameToClassTest() {
+ final Class> aClass = ClassDescUtil.nameToClass("java.lang.Object[]", true, null);
+ assertEquals(Object[].class, aClass);
+ }
+}
diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/provider/GlobalProviderFactory.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/provider/GlobalProviderFactory.java
index 7b3e20cea..61dec78cb 100644
--- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/provider/GlobalProviderFactory.java
+++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/provider/GlobalProviderFactory.java
@@ -12,7 +12,7 @@
package org.dromara.hutool.crypto.provider;
-import org.dromara.hutool.core.util.ServiceLoaderUtil;
+import org.dromara.hutool.core.spi.ServiceLoaderUtil;
import org.dromara.hutool.crypto.SecureUtil;
import java.security.Provider;
diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/aop/proxy/ProxyFactory.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/aop/proxy/ProxyFactory.java
index ad96c719d..96c067a6c 100644
--- a/hutool-extra/src/main/java/org/dromara/hutool/extra/aop/proxy/ProxyFactory.java
+++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/aop/proxy/ProxyFactory.java
@@ -13,7 +13,7 @@
package org.dromara.hutool.extra.aop.proxy;
import org.dromara.hutool.core.reflect.ConstructorUtil;
-import org.dromara.hutool.core.util.ServiceLoaderUtil;
+import org.dromara.hutool.core.spi.ServiceLoaderUtil;
import org.dromara.hutool.extra.aop.aspects.Aspect;
import java.io.Serializable;
diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/expression/engine/ExpressionFactory.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/expression/engine/ExpressionFactory.java
index fb953b641..70701de44 100644
--- a/hutool-extra/src/main/java/org/dromara/hutool/extra/expression/engine/ExpressionFactory.java
+++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/expression/engine/ExpressionFactory.java
@@ -13,7 +13,7 @@
package org.dromara.hutool.extra.expression.engine;
import org.dromara.hutool.core.lang.Singleton;
-import org.dromara.hutool.core.util.ServiceLoaderUtil;
+import org.dromara.hutool.core.spi.ServiceLoaderUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.log.StaticLog;
diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/PinyinFactory.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/PinyinFactory.java
index 5d34b8928..86f5b5068 100644
--- a/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/PinyinFactory.java
+++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/PinyinFactory.java
@@ -13,7 +13,7 @@
package org.dromara.hutool.extra.pinyin.engine;
import org.dromara.hutool.core.lang.Singleton;
-import org.dromara.hutool.core.util.ServiceLoaderUtil;
+import org.dromara.hutool.core.spi.ServiceLoaderUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.log.StaticLog;
diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/template/engine/TemplateFactory.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/template/engine/TemplateFactory.java
index 56296242c..a3fd579c0 100644
--- a/hutool-extra/src/main/java/org/dromara/hutool/extra/template/engine/TemplateFactory.java
+++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/template/engine/TemplateFactory.java
@@ -15,7 +15,7 @@ package org.dromara.hutool.extra.template.engine;
import org.dromara.hutool.core.lang.Singleton;
import org.dromara.hutool.core.reflect.ConstructorUtil;
import org.dromara.hutool.core.text.StrUtil;
-import org.dromara.hutool.core.util.ServiceLoaderUtil;
+import org.dromara.hutool.core.spi.ServiceLoaderUtil;
import org.dromara.hutool.extra.template.TemplateConfig;
import org.dromara.hutool.extra.template.TemplateEngine;
import org.dromara.hutool.extra.template.TemplateException;
diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/tokenizer/engine/TokenizerFactory.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/tokenizer/engine/TokenizerFactory.java
index 435a9ee79..5a748ccf1 100644
--- a/hutool-extra/src/main/java/org/dromara/hutool/extra/tokenizer/engine/TokenizerFactory.java
+++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/tokenizer/engine/TokenizerFactory.java
@@ -13,7 +13,7 @@
package org.dromara.hutool.extra.tokenizer.engine;
import org.dromara.hutool.core.lang.Singleton;
-import org.dromara.hutool.core.util.ServiceLoaderUtil;
+import org.dromara.hutool.core.spi.ServiceLoaderUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.extra.tokenizer.TokenizerEngine;
import org.dromara.hutool.extra.tokenizer.TokenizerException;
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/ClientEngineFactory.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/ClientEngineFactory.java
index 65d487581..5fb5c188a 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/ClientEngineFactory.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/ClientEngineFactory.java
@@ -14,7 +14,7 @@ package org.dromara.hutool.http.client.engine;
import org.dromara.hutool.core.lang.Singleton;
import org.dromara.hutool.core.text.StrUtil;
-import org.dromara.hutool.core.util.ServiceLoaderUtil;
+import org.dromara.hutool.core.spi.ServiceLoaderUtil;
import org.dromara.hutool.http.HttpException;
import org.dromara.hutool.http.client.ClientConfig;
import org.dromara.hutool.http.client.ClientEngine;
diff --git a/hutool-log/src/main/java/org/dromara/hutool/log/LogFactory.java b/hutool-log/src/main/java/org/dromara/hutool/log/LogFactory.java
index dcf2dd996..db75ce6bf 100644
--- a/hutool-log/src/main/java/org/dromara/hutool/log/LogFactory.java
+++ b/hutool-log/src/main/java/org/dromara/hutool/log/LogFactory.java
@@ -15,7 +15,7 @@ package org.dromara.hutool.log;
import org.dromara.hutool.core.io.resource.ResourceUtil;
import org.dromara.hutool.core.lang.caller.CallerUtil;
import org.dromara.hutool.core.map.SafeConcurrentHashMap;
-import org.dromara.hutool.core.util.ServiceLoaderUtil;
+import org.dromara.hutool.core.spi.ServiceLoaderUtil;
import org.dromara.hutool.log.engine.console.ConsoleLogFactory;
import org.dromara.hutool.log.engine.jdk.JdkLogFactory;
diff --git a/hutool-setting/src/main/java/org/dromara/hutool/setting/dialect/Props.java b/hutool-setting/src/main/java/org/dromara/hutool/setting/dialect/Props.java
index 23cff2e80..23c7673ac 100644
--- a/hutool-setting/src/main/java/org/dromara/hutool/setting/dialect/Props.java
+++ b/hutool-setting/src/main/java/org/dromara/hutool/setting/dialect/Props.java
@@ -201,7 +201,7 @@ public final class Props extends Properties implements TypeGetter
public void load(final Resource resource) {
Assert.notNull(resource, "Props resource must be not null!");
this.resource = resource;
- PropsLoaderUtil.loadTo(this, resource, this.charset);
+ ResourceUtil.loadTo(this, resource, this.charset);
}
/**
diff --git a/hutool-setting/src/main/java/org/dromara/hutool/setting/dialect/PropsLoaderUtil.java b/hutool-setting/src/main/java/org/dromara/hutool/setting/dialect/PropsLoaderUtil.java
deleted file mode 100644
index 3d2cc7a7e..000000000
--- a/hutool-setting/src/main/java/org/dromara/hutool/setting/dialect/PropsLoaderUtil.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (c) 2023 looly(loolly@aliyun.com)
- * Hutool is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- * http://license.coscl.org.cn/MulanPSL2
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- */
-
-package org.dromara.hutool.setting.dialect;
-
-import org.dromara.hutool.core.io.IORuntimeException;
-import org.dromara.hutool.core.io.resource.Resource;
-import org.dromara.hutool.core.io.resource.ResourceUtil;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.Charset;
-import java.util.Properties;
-
-/**
- * {@link Properties} 资源内容加载工具
- *
- * @author looly
- * @since 6.0.0
- */
-public class PropsLoaderUtil {
-
- /**
- * 加载配置文件内容到{@link Properties}中
- * 需要注意的是,如果资源文件的扩展名是.xml,会调用{@link Properties#loadFromXML(InputStream)} 读取。
- *
- * @param properties {@link Properties}文件
- * @param resource 资源
- * @param charset 编码,对XML无效
- */
- public static void loadTo(final Properties properties, final Resource resource, final Charset charset) {
- final String filename = resource.getName();
- if (filename != null && filename.endsWith(".xml")) {
- // XML
- try (final InputStream in = resource.getStream()) {
- properties.loadFromXML(in);
- } catch (final IOException e) {
- throw new IORuntimeException(e);
- }
- } else {
- // .properties
- try (final BufferedReader reader = resource.getReader(charset)) {
- properties.load(reader);
- } catch (final IOException e) {
- throw new IORuntimeException(e);
- }
- }
- }
-
- /**
- * 加载指定名称的所有配置文件内容到{@link Properties}中
- *
- * @param properties {@link Properties}文件
- * @param resourceName 资源名,可以是相对classpath的路径,也可以是绝对路径
- * @param classLoader {@link ClassLoader},{@code null}表示使用默认的当前上下文ClassLoader
- * @param charset 编码,对XML无效
- */
- public static void loadAllTo(final Properties properties, final String resourceName, final ClassLoader classLoader, final Charset charset) {
- for (final Resource resource : ResourceUtil.getResources(resourceName, classLoader)) {
- loadTo(properties, resource, charset);
- }
- }
-}