跳转到内容

配置与数据目录

配置类是 config.dev.znlp.ZnlpSystemConfig,默认实例由 SystemDefaultConfig 在类静态块中初始化,加载链是:

  1. 从 classpath 读取 znlp.properties
  2. 用 JVM 系统属性覆盖其中的值

系统属性名格式为 znlp.{kebab-case},例如 -Dznlp.data-dir=/path/to/data。

主库自带一份默认配置,随 jar 发布在 znlp/src/main/resources/znlp.properties:

data-dir=data,data/dat,../data/dat
cache-dir=data/cache
auto-download-resource=true
配置项 系统属性 类型 默认值 说明
data-dir znlp.data-dir 逗号分隔的多路径 data,data/dat,../data/dat 数据目录,按顺序查找
cache-dir znlp.cache-dir 路径 data/cache 派生缓存目录
auto-download-resource znlp.auto-download-resource boolean true 资源缺失时是否尝试自动下载
disabled-modules znlp.disabled-modules 逗号分隔的模块名 无 禁用指定模块

data-dir 有特殊处理:字段名是 dataDirs(复数,List<String>),配置时写单数的 data-dir,逗号分隔会被拆成列表。getDataDir() 返回列表的第一个,getDataDirs() 返回全部。

import config.dev.znlp.ZnlpSystemConfig;
ZnlpSystemConfig config = ZnlpSystemConfig.builder()
.dataDir("/opt/znlp/data")
.dataDirs(List.of("/opt/data", "/mnt/shared")) // 或逗号分隔字符串
.cacheDir("/var/cache/znlp")
.autoDownloadResource(false)
.disabledModules("highlight,converter")
.build(); // 未设置的项为 null
// .buildWithDefaults() // 未设置的项从 SystemDefaultConfig 继承

build() 和 buildWithDefaults() 的区别很重要:前者未设置项保持 null,后者从系统默认配置继承。

ZnlpSystem.builder() 返回的 DefaultZnlpSystemBuilder 是包私有类,它的 build() 方法也是包私有,而且这个 builder 没有任何 withXxx 配置方法——只有两个构造器(默认取 SystemDefaultConfig.getInstance(),或接收一个 ZnlpSystemConfig)。

结果是:包外代码无法通过 ZnlpSystem.builder()...build() 构建自定义系统实例。实际可用的定制手段只有:

  • JVM 系统属性(-Dznlp.*)
  • classpath 上放自己的 znlp.properties 覆盖 jar 内的默认值
  • SystemDefaultConfig.reset()(主要供测试用,会重新加载配置)

ZnlpSystem 本身是一个 record:public record ZnlpSystem(ServiceRegistry registry)。

ZnlpSystem.getInstance() 是进程级单例(静态 Holder 惰性初始化)。构建流程是:new ServiceRegistry → 注册 ZnlpSystemConfig 单例 → ModuleLoader.loadModules(registry, config)。

NlpResourceLoader 为每个存在的 dataDir 依次挂载两个 factory,最后加 classpath factory:

本地文件(dataDir 目录)
↓ 未命中
本地 Jar(dataDir 下的 jar 内查找)
↓ 未命中
Classpath(znlp-resources jar)

即 本地文件 > 本地 Jar > Classpath。资源加载后按 path:charset 缓存,同一资源不会重复读取。

这个顺序给了你一个覆盖内置资源的干净手段:把同名文件放到 dataDir 里,就会优先于 jar 内的版本被加载。

ModuleLoader 扫描 classpath 中所有 META-INF/znlp-modules.idx,按 SystemModule.getPriority()(数值越小越早加载)依次加载,并根据 disabledModules 跳过被禁用的模块。

主库 znlp-modules.idx 的内容:

模块类 优先级 注册的服务
CoreModule(名 core) 10 NlpResourceLoader
SegmentModule — CoreDictionarySplitAlgorithm、CoreDictionary、BiGramTableDictionary、PersonNamePerceptron、CwsPerceptron、PosPerceptron、NERPerceptron、CwsSplitAlgorithm、ResourceUserDictionary、AtomSplitAlgorithm
PinyinModule — PinyinDict、PinyinSplitPerceptron、PinyinService
HighlightModule — SmartHighlighter
TransModule — TransformService

disabled-modules 的值是模块名(如 core),不是类名。禁用 PinyinModule 会连带影响依赖拼音的智能高亮。

service.base.dev.znlp.ServiceRegistry 实现 ServiceLocator,是基于接口的轻量依赖注入容器。注册 API:

registerFactory(Class, ServiceFactory)
registerFactory(Class, ServiceFactory, boolean singleton)
registerFactory(Class, Class<? extends ServiceFactory>, boolean)
registerSingleton(Class, T)
registerClass(Class)
registerSingletonClass(Class)
registerPrototypeClass(Class)
registerClass(Class, boolean)
<T> T getService(Class) // 支持继承/接口智能查找,找不到抛 ServiceNotFoundException

支持父子容器:new ServiceRegistry(ServiceLocator parent),子容器查不到时会向上委托。

取服务:

PosPerceptron pos = Znlp.getService(PosPerceptron.class);

Znlp.getService 委托给进程级 ZnlpSystem,返回的是注册表里的单例,可以放心持有。