Skip to content

Rain 扩展点总览

Rain 不只是提供注入、事件和任务等现成功能。它更重要的用途,是让框架开发者把新的注解、新的组件类型、新的调用协议和新的运行时能力接入同一套扫描、容器与生命周期。

本页先回答三个问题:扩展点在哪里、何时执行、应该选择哪一个。具体模块 API 仍以各模块页面为准。

生态项目还有面向下游组件作者的专门指南:

扩展点地图

扩展点所在模块执行阶段适合解决的问题
Moduleapplication类扫描前提前注册框架基础设施或修改启动环境
ClassRegisterapplication扫描每个类时建立全局类索引、收集模型元数据
@LoadBy + Loaderapi/application扫描完成后、服务启动前实现注解驱动或接口驱动的模块发现
ApplicationServiceapi/applicationLoader 完成后启动和停止线程池、服务器、连接等资源
@AutoBindapi/application扫描期间把接口的实现自动绑定到容器
BeanFactory<T>di/application扫描期间注册,取 Bean 时创建接管某一类对象的创建方式
DataReaderFactorydi参数和字段读取时为新的泛型容器或数据来源提供读取规则
ProcessProvidercontrollerController 加载时创建过程实现鉴权、审计、事务等调用过程
ProcessFiltercontrollerController 加载时筛选过程控制某个过程是否作用于某个 Action
ControllerLoadercontrollerLoader 阶段基于 Controller 内核实现新的协议框架
NextTimejob每次计算下次执行时间时实现新的任务时间策略
EventBusapi/event运行期替换事件分发实现
ClassTransformerclassloader类定义前修改任意待加载类的字节码
@EnchantBy + Enchanterclassloader类定义前以注解声明局部字节码增强
@HookBy + HookRunnablehook增强后方法调用时实现方法级前置、后置和异常逻辑

这些扩展点不是同一层级。优先使用更高层的协议:能用 Loader 完成发现就不要写 ClassTransformer,能用 ProcessProvider 完成调用拦截就不要直接改 Controller 内核。

启动时序中的位置

text
ConfigImpl.init
  └─ 合并 conf/module、conf、conf/{runMode}
ContextImpl.init
  └─ 建立配置读取与 Bean 创建能力
Module.onLoad
  └─ 扫描前的框架初始化
AppLoader 扫描 rain.scanPackages
  ├─ @AutoBind
  ├─ BeanFactory<T>
  ├─ ClassRegister.register
  ├─ @LoadBy 搜索
  └─ ApplicationService 类型收集
Loader.load
  └─ 按 priority 从小到大执行
ApplicationService.start
  └─ 按 priority 从小到大执行
AppStatusEvent.AppStarted

由此可以确定职责边界:

  • Module 能在扫描前工作,但它必须通过 rain.modules 显式配置。
  • ClassRegister 会看见扫描范围中的每个类,不应执行网络访问或重型初始化。
  • Loader 负责把静态声明变成运行时注册,不应长期持有后台任务。
  • ApplicationService 负责资源生命周期,不应重新实现类型发现。
  • ClassTransformer 发生得更早:目标类一旦已由父加载器定义,就无法再通过当前加载过程增强。

首选扩展:Loader

Loader 是 Rain 模块最常用的发现协议。框架开发者定义一种声明方式,再让 Loader 批量处理扫描结果。

kotlin
@LoadBy(CommandLoader::class)
@Target(AnnotationTarget.CLASS)
annotation class Command(val name: String)

class CommandLoader(
    private val context: DiContext,
    private val registry: CommandRegistry,
) : Loader {
    override fun priority() = 20

    override fun load(items: Collection<LoadItem>) {
        items.forEach { item ->
            val annotation = item.annotation as Command
            val handler = context.getBean(item.clazz)
                ?: error("无法创建命令处理器: ${item.clazz.name}")
            registry.register(annotation.name, handler)
        }
    }
}

LoadItem 的三个核心字段含义不同:

  • clazz:最终被发现的业务类。
  • target:触发 @LoadBy 的注解、接口、父类或类型本身。
  • annotation:业务类上实际出现的功能注解;通过接口或父类触发时可能为空。

