Struct Util 权威指南 - 通过 SPI 实现自定义扩展

1 分钟阅读

Struct Util 是一个 Java 语言开发的结构化数据映射处理工具。Struct Util 主要解决两个方面的问题。第一个方面将*.xls, *.csv 等配置友好型数据源转换为业务侧友好型的 bean 结构,对配置数据和使用数据进行解耦,让开发和运营、策划三方实现共赢。第二方面解决了数据表热重载,数据有条件过滤,表结构跨表引用等等应用相关的问题。

StructUtil是博主个人作品, 稍微有自吹自擂的嫌疑, 欢迎:star:收藏。哈哈, 因为是个人作品,应该是足够”权威”了。嘻嘻~~

SPI 扩展

Struct Util 提供了 Converters, Converter, StructFactoryBean, StructHandler四个扩展点,允许用户进行功能扩展。

EnhancedServiceLoader

相比于 JDK 内置的 SPI 功能解决了加载异常处理,懒加载等问题。核心加载代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void handleDefinitionFile(String dir, ClassLoader classLoader, List<ExtensionDefinition> output) throws IOException {
    String fileName = dir + this.service.getName();
    Enumeration<URL> urls = classLoader != null
            ? classLoader.getResources(fileName)
            : ClassLoader.getSystemResources(fileName);
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) {
            String line = null;
            while (null != (line = reader.readLine())) {
                line = line.trim();
                if (line.length() > 0) {
                    try {
                        output.add(this.createExtensionDefinition(line, classLoader));
                    } catch (LinkageError | ClassNotFoundException e) {
                        LOGGER.warn("load [{}] class failure. {}", line, e.getMessage());
                    }
                }
            }
        } catch (Throwable e) {
            LOGGER.warn("handle extension definition file error.", e);
        }
    }
}

ExtensionDefinition createExtensionDefinition(String clzName, ClassLoader loader) throws ClassNotFoundException {
    Class<?> clzOfService = Class.forName(clzName, true, loader);
    String name = null;
    int order = 0;
    SPI anno = AnnotationUtils.findAnnotation(SPI.class, clzOfService);
    if (anno != null) {
        name = anno.name();
        order = anno.order();
    }
    return new ExtensionDefinition(name, clzOfService, order);
}

自定义 Converter

Struct Util内置了常用的类型转换器, 当用户有特殊转换需求时,可以通过扩展 Converter 接口实现转换业务,并通过 SPI 机制自动注册到系统中。以下是一个简单的字符串转数组的转换器实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class StringToArrayConverter implements Converter {

    @Override
    public Object convert(Object originValue, Class<?> targetType) {
        if (!targetType.isArray() || String.class != originValue.getClass()) {
            return null;
        }
        String content = (String) originValue;
        Class<?> componentType = targetType.getComponentType();
        String[] data = content.split(separator);
        if (exceptBlank) {
            data = Arrays.stream(data)
                    .map(String::trim)
                    .filter(s -> !s.isEmpty())
                    .toArray(String[]::new);
        }
        Object array = Array.newInstance(componentType, data.length);
        for (int i = 0; i < data.length; i++) {
            Array.set(array, i, ConverterUtil.covert(data[i], componentType));
        }
        return array;
    }
}

知识共享许可协议

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 TinyZ Zzh (包含链接: https://tinyzzh.github.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。 如有任何疑问,请 与我联系 (tinyzzh815@gmail.com)

TinyZ Zzh

TinyZ Zzh

专注于高并发服务器、网络游戏相关(Java、PHP、Unity3D、Unreal Engine等)技术,热爱游戏事业, 正在努力实现自我价值当中。

评论

  点击开始评论...