CLAUDE.md 6.1 KB

SPI 模块

模块概述

SPI 模块是服务发现和依赖注入框架,基于 Java SPI (Service Provider Interface) 机制实现。该模块提供了一个自定义的服务加载器 WeNextServiceLoader,用于动态发现和加载服务实现类,支持模块化开发和插件化架构。

主要功能

1. 服务发现机制

  • WeNextServiceLoader: 自定义服务加载器
  • 基于 META-INF/services/ 配置文件进行服务发现
  • 支持懒加载和缓存机制
  • 提供详细的错误日志和异常处理

2. 动态服务加载

  • 运行时动态发现服务实现
  • 支持多个服务实现的加载
  • 自动类型检查和转换
  • 线程安全的服务实例管理

3. 配置文件解析

  • 标准 SPI 配置文件格式支持
  • 注释行过滤 (# 开头)
  • 类名合法性验证
  • 重复服务检测

4. 错误处理和日志

  • 详细的错误信息记录
  • 类加载异常处理
  • 类型不匹配检测
  • 实例化异常捕获

核心类和接口

WeNextServiceLoader
class WeNextServiceLoader<S> private constructor(
    private val service: Class<S>, 
    cl: ClassLoader?
) : Iterable<S?> {
    
    // 加载服务
    companion object {
        fun <S> load(service: Class<S>, loader: ClassLoader?): WeNextServiceLoader<S>
        fun <S> load(service: Class<S>): WeNextServiceLoader<S>
        fun <S> getFirst(service: Class<S>): S?
    }
    
    // 重新加载服务
    fun reload()
    
    // 获取第一个服务实例
    fun getFirst(): S?
    
    // 迭代器支持
    override fun iterator(): MutableIterator<S?>
}

LazyIterator

private inner class LazyIterator(
    val service: Class<S>, 
    val loader: ClassLoader?
) : MutableIterator<S?> {
    
    private fun hasNextService(): Boolean
    private fun nextService(): S?
    
    override fun hasNext(): Boolean
    override fun next(): S?
}

使用方式

1. 定义服务接口

interface IHello {
    fun hello()
}

2. 实现服务类

// 默认实现
class AHello : IHello {
    override fun hello() {
        Log.d("Hello", "AHello")
    }
}

// 带键的实现
class BHello : IHello {
    override fun hello() {
        Log.d("Hello", "BHello")
    }
}

3. 配置服务文件

src/main/resources/META-INF/services/ 目录下创建配置文件:

文件名: com.example.IHello

# 默认实现
com.example.AHello
# 键值实现  
com.example.BHello

4. 加载和使用服务

// 加载单个服务
val helloService = WeNextServiceLoader.getFirst(IHello::class.java)
helloService?.hello()

// 加载所有服务实现
val serviceLoader = WeNextServiceLoader.load(IHello::class.java)
serviceLoader.forEach { service ->
    service?.hello()
}

// 自定义 ClassLoader
val customLoader = MyCustomClassLoader()
val loader = WeNextServiceLoader.load(IHello::class.java, customLoader)

5. 迭代所有服务

val serviceLoader = WeNextServiceLoader.load(IHello::class.java)

// 使用迭代器
val iterator = serviceLoader.iterator()
while (iterator.hasNext()) {
    val service = iterator.next()
    service?.hello()
}

// 使用 for-each
for (service in serviceLoader) {
    service?.hello()
}

配置文件格式

标准格式

# 注释行,以 # 开头
com.example.service.ServiceImpl1
com.example.service.ServiceImpl2

# 另一个注释
com.example.service.ServiceImpl3

格式要求

  • 每行一个服务实现类的全限定名
  • 支持注释行 (# 开头)
  • 类名必须符合 Java 标识符规范
  • 不允许空格和制表符
  • 重复的类名会被忽略

工作机制

1. 服务发现流程

  1. 根据服务接口类名构造配置文件路径: META-INF/services/{接口全限定名}
  2. 使用 ClassLoader 获取所有匹配的配置文件
  3. 解析配置文件,提取服务实现类名
  4. 验证类名格式和合法性
  5. 加载类并验证类型匹配
  6. 创建服务实例并缓存

2. 懒加载机制

  • 配置文件在首次访问时解析
  • 服务实例在迭代时按需创建
  • 已创建的实例会被缓存复用
  • 支持重新加载配置

3. 缓存策略

private val providers = LinkedHashMap<String, S>() // 实例缓存
private var lookupIterator: LazyIterator? = null   // 懒加载迭代器

4. 错误处理

  • 配置文件读取异常
  • 类未找到异常
  • 类型不匹配异常
  • 实例化异常
  • 所有异常都会记录详细日志

与 Java SPI 的区别

相似点

  • 都使用 META-INF/services/ 配置文件
  • 都支持服务接口和实现的解耦
  • 都提供迭代器访问方式

不同点

  • 错误处理: WeNextServiceLoader 提供更详细的错误日志
  • null 处理: 支持返回 null 值,不抛出异常
  • 缓存机制: 提供实例缓存,避免重复创建
  • Android 优化: 针对 Android 环境进行了优化

性能考虑

1. 懒加载优势

  • 配置文件按需解析,减少启动时间
  • 服务实例按需创建,节省内存
  • 支持大量服务实现的场景

2. 缓存机制

  • 已解析的配置信息缓存
  • 已创建的服务实例缓存
  • 避免重复的反射操作

3. 内存管理

  • 使用 LinkedHashMap 保持插入顺序
  • 合理的缓存大小控制
  • 支持显式重新加载

依赖关系

编译时依赖

  • androidx.core.ktx - Android 核心扩展
  • 无其他外部依赖

被依赖关系

  • frame:router - 路由模块使用 SPI 进行服务发现
  • 其他需要动态服务发现的模块

构建配置

implementation "com.wenext.android:frame-spi:6.0.0"

注意事项

  1. 配置文件位置: 必须在 META-INF/services/ 目录下
  2. 类名格式: 必须使用完整的类全限定名
  3. 类型匹配: 实现类必须实现或继承指定的服务接口
  4. 线程安全: WeNextServiceLoader 不是线程安全的,多线程使用需要外部同步
  5. ClassLoader: 默认使用当前线程的 ContextClassLoader
  6. 异常处理: 服务加载失败不会抛出异常,会返回 null 并记录日志
  7. 性能影响: 首次加载会有一定开销,建议在适当时机预加载关键服务
  8. 配置文件编码: 配置文件必须使用 UTF-8 编码