@LoadBy.mastBean 保留了源码中的拼写。默认值为 true,表示只接收 Rain 判断为具体 Bean 的类;扩展接口、抽象基类或注解类型本身时,需要明确评估是否设为 false

扫描所有类:ClassRegister

当需求与某个功能注解无关,而是必须观察每个扫描类时,使用 ClassRegister

kotlin
class ModelIndex : ClassRegister {
    private val models = mutableListOf<Class<*>>()

    override fun register(clazz: Class<*>) {
        if (clazz.isAnnotationPresent(Entity::class.java)) {
            models += clazz
        }
    }

    fun all(): List<Class<*>> = models.toList()
}

然后通过模块默认配置或应用配置注册:

properties
rain.classRegisters=com.example.framework.ModelIndex

配置项会合并为数组,因此多个依赖可以各自贡献一个 Register。Register 的实例由容器创建,可以注入其他轻量服务。

扫描前初始化:Module

Module.onLoad() 是扫描前入口:

kotlin
class CommandModule(
    private val context: DiContext,
) : Module {
    override fun onLoad() {
        context.putBean(CommandRegistry::class.java, CommandRegistry())
    }
}

注册方式:

properties
rain.modules=com.example.framework.CommandModule

如果类型本身可以在扫描后由容器正常创建,就不要为了“模块感”额外使用 Module。它适合真正必须先于扫描完成的初始化。

资源生命周期:ApplicationService

Loader 注册完元数据后,ApplicationService 才开始启动:

kotlin
class CommandServer(
    private val registry: CommandRegistry,
) : ApplicationService {
    override fun priority() = 50

    override fun start() {
        registry.open()
    }

    override fun stop() {
        registry.close()
    }
}

当前实现按优先级升序启动,也按同一顺序停止,并非反向关闭。存在资源依赖时,不要假设高优先级服务会先停止;应由一个聚合服务显式管理内部资源顺序。

对象创建:AutoBind 与 BeanFactory

接口只需要实现选择时,优先用 @AutoBind

kotlin
@AutoBind
interface Serializer

@Named("json")
class JsonSerializer : Serializer

目标对象不能由普通构造器创建时,使用 BeanFactory<T>。Rain 会从工厂的泛型参数识别目标类型,并建立专用 ClassContext

Controller 扩展层级

Controller 模块提供三层扩展能力:

  1. ProcessProvider:为现有协议增加 Before、After、Catch 过程,最常用。
  2. ProcessFilter:在加载阶段判断某个过程是否应用于 Action。
  3. ControllerLoader:实现新的路由和 Action 调用协议,适合 Web、RPC、消息消费等框架作者。

实现鉴权、限流、审计时先选择 ProcessProvider。只有调用上下文、路由树和 ActionInvoker 都不同,才继承 ControllerLoader

字节码扩展层级

类增强也分三层:

  1. @HookBy:业务方法级拦截,优先选择。
  2. @EnchantBy:对带功能注解的类或方法执行 ASM 修改。
  3. ClassTransformer:观察所有通过 AppClassloader 定义的候选类。

Transformer 必须在目标类加载前注册,且只能在 FullStackApplicationLauncher 的类加载边界内生效。扩展代码还要避免同时从父加载器与 AppClassloader 引用同一实现类,否则会出现“类名相同但类型不同”的转换错误。

如何选择

需求首选方案
扫描自定义注解并注册处理器@LoadBy + Loader
收集所有实体或模型ClassRegister
启动服务器或连接池ApplicationService
为接口发现多个实现@AutoBind
从外部系统创建对象BeanFactory<T>
给 Action 添加鉴权或审计ProcessProvider
新建 RPC/消息 Controller 协议ControllerLoader
自定义任务调度时间NextTime
给方法添加事务式边界@HookBy
修改类结构或生成方法Enchanter / ClassTransformer

下一步可按框架模块开发实战完成一个可复用模块,并结合 ApplicationController类增强页面深入具体实现。

基于 Apache License 2.0 发布