This commit is contained in:
Looly 2023-04-16 01:34:41 +08:00
parent 3b53230a38
commit bc9d665036
6 changed files with 132 additions and 16 deletions

View File

@ -41,6 +41,16 @@ import java.util.List;
* <li>提供更加灵活的服务加载机制当选择加载指定服务时其它服务无需加载</li>
* </ul>
*
* <p>
* 服务文件默认位于"META-INF/services/"文件名为服务接口类全名内容类似于
* <pre>
* # 我是注释
* hutool.service.Service1
* hutool.service.Service2
* </pre>
* <p>
* 通过调用{@link #getService(int)}方法传入序号即可获取对应服务
*
* @param <S> 服务类型
* @author looly
* @since 6.0.0
@ -145,7 +155,7 @@ public class ListServiceLoader<S> extends AbsServiceLoader<S> {
*/
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<S> extends AbsServiceLoader<S> {
public Iterator<S> iterator() {
return new Iterator<S>() {
private final Iterator<String> nameIter = serviceNames.iterator();
@Override
public boolean hasNext() {
return nameIter.hasNext();
@ -275,7 +286,7 @@ public class ListServiceLoader<S> extends AbsServiceLoader<S> {
*/
private S createService(final String serviceClassName) {
return AccessUtil.doPrivileged(() ->
ConstructorUtil.newInstance(ClassLoaderUtil.loadClass(serviceClassName)),
ConstructorUtil.newInstance(ClassLoaderUtil.loadClass(serviceClassName)),
this.acc);
}

View File

@ -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}加载并存储服务<br>
* 服务文件默认位于"META-INF/hutool/"文件名为服务接口类全名
*
* <p>
* 内容类似于
* <pre>
* # 我是注释
* service1 = hutool.service.Service1
* service2 = hutool.service.Service2
* </pre>
* <p>
* 通过调用{@link #getService(String)}方法传入等号前的名称即可获取对应服务
*
* @param <S> 服务类型
* @author looly
* @since 6.0.0
*/
public class KVServiceLoader<S> extends AbsServiceLoader<S> {
public class MapServiceLoader<S> extends AbsServiceLoader<S> {
private static final String PREFIX_HUTOOL = "META-INF/hutool/";
@ -42,7 +55,7 @@ public class KVServiceLoader<S> extends AbsServiceLoader<S> {
* @param serviceClass 服务名称
* @return KVServiceLoader
*/
public static <S> KVServiceLoader<S> of(final Class<S> serviceClass) {
public static <S> MapServiceLoader<S> of(final Class<S> serviceClass) {
return of(serviceClass, null);
}
@ -54,7 +67,7 @@ public class KVServiceLoader<S> extends AbsServiceLoader<S> {
* @param classLoader 自定义类加载器, {@code null}表示使用默认当前的类加载器
* @return KVServiceLoader
*/
public static <S> KVServiceLoader<S> of(final Class<S> serviceClass, final ClassLoader classLoader) {
public static <S> MapServiceLoader<S> of(final Class<S> serviceClass, final ClassLoader classLoader) {
return of(PREFIX_HUTOOL, serviceClass, classLoader);
}
@ -67,9 +80,9 @@ public class KVServiceLoader<S> extends AbsServiceLoader<S> {
* @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);
public static <S> MapServiceLoader<S> of(final String pathPrefix, final Class<S> serviceClass,
final ClassLoader classLoader) {
return new MapServiceLoader<>(pathPrefix, serviceClass, classLoader, null);
}
// endregion
@ -85,8 +98,8 @@ public class KVServiceLoader<S> extends AbsServiceLoader<S> {
* @param classLoader 自定义类加载器, {@code null}表示使用默认当前的类加载器
* @param charset 编码默认UTF-8
*/
public KVServiceLoader(final String pathPrefix, final Class<S> serviceClass,
final ClassLoader classLoader, final Charset charset) {
public MapServiceLoader(final String pathPrefix, final Class<S> 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<S> extends AbsServiceLoader<S> {
return new Iterator<S>() {
private final Iterator<String> nameIter =
serviceProperties.stringPropertyNames().iterator();
@Override
public boolean hasNext() {
return nameIter.hasNext();
@ -154,6 +168,7 @@ public class KVServiceLoader<S> extends AbsServiceLoader<S> {
}
// region ----- private methods
/**
* 创建服务无缓存
*

View File

@ -25,12 +25,23 @@ public class SpiUtil {
/**
* 加载第一个可用服务如果用户定义了多个接口实现类只获取第一个不报错的服务
*
* @param <T> 接口类型
* @param <S> 服务类型
* @param clazz 服务接口
* @return 第一个服务接口实现对象无实现返回{@code null}
*/
public static <T> T loadFirstAvailable(final Class<T> clazz) {
final Iterator<T> iterator = loadList(clazz).iterator();
public static <S> S loadFirstAvailable(final Class<S> clazz) {
return loadFirstAvailable(loadList(clazz));
}
/**
* 加载第一个可用服务如果用户定义了多个接口实现类只获取第一个不报错的服务
*
* @param <S> 服务类型
* @param serviceLoader {@link ServiceLoader}
* @return 第一个服务接口实现对象无实现返回{@code null}
*/
public static <S> S loadFirstAvailable(final ServiceLoader<S> serviceLoader) {
final Iterator<S> iterator = serviceLoader.iterator();
while (iterator.hasNext()) {
try {
return iterator.next();

View File

@ -11,6 +11,10 @@
*/
/**
* 服务提供接口SPIService Provider interface机制相关封装
* 服务提供接口SPIService Provider interface机制相关封装包括
* <ul>
* <li>{@link org.dromara.hutool.core.spi.ListServiceLoader}提供列表形式的服务定义</li>
* <li>{@link org.dromara.hutool.core.spi.MapServiceLoader}提供键值对形式的服务定义</li>
* </ul>
*/
package org.dromara.hutool.core.spi;

View File

@ -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<TestSPI1> 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<TestSPI1> serviceLoader = MapServiceLoader.of(TestSPI1.class);
final Class<TestSPI1> service1 = serviceLoader.getServiceClass("service1");
Assertions.assertEquals(TestService1.class, service1);
}
@Test
void getServiceClassNotExistTest() {
final MapServiceLoader<TestSPI1> serviceLoader = MapServiceLoader.of(TestSPI1.class);
Assertions.assertThrows(UtilException.class, ()->{
serviceLoader.getServiceClass("service2");
});
}
@Test
void getServiceClassEmptyTest() {
final MapServiceLoader<TestSPI1> serviceLoader = MapServiceLoader.of(TestSPI1.class);
final Class<TestSPI1> serviceEmpty = serviceLoader.getServiceClass("serviceEmpty");
Assertions.assertNull(serviceEmpty);
}
@Test
void getServiceNotDefineTest() {
final MapServiceLoader<TestSPI1> serviceLoader = MapServiceLoader.of(TestSPI1.class);
final Class<TestSPI1> 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";
}
}
}

View File

@ -0,0 +1,6 @@
# 无服务
serviceEmpty =
# 正常服务
service1 = org.dromara.hutool.core.spi.SpiUtilTest$TestService1
# 此服务类未定义
service2 = org.dromara.hutool.core.spi.SpiUtilTest$TestService2