Skip to content

About

A Kotlin Multiplatform service registration and discovery library.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

service-center

一个服务注册 & 发现的 Kotlin Multiplatform 库。

  • 注解 + KSP 代码生成,注册表本身无反射(Native / JS / Wasm 可用)
  • 运行时核心操作:setService / getService,外加 initialize / install 注册入口
  • 生成代码进入 commonMain(kspCommonMainMetadata),所有平台共享同一份注册代码
  • JVM / Android 手写 FastServiceLoader 解析生成的 META-INF/services 资源自动发现注册对象(跳过 JAR 校验,异常时回退 ServiceLoader)

设计说明(原始需求)

由以下组成

  1. annotation module
  2. annotation processor model

生成对应的 annotation 代码,连接到 service center:

ServiceCenter.setService(IFoo.class, FooImpl.class)

  1. service center

负责 service 注册、发现的核心:setService, getService

对应到本仓库(KMP 下没有 Java annotation processing,代码生成使用 KSP;IFoo.class 的 Kotlin 写法为 IFoo::class):

需求 模块
annotation module service-center-annotations(@Service、IService)
annotation processor model service-center-compiler(KSP 处理器,生成注册代码)
service center service-center(注册表运行时)

模块

service-center/              # 运行时:ServiceCenter、ServiceRegistrar、异常
service-center-annotations/  # @Service、IService
service-center-compiler/     # KSP 处理器,生成 <Name>Generated 注册类与 META-INF/services 资源
sample/                      # 集成示例与测试(含 KSP 接线)
sample-android/              # 可运行的 Android app,消费 sample,演示 initialize()/getService()
logging/                     # 应用层日志:以 Maven 依赖引入 Loga(jvm/android/ios)

快速开始

1. 依赖与 KSP 接线

plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.ksp)
}

kotlin {
    sourceSets {
        commonMain {
            kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
            resources.srcDir("build/generated/ksp/metadata/commonMain/resources")
            dependencies {
                implementation("io.github.xechoz:service-center:0.0.1")
            }
        }
    }
}

