Skip to content

概念参考

这组文档不以注解和 API 为起点,而是从框架背后的设计问题出发:为什么需要容器、控制反转反转了什么、依赖注入解决了什么问题,以及 Rain 为什么选择现在的实现方式。

即使你从未系统学习过框架,也可以从这里开始。阅读时不要求你预先理解 IoC、DI、Bean、容器、扫描、生命周期或 AOP。每一页都会先展示一个普通程序会遇到的问题,再观察我们自然写出的解决办法,最后才给这种办法命名。

Rain 吸收了许多现代 JVM 框架,尤其是 Spring 在容器、依赖管理、生命周期和扩展机制上的成熟思想。但 Rain 不是一个缩小版 Spring,也没有照搬 Spring 的全部抽象。理解相似点有助于快速入门,理解差异则能避免把 Spring 的使用经验错误套用到 Rain。

开始前:框架是什么

第一次接触“框架”时,最容易产生的误解是:框架就是一组很多、很复杂的工具函数。

工具库和框架的关键区别不在代码量,而在谁控制程序的主流程

使用工具库时,是你的代码决定何时调用工具:

kotlin
fun main() {
    val text = Json.encode(user)
    FileUtil.write("user.json", text)
}

使用框架时,你通常先按照约定提供一些类、方法或注解,然后由框架发现并调用它们:

kotlin
@EventListener
class UserListener {
    @SubscribeEvent
    fun onCreated(event: UserCreated) = Unit
}

这里没有任何代码直接调用 onCreated()。Rain 会扫描这个类、识别注解、创建对象、注册方法,并在事件出现时调用它。

这就是学习框架时最重要的观察角度:

不要只问“这个注解怎么写”,还要问“谁发现了它、谁创建了对象、谁在什么时候调用它、为什么业务代码不自己完成这些步骤”。

贯穿全文的五个问题

阅读每一页时,可以反复使用下面五个问题:

  1. 谁创建对象? 是业务代码 new,还是容器创建?
  2. 谁保存对象? 对象只用一次,还是由容器长期复用?
  3. 谁连接对象? 一个服务需要另一个服务时,由谁把它们组装起来?
  4. 谁调用对象? 是业务代码直接调用,还是框架在特定阶段回调?
  5. 谁决定规则? 实现选择、启动顺序和扩展发现是写死的,还是由配置和框架约定决定?

只要能回答这五个问题,你就已经开始理解一个框架,而不仅是记忆它的 API。

新人阅读建议

  • 第一次阅读不要急着记住所有类型名,先理解问题为什么出现。
  • 遇到术语时,尝试用“谁负责什么”重新表达它。
  • 每看到一段自动完成的行为,都试着想象不用框架时要手写哪些代码。
  • 完成每页的“停下来想一想”,再继续阅读答案。
  • 对 Spring 有经验的读者,可以直接关注每页后半部分的实现边界和对比。

最小术语表

术语暂时可以这样理解
对象运行中的一个类实例,例如一个 UserService
依赖一个对象为了工作而需要的另一个对象或配置
组件在应用中承担稳定职责、通常需要被管理的对象
Bean由容器提供或管理的对象
容器负责寻找、创建、保存和连接 Bean 的运行时对象
扫描在指定包中寻找类和注解的过程
生命周期对象从创建、启动、工作到停止的过程
扩展点框架预留的、允许应用插入自定义行为的位置

这些解释不是全部定义,但足够支撑第一次阅读。后续页面会逐渐补全它们。

阅读路线

控制反转

从手工创建对象讲起,解释 IoC 反转的是对象创建权、程序调用方向、扩展发现权和生命周期控制权。

依赖注入

区分 DI 与 Service Locator,讨论构造器注入、字段注入、配置注入、可选依赖与依赖倒置原则。

Rain 容器模型

深入理解 DiContextClassContextBeanCreatorBeanInjector、默认实例、命名绑定、自动绑定和集合注入。

与 Spring 的关系

克制地比较两者在 Bean 发现、容器元数据、扩展协议、增强技术、生命周期和配置环境上的一致与分歧。

收益与代价

分析 Rain 带来的模块解耦、统一扩展和运行时增强能力,以及隐式行为、扫描、反射、类加载和生态方面的成本。

设计与实践

给出依赖边界、扫描范围、多实现、Loader、字节码增强和测试方面的实践建议,并用完整示例串联概念。

系统总结

面向初学者重新串联全部知识,解释框架如何从代码复用、对象组装、模块扩展和工程治理中逐步诞生,以及为什么现代复杂软件越来越离不开框架化结构。

核心结论

Rain 的价值不是简单省掉几个 new,而是提供一套模块化 JVM 应用的组织方式:

text
配置决定扫描边界

AppLoader 发现类型与扩展声明

DiContext 建立类型上下文并组装对象图

Loader 注册事件、任务、Controller 等能力

ApplicationService 驱动应用生命周期

AppClassloader / Hook 提供类级增强

它的代价也来自同一个地方:更多行为从显式代码移动到了运行时约定、扫描、反射和类加载过程中。正确使用 Rain 的关键不是把一切都交给容器,而是清楚划分“应用级组件”和“普通对象”的边界。

基于 Apache License 2.0 发布