From 3b53230a3810120b7958fe61fc37d446c228396a Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 16 Apr 2023 00:54:24 +0800 Subject: [PATCH] fix code --- README-EN.md | 2 +- README.md | 2 +- ...derUtil.java => JdkServiceLoaderUtil.java} | 2 +- .../hutool/core/spi/KVServiceLoader.java | 23 +++- .../hutool/core/spi/ListServiceLoader.java | 114 +++++++++++++++++- .../hutool/core/spi/ServiceLoader.java | 4 +- .../org/dromara/hutool/core/spi/SpiUtil.java | 66 ++++++++++ .../provider/GlobalProviderFactory.java | 4 +- .../hutool/extra/aop/proxy/ProxyFactory.java | 4 +- .../expression/engine/ExpressionFactory.java | 7 +- .../extra/pinyin/engine/PinyinFactory.java | 7 +- .../template/engine/TemplateFactory.java | 4 +- .../tokenizer/engine/TokenizerFactory.java | 4 +- .../client/engine/ClientEngineFactory.java | 4 +- .../org/dromara/hutool/log/LogFactory.java | 4 +- 15 files changed, 221 insertions(+), 30 deletions(-) rename hutool-core/src/main/java/org/dromara/hutool/core/spi/{ServiceLoaderUtil.java => JdkServiceLoaderUtil.java} (98%) create mode 100644 hutool-core/src/main/java/org/dromara/hutool/core/spi/SpiUtil.java diff --git a/README-EN.md b/README-EN.md index 907e09b0e..89b170d12 100755 --- a/README-EN.md +++ b/README-EN.md @@ -2,7 +2,7 @@

- 🍬A set of tools that keep Java sweet. + 🍬Make Java Sweet Again.

👉 https://hutool.cn/ 👈 diff --git a/README.md b/README.md index 3d28a092b..0798cc7e4 100755 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

- 🍬A set of tools that keep Java sweet. + 🍬Make Java Sweet Again.

👉 https://hutool.cn/ 👈 diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/spi/ServiceLoaderUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/spi/JdkServiceLoaderUtil.java similarity index 98% rename from hutool-core/src/main/java/org/dromara/hutool/core/spi/ServiceLoaderUtil.java rename to hutool-core/src/main/java/org/dromara/hutool/core/spi/JdkServiceLoaderUtil.java index 7d349c573..b4bffa78f 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/spi/ServiceLoaderUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/spi/JdkServiceLoaderUtil.java @@ -34,7 +34,7 @@ import java.util.ServiceLoader; * @author looly * @since 5.1.6 */ -public class ServiceLoaderUtil { +public class JdkServiceLoaderUtil { /** * 。加载第一个可用服务,如果用户定义了多个接口实现类,只获取第一个不报错的服务 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 index 6506dc525..e84c2ea0a 100644 --- 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 @@ -20,8 +20,7 @@ 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; +import java.util.*; /** * 键值对服务加载器,使用{@link Properties}加载并存储服务 @@ -75,6 +74,7 @@ public class KVServiceLoader extends AbsServiceLoader { // endregion private Properties serviceProperties; + // key: serviceName, value: service instance private final SimpleCache serviceCache; /** @@ -136,6 +136,24 @@ public class KVServiceLoader extends AbsServiceLoader { return this.serviceCache.get(serviceName, () -> createService(serviceName)); } + @Override + public Iterator iterator() { + return new Iterator() { + private final Iterator nameIter = + serviceProperties.stringPropertyNames().iterator(); + @Override + public boolean hasNext() { + return nameIter.hasNext(); + } + + @Override + public S next() { + return getService(nameIter.next()); + } + }; + } + + // region ----- private methods /** * 创建服务,无缓存 * @@ -161,4 +179,5 @@ public class KVServiceLoader extends AbsServiceLoader { return ClassLoaderUtil.loadClass(serviceClassName); } + // endregion } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/spi/ListServiceLoader.java b/hutool-core/src/main/java/org/dromara/hutool/core/spi/ListServiceLoader.java index 0523844d4..8f59cce99 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/spi/ListServiceLoader.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/spi/ListServiceLoader.java @@ -13,21 +13,33 @@ 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.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.reflect.ConstructorUtil; import org.dromara.hutool.core.text.StrUtil; +import org.dromara.hutool.core.util.AccessUtil; import java.io.BufferedReader; import java.io.IOException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; /** - * 列表类型的服务加载器,用于替换JDK提供的{@link java.util.ServiceLoader} + * 列表类型的服务加载器,用于替换JDK提供的{@link java.util.ServiceLoader}
+ * 相比JDK,增加了: + *