dependencies {
    add("kspCommonMainMetadata", "io.github.xechoz:service-center-compiler:0.0.1")
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask<*>>().configureEach {
    if (name != "kspCommonMainKotlinMetadata") {
        dependsOn("kspCommonMainKotlinMetadata")
    }
}

tasks.matching { it.name.startsWith("ksp") && it.name != "kspCommonMainKotlinMetadata" }.configureEach {
    dependsOn("kspCommonMainKotlinMetadata")
}

// 生成的 META-INF/services 资源需先由 KSP 产出
tasks.withType<org.gradle.language.jvm.tasks.ProcessResources>().configureEach {
    dependsOn("kspCommonMainKotlinMetadata")
}

tasks.matching { it.name.endsWith("JavaRes") }.configureEach {
    dependsOn("kspCommonMainKotlinMetadata")
}

可选 KSP 参数:

ksp {
    arg("serviceCenter.generatedPackage", "com.example.generated")
    arg("serviceCenter.generatedName", "ServiceCenterGenerated")
}

2. 声明服务

// commonMain
import me.xechoz.servicecenter.IService
import me.xechoz.servicecenter.Service

interface Greeter : IService {
    fun greet(): String
}

@Service
class GreeterImpl : Greeter {
    override fun greet(): String = "hello"
}

@Service 类必须实现 IService 的一个子接口,注册键就是该子接口;查表同样通过子接口:

生成(包名与类名可通过 KSP 参数覆盖):

public class ServiceCenterGenerated : ServiceRegistrar {
    override fun register() {
        ServiceCenter.setService(Greeter::class) { GreeterImpl() }
    }
}

生成类是 public 无参构造;同时生成 META-INF/services/me.xechoz.servicecenter.ServiceRegistrar(内容为生成类全名),JVM / Android 上由库内置的加载器按名实例化,因此无需引用任何生成类。加载器直接读取该资源并跳过 JAR 校验(避免 Android 上读整包做签名校验的卡顿),仅在异常时才回退 ServiceLoader。

3. 初始化与使用(JVM / Android)

fun main() {
    ServiceCenter.initialize() // 发现并注册 classpath 上所有模块的注册对象,可重复调用

    val greeter = ServiceCenter.getService<Greeter>()
    println(greeter.greet())
}

多模块时不需要逐个列出生成对象,initialize() 一次发现全部。

JS / Wasm / Native 没有可靠的自动发现机制,仍按模块显式 install:

ServiceCenter.install(AppGenerated(), DataGenerated())

install 按 registrar 类型幂等,重复安装同一个 registrar 会跳过。

行为约定

项目 行为
绑定类型 @Service 类必须实现 IService 的一个子接口,注册键为该直接父接口(规则见下)
生命周期 懒加载单例:首次 getService 创建并缓存
缺失服务 getService 抛 ServiceNotFoundException(消息含已注册类型列表);getServiceOrNull 返回 null
自动加载 JVM / Android 调用一次 ServiceCenter.initialize(),经手写的 FastServiceLoader 解析 META-INF/services 资源(失败回退 ServiceLoader)发现并注册所有模块的注册对象;其他平台为 no-op,需显式 install
重复注册 两个不同 registrar 绑定同一服务类型时抛 DuplicateServiceException;同一 registrar 重复 install / initialize 幂等跳过
触发时机 不做惰性触发,getService 不会自动初始化;由使用方显式 initialize() 或 install(...)
测试支持 ServiceCenter.reset() 清空注册表、已安装 registrar 与初始化标记
线程安全 注册表使用 atomicfu 写时复制;单例创建使用 lazy(SYNCHRONIZED)

绑定规则:

@Service 类的直接父类型 行为
恰好一个 IService 子接口(如 IFooService : IService) 绑定该接口:getService<IFooService>()
同时直接写 IService + 一个子接口 绑定子接口(直接写的 IService 不计入)
只有 IService 本身,或没有父类型 编译报错:必须实现 IService 的子接口
多个 IService 子接口(如 Store、Cache) 编译报错:只允许一个服务接口
非 IService 体系的接口(如 Closeable) 忽略,不参与绑定
父类型是类而非接口 编译报错:只支持接口

@Service 类的约束(KSP 编译期报错,带文件位置):

  • 只能是 class 或 object,且必须是可访问(public/internal)的具体类型
  • class 必须有可访问的无参构造(允许全部参数带默认值);不支持构造注入
  • 不能是 inner / expect / abstract / sealed

支持平台

平台 状态
jvm 测试通过
android 测试通过(host test)、主源码编译通过
iosArm64 / iosSimulatorArm64 / iosX64 klib 编译通过(Linux 交叉编译;完整测试需 macOS)
linuxX64 测试通过
mingwX64 klib 编译通过(测试需 Windows/wine)
macosArm64 klib 编译通过(测试需 macOS)
js / wasmJs Node 测试通过

已知限制

  • 自动加载(initialize())只在 JVM / Android 生效;JS / Wasm / Native 需显式 install
  • 自动发现按名加载注册类,Android release(R8)依赖库内置的 consumer keep 规则保留生成类(service-center/consumer-rules.pro,经 KMP optimization.consumerKeepRules 发布到 AAR)
  • 只处理 commonMain 中的 @Service;target 源集(如 iosMain)中的声明不会被处理
  • 注册键为 KClass,Repo<User> 与 Repo<Order> 无法区分
  • 无构造注入、作用域、限定符、多绑定;每个 @Service 类只允许一个服务接口
  • 同一编译单元内多个类绑定到同一类型会在编译期报错

构建与测试

./gradlew :service-center:jvmTest :sample:jvmTest
./gradlew :service-center:testAndroidHostTest :sample:testAndroidHostTest
./gradlew :service-center:linuxX64Test :sample:linuxX64Test
./gradlew :service-center:jsNodeTest :sample:jsNodeTest
./gradlew :service-center:wasmJsNodeTest :sample:wasmJsNodeTest

sample-android 是可运行的 Android app(Compose + Material3),启动时调用 ServiceCenter.initialize() 并从 sample 的 AAR 中自动发现生成的 registrar:

./gradlew :sample-android:assembleDebug
adb install -r sample-android/build/outputs/apk/debug/sample-android-debug.apk

sample 模块即集成验证:ServiceCenterGenerated 由 service-center-compiler 在构建中真实生成,测试覆盖接口绑定、object、默认参数构造、单例、缺失与重复注册。

发布使用 vanniktech maven-publish(publishToMavenCentral + signAllPublications),版本号由 gradle.properties 中的 servicecenter.version 控制。

后续优化方向

  1. Gradle 插件(service-center-gradle-plugin)自动完成 KSP 接线,消费方只加一行插件
  2. Native 零显式初始化:@EagerInitialization(JVM/Android 已由 initialize() + FastServiceLoader 实现)
  3. 编译期图校验:循环依赖、缺失绑定、重复绑定诊断
  4. 限定符(@Named)、作用域(Singleton/Factory)、@Provides 模块与 Set/Map 多绑定
  5. ServiceCenter.describe() 注册表 dump;异常附候选类型建议
  6. binary-compatibility-validator 与 GitHub Actions 多平台 CI(ubuntu:jvm/linuxX64/js,macos:iOS)

About

A Kotlin Multiplatform service registration and discovery library.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages