概念参考
这组文档不以注解和 API 为起点,而是从框架背后的设计问题出发:为什么需要容器、控制反转反转了什么、依赖注入解决了什么问题,以及 Rain 为什么选择现在的实现方式。
即使你从未系统学习过框架,也可以从这里开始。阅读时不要求你预先理解 IoC、DI、Bean、容器、扫描、生命周期或 AOP。每一页都会先展示一个普通程序会遇到的问题,再观察我们自然写出的解决办法,最后才给这种办法命名。
Rain 吸收了许多现代 JVM 框架,尤其是 Spring 在容器、依赖管理、生命周期和扩展机制上的成熟思想。但 Rain 不是一个缩小版 Spring,也没有照搬 Spring 的全部抽象。理解相似点有助于快速入门,理解差异则能避免把 Spring 的使用经验错误套用到 Rain。
开始前:框架是什么
第一次接触“框架”时,最容易产生的误解是:框架就是一组很多、很复杂的工具函数。
工具库和框架的关键区别不在代码量,而在谁控制程序的主流程。
使用工具库时,是你的代码决定何时调用工具:
fun main() {
val text = Json.encode(user)
FileUtil.write("user.json", text)
}使用框架时,你通常先按照约定提供一些类、方法或注解,然后由框架发现并调用它们:
@EventListener
class UserListener {
@SubscribeEvent
fun onCreated(event: UserCreated) = Unit
}这里没有任何代码直接调用 onCreated()。Rain 会扫描这个类、识别注解、创建对象、注册方法,并在事件出现时调用它。
这就是学习框架时最重要的观察角度:
不要只问“这个注解怎么写”,还要问“谁发现了它、谁创建了对象、谁在什么时候调用它、为什么业务代码不自己完成这些步骤”。
贯穿全文的五个问题
阅读每一页时,可以反复使用下面五个问题:
- 谁创建对象? 是业务代码
new,还是容器创建? - 谁保存对象? 对象只用一次,还是由容器长期复用?
- 谁连接对象? 一个服务需要另一个服务时,由谁把它们组装起来?
- 谁调用对象? 是业务代码直接调用,还是框架在特定阶段回调?
- 谁决定规则? 实现选择、启动顺序和扩展发现是写死的,还是由配置和框架约定决定?
只要能回答这五个问题,你就已经开始理解一个框架,而不仅是记忆它的 API。
新人阅读建议
- 第一次阅读不要急着记住所有类型名,先理解问题为什么出现。
- 遇到术语时,尝试用“谁负责什么”重新表达它。
- 每看到一段自动完成的行为,都试着想象不用框架时要手写哪些代码。
- 完成每页的“停下来想一想”,再继续阅读答案。
- 对 Spring 有经验的读者,可以直接关注每页后半部分的实现边界和对比。
最小术语表
| 术语 | 暂时可以这样理解 |
|---|---|
| 对象 | 运行中的一个类实例,例如一个 UserService |
| 依赖 | 一个对象为了工作而需要的另一个对象或配置 |
| 组件 | 在应用中承担稳定职责、通常需要被管理的对象 |
| Bean | 由容器提供或管理的对象 |
| 容器 | 负责寻找、创建、保存和连接 Bean 的运行时对象 |
| 扫描 | 在指定包中寻找类和注解的过程 |
| 生命周期 | 对象从创建、启动、工作到停止的过程 |
| 扩展点 | 框架预留的、允许应用插入自定义行为的位置 |
这些解释不是全部定义,但足够支撑第一次阅读。后续页面会逐渐补全它们。
阅读路线
控制反转
从手工创建对象讲起,解释 IoC 反转的是对象创建权、程序调用方向、扩展发现权和生命周期控制权。
依赖注入
区分 DI 与 Service Locator,讨论构造器注入、字段注入、配置注入、可选依赖与依赖倒置原则。
Rain 容器模型
深入理解 DiContext、ClassContext、BeanCreator、BeanInjector、默认实例、命名绑定、自动绑定和集合注入。
与 Spring 的关系
克制地比较两者在 Bean 发现、容器元数据、扩展协议、增强技术、生命周期和配置环境上的一致与分歧。
收益与代价
分析 Rain 带来的模块解耦、统一扩展和运行时增强能力,以及隐式行为、扫描、反射、类加载和生态方面的成本。
设计与实践
给出依赖边界、扫描范围、多实现、Loader、字节码增强和测试方面的实践建议,并用完整示例串联概念。
系统总结
面向初学者重新串联全部知识,解释框架如何从代码复用、对象组装、模块扩展和工程治理中逐步诞生,以及为什么现代复杂软件越来越离不开框架化结构。
核心结论
Rain 的价值不是简单省掉几个 new,而是提供一套模块化 JVM 应用的组织方式:
配置决定扫描边界
↓
AppLoader 发现类型与扩展声明
↓
DiContext 建立类型上下文并组装对象图
↓
Loader 注册事件、任务、Controller 等能力
↓
ApplicationService 驱动应用生命周期
↓
AppClassloader / Hook 提供类级增强它的代价也来自同一个地方:更多行为从显式代码移动到了运行时约定、扫描、反射和类加载过程中。正确使用 Rain 的关键不是把一切都交给容器,而是清楚划分“应用级组件”和“普通对象”的边界。