This commit is contained in:
Looly 2023-04-15 14:48:47 +08:00
parent 241aea60e4
commit 2c3eac6046
20 changed files with 676 additions and 289 deletions

View File

@ -0,0 +1,366 @@
/*
* 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.dromara.hutool.core.exceptions.UtilException;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.map.BiMap;
import org.dromara.hutool.core.reflect.ClassUtil;
import org.dromara.hutool.core.text.StrTrimer;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.util.CharUtil;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.net.URL;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.HashMap;
/**
* 类描述工具类<br>
* 来自org.apache.dubbo.common.utils.ReflectUtils
*
* @author Dubbo
* @since 6.0.0
*/
public class ClassDescUtil {
/**
* void(V).
*/
public static final char JVM_VOID = 'V';
/**
* boolean(Z).
*/
public static final char JVM_BOOLEAN = 'Z';
/**
* byte(B).
*/
public static final char JVM_BYTE = 'B';
/**
* char(C).
*/
public static final char JVM_CHAR = 'C';
/**
* double(D).
*/
public static final char JVM_DOUBLE = 'D';
/**
* float(F).
*/
public static final char JVM_FLOAT = 'F';
/**
* int(I).
*/
public static final char JVM_INT = 'I';
/**
* long(J).
*/
public static final char JVM_LONG = 'J';
/**
* short(S).
*/
public static final char JVM_SHORT = 'S';
/**
* 原始类型名和其class对应表例如int.class = int
*/
private static final BiMap<Class<?>, Character> PRIMITIVE_CLASS_DESC_MAP = new BiMap<>(new HashMap<>(9, 1));
private static final BiMap<Class<?>, 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
* <pre>{@code
* "[Z" => boolean[].class
* "[[Ljava/util/Map;" => java.util.Map[][].class
* }</pre>
*
* @param desc 类描述
* @return Class
* @throws UtilException 类没有找到
*/
public static Class<?> descToClass(final String desc) throws UtilException {
return descToClass(desc, true, null);
}
/**
* Class描述转Class
* <pre>{@code
* "[Z" => boolean[].class
* "[[Ljava/util/Map;" => java.util.Map[][].class
* }</pre>
*
* @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();
}
/**
* 获取方法或构造描述<br>
* 方法
* <pre>{@code
* int do(int arg1) => "do(I)I"
* void do(String arg1,boolean arg2) => "do(Ljava/lang/String;Z)V"
* }</pre>
* 构造
* <pre>
* "()V", "(Ljava/lang/String;I)V"
* </pre>
*
* @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);
}
/**
* 类名称转描述
*
* <pre>{@code
* java.util.Map[][] => "[[Ljava/util/Map;"
* }</pre>
*
* @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();
}
/**
* 类描述转名称
*
* <pre>{@code
* "[[I" => "int[][]"
* }</pre>
*
* @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();
}
}

View File

@ -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}工具类<br>
@ -39,48 +28,26 @@ import java.util.Map;
public class ClassLoaderUtil {
/**
* 数组类的结尾符: "[]"
* 获取{@link ClassLoader}<br>
* 获取顺序如下<br>
*
* <pre>
* 1获取当前线程的ContextClassLoader
* 2获取当前类对应的ClassLoader
* 3获取系统ClassLoader{@link ClassLoader#getSystemClassLoader()}
* </pre>
*
* @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<String, Class<?>> PRIMITIVE_TYPE_NAME_MAP = new SafeConcurrentHashMap<>(32);
static {
final List<Class<?>> 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}<br>
* 获取顺序如下<br>
* 创建新的{@link JarClassLoader}并使用此Classloader加载目录下的class文件和jar文件
*
* <pre>
* 1获取当前线程的ContextClassLoader
* 2获取当前类对应的ClassLoader
* 3获取系统ClassLoader{@link ClassLoader#getSystemClassLoader()}
* </pre>
*
* @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属性<br>
* 扩展{@link Class#forName(String, boolean, ClassLoader)}方法支持以下几类类名的加载
@ -201,48 +153,8 @@ public class ClassLoaderUtil {
* @throws UtilException 包装{@link ClassNotFoundException}没有类名对应的类时抛出此异常
*/
@SuppressWarnings("unchecked")
public static <T> Class<T> 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<T>) 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 <T> Class<T> loadClass(final String name, final boolean isInitialized, final ClassLoader classLoader) throws UtilException {
return (Class<T>) 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
}

View File

@ -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}<br>
* 需要注意的是如果资源文件的扩展名是.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);
}
}
}

View File

@ -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;
}
/**
* 获取指定类的的接口列表
*

View File

@ -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<S> implements ServiceLoader<S>{
private final String pathPrefix;
private final Class<S> serviceClass;
private final ClassLoader classLoader;
private final Charset charset;
private Properties serviceProperties;
public KVServiceLoader(final String pathPrefix, final Class<S> serviceClass,
final ClassLoader classLoader, final Charset charset) {
this.pathPrefix = pathPrefix;
this.serviceClass = serviceClass;
this.classLoader = classLoader;
this.charset = charset;
load();
}
/**
* 加载或重新加载全部服务
* @return this
*/
public KVServiceLoader<S> load(){
final Properties properties = new Properties();
ResourceUtil.loadAllTo(
properties,
pathPrefix + serviceClass.getName(),
classLoader,
charset);
this.serviceProperties = properties;
return this;
}
public Class<S> getServiceClass(final String serviceName){
final String serviceClassName = this.serviceProperties.getProperty(serviceName);
if(StrUtil.isNotBlank(serviceClassName)){
return ClassLoaderUtil.loadClass(serviceClassName);
}
return null;
}
}

View File

@ -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服务加载接口<br>
* 用户实现此接口用于制定不同的服务加载方式
*
* @param <T> 服务对象类型
* @author looly
*/
public interface ServiceLoader<T> {
}

View File

@ -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;

View File

@ -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.
*/
/**
* 服务提供接口SPIService Provider interface机制相关封装
*/
package org.dromara.hutool.core.spi;

View File

@ -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);

View File

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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -201,7 +201,7 @@ public final class Props extends Properties implements TypeGetter<CharSequence>
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);
}
/**

View File

@ -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}<br>
* 需要注意的是如果资源文件的扩展名是.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);
}
}
}