From bc9d66503673bba89f0191e78417dbd00b930b9b Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 16 Apr 2023 01:34:41 +0800 Subject: [PATCH] add test --- .../hutool/core/spi/ListServiceLoader.java | 15 +++- ...rviceLoader.java => MapServiceLoader.java} | 35 +++++++--- .../org/dromara/hutool/core/spi/SpiUtil.java | 17 ++++- .../dromara/hutool/core/spi/package-info.java | 6 +- .../dromara/hutool/core/spi/SpiUtilTest.java | 69 +++++++++++++++++++ ...omara.hutool.core.spi.SpiUtilTest$TestSPI1 | 6 ++ 6 files changed, 132 insertions(+), 16 deletions(-) rename hutool-core/src/main/java/org/dromara/hutool/core/spi/{KVServiceLoader.java => MapServiceLoader.java} (81%) create mode 100644 hutool-core/src/test/java/org/dromara/hutool/core/spi/SpiUtilTest.java create mode 100644 hutool-core/src/test/resources/META-INF/hutool/org.dromara.hutool.core.spi.SpiUtilTest$TestSPI1 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 8f59cce99..f80a4f63c 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 @@ -41,6 +41,16 @@ import java.util.List; *
  • 提供更加灵活的服务加载机制,当选择加载指定服务时,其它服务无需加载。
  • * * + *

    + * 服务文件默认位于"META-INF/services/"下,文件名为服务接口类全名。内容类似于: + *

    + *     # 我是注释
    + *     hutool.service.Service1
    + *     hutool.service.Service2
    + * 
    + *

    + * 通过调用{@link #getService(int)}方法,传入序号,即可获取对应服务。 + * * @param 服务类型 * @author looly * @since 6.0.0 @@ -145,7 +155,7 @@ public class ListServiceLoader extends AbsServiceLoader { */ public S getService(final int index) { final String serviceClassName = this.serviceNames.get(index); - if(null == serviceClassName){ + if (null == serviceClassName) { return null; } return getServiceByName(serviceClassName); @@ -155,6 +165,7 @@ public class ListServiceLoader extends AbsServiceLoader { public Iterator iterator() { return new Iterator() { private final Iterator nameIter = serviceNames.iterator(); + @Override public boolean hasNext() { return nameIter.hasNext(); @@ -275,7 +286,7 @@ public class ListServiceLoader extends AbsServiceLoader { */ private S createService(final String serviceClassName) { return AccessUtil.doPrivileged(() -> - ConstructorUtil.newInstance(ClassLoaderUtil.loadClass(serviceClassName)), + ConstructorUtil.newInstance(ClassLoaderUtil.loadClass(serviceClassName)), this.acc); } 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/MapServiceLoader.java similarity index 81% rename from hutool-core/src/main/java/org/dromara/hutool/core/spi/KVServiceLoader.java rename to hutool-core/src/main/java/org/dromara/hutool/core/spi/MapServiceLoader.java index e84c2ea0a..ec595dcf5 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/MapServiceLoader.java @@ -20,16 +20,29 @@ import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.util.AccessUtil; import java.nio.charset.Charset; -import java.util.*; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Properties; /** - * 键值对服务加载器,使用{@link Properties}加载并存储服务 + * 键值对服务加载器,使用{@link Properties}加载并存储服务
    + * 服务文件默认位于"META-INF/hutool/"下,文件名为服务接口类全名。 + * + *

    + * 内容类似于: + *

    + *     # 我是注释
    + *     service1 = hutool.service.Service1
    + *     service2 = hutool.service.Service2
    + * 
    + *

    + * 通过调用{@link #getService(String)}方法,传入等号前的名称,即可获取对应服务。 * * @param 服务类型 * @author looly * @since 6.0.0 */ -public class KVServiceLoader extends AbsServiceLoader { +public class MapServiceLoader extends AbsServiceLoader { private static final String PREFIX_HUTOOL = "META-INF/hutool/"; @@ -42,7 +55,7 @@ public class KVServiceLoader extends AbsServiceLoader { * @param serviceClass 服务名称 * @return KVServiceLoader */ - public static KVServiceLoader of(final Class serviceClass) { + public static MapServiceLoader of(final Class serviceClass) { return of(serviceClass, null); } @@ -54,7 +67,7 @@ public class KVServiceLoader extends AbsServiceLoader { * @param classLoader 自定义类加载器, {@code null}表示使用默认当前的类加载器 * @return KVServiceLoader */ - public static KVServiceLoader of(final Class serviceClass, final ClassLoader classLoader) { + public static MapServiceLoader of(final Class serviceClass, final ClassLoader classLoader) { return of(PREFIX_HUTOOL, serviceClass, classLoader); } @@ -67,9 +80,9 @@ public class KVServiceLoader extends AbsServiceLoader { * @param classLoader 自定义类加载器, {@code null}表示使用默认当前的类加载器 * @return KVServiceLoader */ - public static KVServiceLoader of(final String pathPrefix, final Class serviceClass, - final ClassLoader classLoader) { - return new KVServiceLoader<>(pathPrefix, serviceClass, classLoader, null); + public static MapServiceLoader of(final String pathPrefix, final Class serviceClass, + final ClassLoader classLoader) { + return new MapServiceLoader<>(pathPrefix, serviceClass, classLoader, null); } // endregion @@ -85,8 +98,8 @@ public class KVServiceLoader extends AbsServiceLoader { * @param classLoader 自定义类加载器, {@code null}表示使用默认当前的类加载器 * @param charset 编码,默认UTF-8 */ - public KVServiceLoader(final String pathPrefix, final Class serviceClass, - final ClassLoader classLoader, final Charset charset) { + public MapServiceLoader(final String pathPrefix, final Class serviceClass, + final ClassLoader classLoader, final Charset charset) { super(pathPrefix, serviceClass, classLoader, charset); this.serviceCache = new SimpleCache<>(new HashMap<>()); @@ -141,6 +154,7 @@ public class KVServiceLoader extends AbsServiceLoader { return new Iterator() { private final Iterator nameIter = serviceProperties.stringPropertyNames().iterator(); + @Override public boolean hasNext() { return nameIter.hasNext(); @@ -154,6 +168,7 @@ public class KVServiceLoader extends AbsServiceLoader { } // region ----- private methods + /** * 创建服务,无缓存 * 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 index 40998df9c..cdcc05408 100644 --- 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 @@ -25,12 +25,23 @@ public class SpiUtil { /** * 加载第一个可用服务,如果用户定义了多个接口实现类,只获取第一个不报错的服务 * - * @param 接口类型 + * @param 服务类型 * @param clazz 服务接口 * @return 第一个服务接口实现对象,无实现返回{@code null} */ - public static T loadFirstAvailable(final Class clazz) { - final Iterator iterator = loadList(clazz).iterator(); + public static S loadFirstAvailable(final Class clazz) { + return loadFirstAvailable(loadList(clazz)); + } + + /** + * 加载第一个可用服务,如果用户定义了多个接口实现类,只获取第一个不报错的服务 + * + * @param 服务类型 + * @param serviceLoader {@link ServiceLoader} + * @return 第一个服务接口实现对象,无实现返回{@code null} + */ + public static S loadFirstAvailable(final ServiceLoader serviceLoader) { + final Iterator iterator = serviceLoader.iterator(); while (iterator.hasNext()) { try { return iterator.next(); 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 index bc41e33de..edb2c65f8 100644 --- 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 @@ -11,6 +11,10 @@ */ /** - * 服务提供接口SPI(Service Provider interface)机制相关封装 + * 服务提供接口SPI(Service Provider interface)机制相关封装,包括: + *

      + *
    • {@link org.dromara.hutool.core.spi.ListServiceLoader},提供列表形式的服务定义
    • + *
    • {@link org.dromara.hutool.core.spi.MapServiceLoader},提供键值对形式的服务定义
    • + *
    */ package org.dromara.hutool.core.spi; diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/spi/SpiUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/spi/SpiUtilTest.java new file mode 100644 index 000000000..7596bbeb2 --- /dev/null +++ b/hutool-core/src/test/java/org/dromara/hutool/core/spi/SpiUtilTest.java @@ -0,0 +1,69 @@ +/* + * 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.UtilException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class SpiUtilTest { + + @Test + void loadFirstAvailableTest() { + final MapServiceLoader serviceLoader = MapServiceLoader.of(TestSPI1.class); + final TestSPI1 testSPI1 = SpiUtil.loadFirstAvailable(serviceLoader); + Assertions.assertNotNull(testSPI1); + Assertions.assertEquals("test service 1", testSPI1.doSth()); + } + + @Test + void getServiceClassTest() { + final MapServiceLoader serviceLoader = MapServiceLoader.of(TestSPI1.class); + final Class service1 = serviceLoader.getServiceClass("service1"); + Assertions.assertEquals(TestService1.class, service1); + } + + @Test + void getServiceClassNotExistTest() { + final MapServiceLoader serviceLoader = MapServiceLoader.of(TestSPI1.class); + Assertions.assertThrows(UtilException.class, ()->{ + serviceLoader.getServiceClass("service2"); + }); + } + + @Test + void getServiceClassEmptyTest() { + final MapServiceLoader serviceLoader = MapServiceLoader.of(TestSPI1.class); + final Class serviceEmpty = serviceLoader.getServiceClass("serviceEmpty"); + Assertions.assertNull(serviceEmpty); + } + + @Test + void getServiceNotDefineTest() { + final MapServiceLoader serviceLoader = MapServiceLoader.of(TestSPI1.class); + final Class service1 = serviceLoader.getServiceClass("serviceNotDefine"); + Assertions.assertNull(service1); + } + + public interface TestSPI1{ + String doSth(); + } + + public static class TestService1 implements TestSPI1{ + + @Override + public String doSth() { + return "test service 1"; + } + } +} diff --git a/hutool-core/src/test/resources/META-INF/hutool/org.dromara.hutool.core.spi.SpiUtilTest$TestSPI1 b/hutool-core/src/test/resources/META-INF/hutool/org.dromara.hutool.core.spi.SpiUtilTest$TestSPI1 new file mode 100644 index 000000000..6bb02d975 --- /dev/null +++ b/hutool-core/src/test/resources/META-INF/hutool/org.dromara.hutool.core.spi.SpiUtilTest$TestSPI1 @@ -0,0 +1,6 @@ +# 无服务 +serviceEmpty = +# 正常服务 +service1 = org.dromara.hutool.core.spi.SpiUtilTest$TestService1 +# 此服务类未定义 +service2 = org.dromara.hutool.core.spi.SpiUtilTest$TestService2