This commit is contained in:
Looly 2023-04-15 17:31:13 +08:00
parent a280ff0918
commit 9781d5eb9f
9 changed files with 443 additions and 19 deletions

View File

@ -71,6 +71,21 @@ public class SimpleCache<K, V> implements Iterable<Map.Entry<K, V>>, Serializabl
this.rawMap = initMap;
}
/**
* 是否包含键
*
* @param key
* @return 是否包含
*/
public boolean containsKey(final K key) {
lock.readLock().lock();
try {
return rawMap.containsKey(MutableObj.of(key));
} finally {
lock.readLock().unlock();
}
}
/**
* 从缓存池中查找值
*

View File

@ -299,7 +299,7 @@ public class ResourceUtil {
*
* @param properties {@link Properties}文件
* @param resource 资源
* @param charset 编码对XML无效
* @param charset 编码对XML无效默认UTF-8
*/
public static void loadTo(final Properties properties, final Resource resource, final Charset charset) {
Assert.notNull(properties);
@ -329,7 +329,7 @@ public class ResourceUtil {
* @param properties {@link Properties}文件
* @param resourceName 资源名可以是相对classpath的路径也可以是绝对路径
* @param classLoader {@link ClassLoader}{@code null}表示使用默认的当前上下文ClassLoader
* @param charset 编码对XML无效
* @param charset 编码对XML无效默认UTF-8
*/
public static void loadAllTo(final Properties properties, final String resourceName,
final ClassLoader classLoader, final Charset charset) {

View File

@ -12,6 +12,7 @@
package org.dromara.hutool.core.reflect;
import org.dromara.hutool.core.classloader.ClassLoaderUtil;
import org.dromara.hutool.core.exceptions.UtilException;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.map.WeakConcurrentMap;
@ -103,7 +104,7 @@ public class ConstructorUtil {
@SuppressWarnings("unchecked")
public static <T> T newInstance(final String clazz) throws UtilException {
try {
return (T) Class.forName(clazz).newInstance();
return (T) ClassLoaderUtil.loadClass(clazz).newInstance();
} catch (final Exception e) {
throw new UtilException(e, "Instance class [{}] error!", clazz);
}

View File

@ -0,0 +1,52 @@
/*
* 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.text.StrUtil;
import java.nio.charset.Charset;
import java.security.AccessControlContext;
import java.security.AccessController;
/**
* 抽象服务加载器提供包括路径前缀服务类类加载器编码安全相关持有
*
* @param <S> 服务类型
* @author looly
* @since 6.0.0
*/
public abstract class AbsServiceLoader<S> implements ServiceLoader<S> {
protected final String pathPrefix;
protected final Class<S> serviceClass;
protected final ClassLoader classLoader;
protected final Charset charset;
protected final AccessControlContext acc;
/**
* 构造
*
* @param pathPrefix 路径前缀
* @param serviceClass 服务名称
* @param classLoader 自定义类加载器, {@code null}表示使用默认当前的类加载器
* @param charset 编码默认UTF-8
*/
public AbsServiceLoader(final String pathPrefix, final Class<S> serviceClass,
final ClassLoader classLoader, final Charset charset) {
this.pathPrefix = StrUtil.addSuffixIfNot(pathPrefix, StrUtil.SLASH);
this.serviceClass = serviceClass;
this.classLoader = classLoader;
this.charset = charset;
this.acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
}
}

View File

@ -12,37 +12,92 @@
package org.dromara.hutool.core.spi;
import org.dromara.hutool.core.cache.SimpleCache;
import org.dromara.hutool.core.classloader.ClassLoaderUtil;
import org.dromara.hutool.core.io.resource.ResourceUtil;
import org.dromara.hutool.core.reflect.ConstructorUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.util.AccessUtil;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Properties;
public class KVServiceLoader<S> implements ServiceLoader<S>{
/**
* 键值对服务加载器使用{@link Properties}加载并存储服务
*
* @param <S> 服务类型
* @author looly
* @since 6.0.0
*/
public class KVServiceLoader<S> extends AbsServiceLoader<S> {
private final String pathPrefix;
private final Class<S> serviceClass;
private final ClassLoader classLoader;
private final Charset charset;
private static final String PREFIX_HUTOOL = "META-INF/hutool/";
// region ----- of
/**
* 构建KVServiceLoader
*
* @param <S> 服务类型
* @param serviceClass 服务名称
* @return KVServiceLoader
*/
public static <S> KVServiceLoader<S> of(final Class<S> serviceClass) {
return of(serviceClass, null);
}
/**
* 构建KVServiceLoader
*
* @param <S> 服务类型
* @param serviceClass 服务名称
* @param classLoader 自定义类加载器, {@code null}表示使用默认当前的类加载器
* @return KVServiceLoader
*/
public static <S> KVServiceLoader<S> of(final Class<S> serviceClass, final ClassLoader classLoader) {
return of(PREFIX_HUTOOL, serviceClass, classLoader);
}
/**
* 构建KVServiceLoader
*
* @param <S> 服务类型
* @param pathPrefix 路径前缀
* @param serviceClass 服务名称
* @param classLoader 自定义类加载器, {@code null}表示使用默认当前的类加载器
* @return KVServiceLoader
*/
public static <S> KVServiceLoader<S> of(final String pathPrefix, final Class<S> serviceClass,
final ClassLoader classLoader) {
return new KVServiceLoader<>(pathPrefix, serviceClass, classLoader, null);
}
// endregion
private Properties serviceProperties;
private final SimpleCache<String, S> serviceCache;
/**
* 构造
*
* @param pathPrefix 路径前缀
* @param serviceClass 服务名称
* @param classLoader 自定义类加载器, {@code null}表示使用默认当前的类加载器
* @param charset 编码默认UTF-8
*/
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;
super(pathPrefix, serviceClass, classLoader, charset);
this.serviceCache = new SimpleCache<>(new HashMap<>());
load();
}
/**
* 加载或重新加载全部服务
* @return this
*/
public KVServiceLoader<S> load(){
@Override
public void load() {
final Properties properties = new Properties();
ResourceUtil.loadAllTo(
properties,
@ -50,15 +105,56 @@ public class KVServiceLoader<S> implements ServiceLoader<S>{
classLoader,
charset);
this.serviceProperties = properties;
return this;
}
public Class<S> getServiceClass(final String serviceName){
@Override
public int size() {
return this.serviceProperties.size();
}
/**
* 获取指定服务的实现类
*
* @param serviceName 服务名称
* @return 服务名称对应的实现类
*/
public Class<S> getServiceClass(final String serviceName) {
return AccessUtil.doPrivileged(() -> getServiceClassUnsafe(serviceName), this.acc);
}
/**
* 获取指定名称对应的服务使用缓存多次调用只返回相同的服务对象
*
* @param serviceName 服务名称
* @return 服务对象
*/
public S getService(final String serviceName) {
return this.serviceCache.get(serviceName, () -> createService(serviceName));
}
/**
* 创建服务无缓存
*
* @param serviceName 服务名称
* @return 服务对象
*/
private S createService(final String serviceName) {
return AccessUtil.doPrivileged(() ->
ConstructorUtil.newInstance(getServiceClassUnsafe(serviceName)), this.acc);
}
/**
* 获取指定服务的实现类
*
* @param serviceName 服务名称
* @return 服务名称对应的实现类
*/
private Class<S> getServiceClassUnsafe(final String serviceName) {
final String serviceClassName = this.serviceProperties.getProperty(serviceName);
if(StrUtil.isNotBlank(serviceClassName)){
return ClassLoaderUtil.loadClass(serviceClassName);
if (StrUtil.isBlank(serviceClassName)) {
return null;
}
return null;
return ClassLoaderUtil.loadClass(serviceClassName);
}
}

View File

@ -0,0 +1,158 @@
/*
* 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.cache.SimpleCache;
import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.core.io.resource.MultiResource;
import org.dromara.hutool.core.io.resource.Resource;
import org.dromara.hutool.core.io.resource.ResourceUtil;
import org.dromara.hutool.core.util.CharUtil;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* 列表类型的服务加载器用于替换JDK提供的{@link java.util.ServiceLoader}
*
* @param <S> 服务类型
* @author looly
* @since 6.0.0
*/
public class ListServiceLoader<S> extends AbsServiceLoader<S> {
private static final String PREFIX_SERVICES = "META-INF/services/";
// region ----- of
/**
* 构建KVServiceLoader
*
* @param <S> 服务类型
* @param serviceClass 服务名称
* @return KVServiceLoader
*/
public static <S> ListServiceLoader<S> of(final Class<S> serviceClass) {
return of(serviceClass, null);
}
/**
* 构建KVServiceLoader
*
* @param <S> 服务类型
* @param serviceClass 服务名称
* @param classLoader 自定义类加载器, {@code null}表示使用默认当前的类加载器
* @return KVServiceLoader
*/
public static <S> ListServiceLoader<S> of(final Class<S> serviceClass, final ClassLoader classLoader) {
return of(PREFIX_SERVICES, serviceClass, classLoader);
}
/**
* 构建KVServiceLoader
*
* @param <S> 服务类型
* @param pathPrefix 路径前缀
* @param serviceClass 服务名称
* @param classLoader 自定义类加载器, {@code null}表示使用默认当前的类加载器
* @return KVServiceLoader
*/
public static <S> ListServiceLoader<S> of(final String pathPrefix, final Class<S> serviceClass,
final ClassLoader classLoader) {
return new ListServiceLoader<>(pathPrefix, serviceClass, classLoader, null);
}
// endregion
private final List<String> serviceNames;
private final SimpleCache<String, S> serviceCache;
/**
* 构造
*
* @param pathPrefix 路径前缀
* @param serviceClass 服务名称
* @param classLoader 自定义类加载器, {@code null}表示使用默认当前的类加载器
* @param charset 编码默认UTF-8
*/
public ListServiceLoader(final String pathPrefix, final Class<S> serviceClass,
final ClassLoader classLoader, final Charset charset) {
super(pathPrefix, serviceClass, classLoader, charset);
this.serviceNames = new ArrayList<>();
this.serviceCache = new SimpleCache<>(new HashMap<>());
load();
}
@Override
public void load() {
final MultiResource resources = ResourceUtil.getResources(
pathPrefix + serviceClass.getName(),
this.classLoader);
for (final Resource resource : resources) {
parse(resource);
}
}
@Override
public int size() {
return this.serviceNames.size();
}
private void parse(final Resource resource){
try(final BufferedReader reader = resource.getReader(this.charset)){
int lc = 1;
while(lc >= 0){
lc = parseLine(resource, reader, lc, this.serviceNames);
}
} catch (final IOException e) {
throw new IORuntimeException(e);
}
}
private int parseLine(final Resource resource, final BufferedReader r, final int lc,
final List<String> names)
throws IOException {
String ln = r.readLine();
if (ln == null) {
return -1;
}
final int ci = ln.indexOf('#');
if (ci >= 0) ln = ln.substring(0, ci);
ln = ln.trim();
final int n = ln.length();
if (n != 0) {
if ((ln.indexOf(CharUtil.SPACE) >= 0) || (ln.indexOf(CharUtil.TAB) >= 0))
fail(resource, lc, "Illegal configuration-file syntax");
int cp = ln.codePointAt(0);
if (!Character.isJavaIdentifierStart(cp))
fail(resource, lc, "Illegal provider-class name: " + ln);
for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
cp = ln.codePointAt(i);
if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
fail(resource, lc, "Illegal provider-class name: " + ln);
}
if (!serviceCache.containsKey(ln) && !names.contains(ln))
names.add(ln);
}
return lc + 1;
}
private void fail(final Resource resource, final int line, final String msg) {
throw new SPIException(this.serviceClass + ":" + resource.getUrl() + ":" + line + ": " + msg);
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.exceptions.ExceptionUtil;
import org.dromara.hutool.core.text.StrUtil;
/**
* SPI异常
*
* @author looly
*/
public class SPIException extends RuntimeException {
private static final long serialVersionUID = 1L;
public SPIException(final Throwable cause) {
super(ExceptionUtil.getMessage(cause), cause);
}
public SPIException(final String message) {
super(message);
}
public SPIException(final String messageTemplate, final Object... params) {
super(StrUtil.format(messageTemplate, params));
}
public SPIException(final String message, final Throwable cause) {
super(message, cause);
}
public SPIException(final String message, final Throwable cause, final boolean enableSuppression, final boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public SPIException(final Throwable cause, final String messageTemplate, final Object... params) {
super(StrUtil.format(messageTemplate, params), cause);
}
}

View File

@ -20,4 +20,16 @@ package org.dromara.hutool.core.spi;
* @author looly
*/
public interface ServiceLoader<T> {
/**
* 加载服务
*/
void load();
/**
* 服务总数
*
* @return 总数
*/
int size();
}

View File

@ -0,0 +1,41 @@
/*
* 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.util;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* {@link AccessController}相关封装
*
* @author looly
* @since 6.0.0
*/
public class AccessUtil {
/**
* @param action 执行内容
* @param context 上下文当为{@code null}时直接执行内容而不检查
* @param <T> 执行结果类型
* @return 结果
*/
public static <T> T doPrivileged(final PrivilegedAction<T> action,
final AccessControlContext context) {
if (null == context) {
return action.run();
}
return AccessController.doPrivileged(action, context);
}
}