* * @param 服务类型 * @author looly @@ -78,6 +90,7 @@ public class ListServiceLoader extends AbsServiceLoader { // endregion private final List serviceNames; + // key: className, value: service instance private final SimpleCache serviceCache; /** @@ -114,6 +127,48 @@ public class ListServiceLoader extends AbsServiceLoader { return this.serviceNames.size(); } + /** + * 获取指定服务的实现类 + * + * @param index 服务名称 + * @return 服务名称对应的实现类 + */ + public Class getServiceClass(final int index) { + return AccessUtil.doPrivileged(() -> getServiceClassUnsafe(index), this.acc); + } + + /** + * 获取指定序号对应的服务,使用缓存,多次调用只返回相同的服务对象 + * + * @param index 服务名称 + * @return 服务对象 + */ + public S getService(final int index) { + final String serviceClassName = this.serviceNames.get(index); + if(null == serviceClassName){ + return null; + } + return getServiceByName(serviceClassName); + } + + @Override + public Iterator iterator() { + return new Iterator() { + private final Iterator nameIter = serviceNames.iterator(); + @Override + public boolean hasNext() { + return nameIter.hasNext(); + } + + @Override + public S next() { + return getServiceByName(nameIter.next()); + } + }; + } + + // region ----- private methods + /** * 解析一个资源,一个资源对应一个service文件 * @@ -165,12 +220,21 @@ public class ListServiceLoader extends AbsServiceLoader { return lineNo + 1; } + /** + * 检查行 + * + * @param resource 资源 + * @param lineNo 行号 + * @param line 行内容 + */ private void checkLine(final Resource resource, final int lineNo, final String line) { if (StrUtil.containsBlank(line)) { + // 类中不允许空白符 fail(resource, lineNo, "Illegal configuration-file syntax"); } int cp = line.codePointAt(0); if (!Character.isJavaIdentifierStart(cp)) { + // 非Java合法标识符 fail(resource, lineNo, "Illegal provider-class name: " + line); } final int n = line.length(); @@ -182,8 +246,52 @@ public class ListServiceLoader extends AbsServiceLoader { } } - private void fail(final Resource resource, final int line, final String msg) { - throw new SPIException(this.serviceClass + ":" + resource.getUrl() + ":" + line + ": " + msg); + /** + * 抛出异常 + * + * @param resource 资源 + * @param lineNo 行号 + * @param msg 消息 + */ + private void fail(final Resource resource, final int lineNo, final String msg) { + throw new SPIException(this.serviceClass + ":" + resource.getUrl() + ":" + lineNo + ": " + msg); } + /** + * 获取指定class名对应的服务,使用缓存,多次调用只返回相同的服务对象 + * + * @param serviceClassName 服务名称 + * @return 服务对象 + */ + private S getServiceByName(final String serviceClassName) { + return this.serviceCache.get(serviceClassName, () -> createService(serviceClassName)); + } + + /** + * 创建服务,无缓存 + * + * @param serviceClassName 服务类名称 + * @return 服务对象 + */ + private S createService(final String serviceClassName) { + return AccessUtil.doPrivileged(() -> + ConstructorUtil.newInstance(ClassLoaderUtil.loadClass(serviceClassName)), + this.acc); + } + + /** + * 获取指定服务的实现类 + * + * @param index 服务索引号 + * @return 服务名称对应的实现类 + */ + private Class getServiceClassUnsafe(final int index) { + final String serviceClassName = this.serviceNames.get(index); + if (StrUtil.isBlank(serviceClassName)) { + return null; + } + + return ClassLoaderUtil.loadClass(serviceClassName); + } + // endregion } 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 index 59efebe81..a822e526f 100644 --- 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 @@ -16,10 +16,10 @@ package org.dromara.hutool.core.spi; * SPI服务加载接口
* 用户实现此接口用于制定不同的服务加载方式 * - * @param 服务对象类型 + * @param 服务对象类型 * @author looly */ -public interface ServiceLoader { +public interface ServiceLoader extends Iterable{ /** * 加载服务 diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/spi/SpiUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/spi/SpiUtil.java new file mode 100644 index 000000000..40998df9c --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/spi/SpiUtil.java @@ -0,0 +1,66 @@ +/* + * 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 java.util.Iterator; + +/** + * 服务提供接口SPI(Service Provider interface)相关工具类 + * + * @author looly + * @since 6.0.0 + */ +public class SpiUtil { + + /** + * 加载第一个可用服务,如果用户定义了多个接口实现类,只获取第一个不报错的服务 + * + * @param 接口类型 + * @param clazz 服务接口 + * @return 第一个服务接口实现对象,无实现返回{@code null} + */ + public static T loadFirstAvailable(final Class clazz) { + final Iterator iterator = loadList(clazz).iterator(); + while (iterator.hasNext()) { + try { + return iterator.next(); + } catch (final Throwable ignore) { + // ignore + } + } + return null; + } + + /** + * 加载服务 + * + * @param 接口类型 + * @param clazz 服务接口 + * @return 服务接口实现列表 + */ + public static ServiceLoader loadList(final Class clazz) { + return loadList(clazz, null); + } + + /** + * 加载服务 + * + * @param 接口类型 + * @param clazz 服务接口 + * @param loader {@link ClassLoader} + * @return 服务接口实现列表 + */ + public static ServiceLoader loadList(final Class clazz, final ClassLoader loader) { + return ListServiceLoader.of(clazz, loader); + } +} 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 61dec78cb..5c0c76ac1 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.spi.ServiceLoaderUtil; +import org.dromara.hutool.core.spi.SpiUtil; import org.dromara.hutool.crypto.SecureUtil; import java.security.Provider; @@ -54,7 +54,7 @@ public class GlobalProviderFactory { * @return {@link Provider} or {@code null} */ private static Provider _createProvider() { - final ProviderFactory factory = ServiceLoaderUtil.loadFirstAvailable(ProviderFactory.class); + final ProviderFactory factory = SpiUtil.loadFirstAvailable(ProviderFactory.class); if (null == factory) { return null; } 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 96c067a6c..0f1e4d67b 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.spi.ServiceLoaderUtil; +import org.dromara.hutool.core.spi.SpiUtil; import org.dromara.hutool.extra.aop.aspects.Aspect; import java.io.Serializable; @@ -56,7 +56,7 @@ public interface ProxyFactory extends Serializable { * @return 代理工厂 */ static ProxyFactory of() { - return ServiceLoaderUtil.loadFirstAvailable(ProxyFactory.class); + return SpiUtil.loadFirstAvailable(ProxyFactory.class); } /** 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 70701de44..ea92d9c3d 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,12 +13,11 @@ package org.dromara.hutool.extra.expression.engine; import org.dromara.hutool.core.lang.Singleton; -import org.dromara.hutool.core.spi.ServiceLoaderUtil; +import org.dromara.hutool.core.spi.SpiUtil; import org.dromara.hutool.core.text.StrUtil; -import org.dromara.hutool.log.StaticLog; - import org.dromara.hutool.extra.expression.ExpressionEngine; import org.dromara.hutool.extra.expression.ExpressionException; +import org.dromara.hutool.log.StaticLog; /** * 表达式语言引擎工厂类,用于根据用户引入的表达式jar,自动创建对应的引擎对象 @@ -56,7 +55,7 @@ public class ExpressionFactory { * @return {@link ExpressionEngine} */ private static ExpressionEngine doCreate() { - final ExpressionEngine engine = ServiceLoaderUtil.loadFirstAvailable(ExpressionEngine.class); + final ExpressionEngine engine = SpiUtil.loadFirstAvailable(ExpressionEngine.class); if(null != engine){ return engine; } 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 86f5b5068..5293d0596 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,12 +13,11 @@ package org.dromara.hutool.extra.pinyin.engine; import org.dromara.hutool.core.lang.Singleton; -import org.dromara.hutool.core.spi.ServiceLoaderUtil; +import org.dromara.hutool.core.spi.SpiUtil; import org.dromara.hutool.core.text.StrUtil; -import org.dromara.hutool.log.StaticLog; - import org.dromara.hutool.extra.pinyin.PinyinEngine; import org.dromara.hutool.extra.pinyin.PinyinException; +import org.dromara.hutool.log.StaticLog; /** * 简单拼音引擎工厂,用于根据用户引入的拼音库jar,自动创建对应的拼音引擎对象 @@ -55,7 +54,7 @@ public class PinyinFactory { * @return {@link PinyinEngine} */ private static PinyinEngine doCreate() { - final PinyinEngine engine = ServiceLoaderUtil.loadFirstAvailable(PinyinEngine.class); + final PinyinEngine engine = SpiUtil.loadFirstAvailable(PinyinEngine.class); if(null != engine){ return engine; } 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 a3fd579c0..ecd25fe07 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 @@ -14,8 +14,8 @@ 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.spi.SpiUtil; import org.dromara.hutool.core.text.StrUtil; -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; @@ -75,7 +75,7 @@ public class TemplateFactory { if(null != customEngineClass){ engine = ConstructorUtil.newInstance(customEngineClass); }else{ - engine = ServiceLoaderUtil.loadFirstAvailable(TemplateEngine.class); + engine = SpiUtil.loadFirstAvailable(TemplateEngine.class); } if(null != engine){ return engine.init(config); 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 5a748ccf1..537d630c6 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.spi.ServiceLoaderUtil; +import org.dromara.hutool.core.spi.SpiUtil; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.extra.tokenizer.TokenizerEngine; import org.dromara.hutool.extra.tokenizer.TokenizerException; @@ -55,7 +55,7 @@ public class TokenizerFactory { * @return {@link TokenizerEngine} */ private static TokenizerEngine doCreate() { - final TokenizerEngine engine = ServiceLoaderUtil.loadFirstAvailable(TokenizerEngine.class); + final TokenizerEngine engine = SpiUtil.loadFirstAvailable(TokenizerEngine.class); if(null != engine){ return engine; } 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 5fb5c188a..c96605bca 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 @@ -13,8 +13,8 @@ package org.dromara.hutool.http.client.engine; import org.dromara.hutool.core.lang.Singleton; +import org.dromara.hutool.core.spi.SpiUtil; import org.dromara.hutool.core.text.StrUtil; -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; @@ -68,7 +68,7 @@ public class ClientEngineFactory { * @return {@code EngineFactory} */ private static ClientEngine doCreate() { - final ClientEngine engine = ServiceLoaderUtil.loadFirstAvailable(ClientEngine.class); + final ClientEngine engine = SpiUtil.loadFirstAvailable(ClientEngine.class); if (null != engine) { return engine; } 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 db75ce6bf..d822b0ba1 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.spi.ServiceLoaderUtil; +import org.dromara.hutool.core.spi.SpiUtil; import org.dromara.hutool.log.engine.console.ConsoleLogFactory; import org.dromara.hutool.log.engine.jdk.JdkLogFactory; @@ -182,7 +182,7 @@ public abstract class LogFactory { * @return 日志实现类 */ private static LogFactory doCreate() { - final LogFactory factory = ServiceLoaderUtil.loadFirstAvailable(LogFactory.class); + final LogFactory factory = SpiUtil.loadFirstAvailable(LogFactory.class); if (null != factory) { return factory; }