第一章:走进 IOC 容器——设计思想与整体架构
第一章:走进 IOC 容器——设计思想与整体架构
本章将带你理解 IOC 容器的核心思想,建立对 Jfire 框架的整体认知,并通过源码分析揭示一个轻量级 IOC 容器的设计精髓。本专栏的项目地址在:https://github.com/linbin-eric/jfire
1.1 什么是 IOC 与 DI
在传统的 Java 开发中,当一个对象需要依赖另一个对象时,我们通常会这样写:
public class UserService {
private UserDao userDao = new UserDaoImpl(); // 主动创建依赖
public void saveUser(User user) {
userDao.save(user);
}
}
这种方式存在几个明显的问题:
- 耦合度高:
UserService与UserDaoImpl紧密耦合,如果要更换实现类,必须修改源代码 - 难以测试:无法轻松替换为 Mock 对象进行单元测试
- 生命周期管理困难:对象的创建、销毁完全由使用方控制,难以实现单例等复用模式
1.1.1 控制反转(IoC)
控制反转(Inversion of Control) 是一种设计思想,它将对象的创建和依赖关系的管理从应用代码中转移到外部容器中。
所谓"反转",指的是:
| 对比项 | 传统方式 | IoC 方式 |
|---|---|---|
| 对象创建 | 应用代码主动 new | 容器负责创建 |
| 依赖获取 | 主动查找或创建 | 容器注入 |
| 控制权 | 在应用代码中 | 在容器中 |
1.1.2 依赖注入(DI)
依赖注入(Dependency Injection) 是实现 IoC 的一种具体方式。容器在创建对象时,自动将其依赖的对象注入进去。
public class UserService {
@Resource // 声明需要注入
private UserDao userDao; // 不再主动创建,由容器注入
public void saveUser(User user) {
userDao.save(user);
}
}
依赖注入的三种常见方式:
- 字段注入:通过反射直接设置字段值(Jfire 采用的方式)
- 构造器注入:通过构造函数传入依赖
- Setter 注入:通过 setter 方法传入依赖
1.1.3 IoC 与 DI 的关系
简单来说:
- IoC 是一种设计思想:强调控制权的转移
- DI 是 IoC 的实现手段:通过注入的方式实现控制反转
IoC 容器 = IoC 思想 + DI 实现 + Bean 生命周期管理
1.2 为什么要手写 IOC
你可能会问:Spring 已经那么成熟了,为什么还要手写 IOC 容器?
1.2.1 理解框架本质
使用 Spring 多年,你是否真正理解过:
- Bean 是如何被创建的?
- 依赖注入是如何实现的?
- AOP 代理是怎么生成的?
- 事务是如何被管理的?
手写 IOC 容器,能让你从"会用"变成"懂原理",这是架构师与普通开发者的分水岭。
1.2.2 面试加分项
大厂面试常见问题:
- "请说说 Spring IoC 的实现原理"
- "循环依赖是如何解决的?"
- "AOP 动态代理有几种方式?"
只有深入源码,才能给出有深度的回答。
1.2.3 定制化需求
在某些场景下,Spring 显得过于庞大:
- 嵌入式设备:资源受限
- 快速启动:Serverless 场景
- 特殊定制:需要深度改造容器行为
Jfire 作为一个轻量级框架,非常适合作为学习和定制的基础。
1.2.4 学习设计模式
一个 IOC 容器是设计模式的集大成者:
- 工厂模式、策略模式、责任链模式
- 模板方法、观察者模式、装饰器模式
- 单例模式、代理模式...
通过手写 IOC,你将深刻理解这些模式的实际应用。
1.3 Jfire 整体架构设计
Jfire 是一个轻量级的 Java IOC/AOP 容器,其设计目标是:
- 轻量:核心代码精简,依赖少
- 高效:启动快,运行时开销小
- 易学:代码结构清晰,适合学习
1.3.1 项目基本信息
从 pom.xml 可以看出 Jfire 的基本配置:
<groupId>cc.jfire</groupId>
<artifactId>jfire</artifactId>
<version>1.0.1-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
核心依赖:
| 依赖 | 版本 | 作用 |
|---|---|---|
| baseutil | 1.0 | 基础工具库,包含 @Resource 注解、反射工具、字节码生成 |
| jfireEL | 1.0 | 表达式语言,用于缓存键计算、条件表达式 |
| lombok | 1.18.30 | 代码简化 |
1.3.2 核心模块划分
Jfire 的源码结构如下:
src/main/java/cc/jfire/jfire/
├── core/ # 核心模块
│ ├── ApplicationContext.java # 容器接口
│ ├── DefaultApplicationContext.java # 默认实现
│ ├── AwareContextInited.java # 初始化感知接口
│ ├── aop/ # AOP 增强模块
│ │ ├── EnhanceManager.java # 增强管理器接口
│ │ ├── ProceedPoint.java # 连接点接口
│ │ ├── impl/ # 增强器实现
│ │ └── notated/ # AOP 注解
│ ├── bean/ # Bean 定义模块
│ │ ├── BeanDefinition.java # Bean 定义接口
│ │ ├── BeanRegisterInfo.java # Bean 注册信息接口
│ │ └── impl/ # 具体实现
│ ├── beanfactory/ # Bean 工厂模块
│ │ ├── BeanFactory.java # 工厂接口
│ │ └── impl/ # 工厂实现
│ ├── inject/ # 依赖注入模块
│ │ ├── InjectHandler.java # 注入处理器接口
│ │ └── impl/ # 注入实现
│ └── prepare/ # 容器初始化模块
│ ├── ContextPrepare.java # 准备器接口
│ ├── processor/ # 处理器实现
│ └── annotation/ # 配置注解
├── exception/ # 异常定义
└── util/ # 工具类
1.3.3 模块职责说明
让我们逐一理解每个模块的职责:
1. 容器核心(ApplicationContext)
容器的入口和门面,负责:
- 启动和初始化容器
- 提供 Bean 获取接口
- 管理 Bean 注册信息
2. Bean 定义模块(bean)
管理 Bean 的元信息和实例化逻辑:
BeanDefinition:定义如何获取 Bean 实例BeanRegisterInfo:存储 Bean 的注册信息和增强器
3. Bean 工厂模块(beanfactory)
负责创建 Bean 的原始实例:
ClassBeanFactory:通过构造器创建MethodBeanFactory:通过 @Bean 方法创建
4. 依赖注入模块(inject)
负责处理 Bean 之间的依赖关系:
- 字段注入、集合注入、延迟注入等多种策略
5. AOP 增强模块(aop)
负责 Bean 的动态增强:
- 前置、后置、环绕等增强方式
- 事务增强、缓存增强
6. 容器初始化模块(prepare)
负责容器启动时的各种准备工作:
- 组件扫描、配置加载、条件判断等
1.3.4 模块依赖关系
┌─────────────────────────────────┐
│ ApplicationContext │
│ (容器入口) │
└─────────────────────────────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ prepare │ │ bean │ │ aop │
│ (初始化) │ │ (Bean管理) │ │ (增强) │
└─────────────┘ └─────────────┘ └─────────────┘
│ │
▼ │
┌─────────────┐ │
│ beanfactory │◄──────────────┘
│ (对象创建) │
└─────────────┘
│
▼
┌─────────────┐
│ inject │
│ (依赖注入) │
└─────────────┘
1.4 核心设计模式概览
Jfire 中运用了多种经典设计模式,这些模式的巧妙运用是框架优雅的关键。
1.4.1 工厂模式(Factory Pattern)
应用场景:Bean 的创建
// BeanFactory 接口定义
public interface BeanFactory {
<E> E getUnEnhanceyInstance(BeanDefinition beanDefinition);
}
不同的工厂实现负责不同的创建方式:
ClassBeanFactory:通过反射调用构造器MethodBeanFactory:通过调用 @Bean 方法
好处:将对象的创建逻辑封装起来,调用方无需关心具体创建过程。
1.4.2 策略模式(Strategy Pattern)
应用场景:依赖注入
// InjectHandler 接口定义注入策略
public interface InjectHandler {
void init(Field field, ApplicationContext context);
void inject(Object instance);
}
根据字段类型选择不同的注入策略:
- 普通类型 →
InstacenInject - 接口类型 →
AbstractInject - 集合类型 →
CollectionInject - Map 类型 →
MapInject - BeanHolder →
BeanHolderInject
好处:策略可以独立变化,新增注入方式不影响现有代码。
1.4.3 责任链模式(Chain of Responsibility)
应用场景:增强器链、准备器链
// EnhanceManager 增强管理器
public interface EnhanceManager {
int order(); // 定义执行顺序
void enhance(ClassModel classModel, Class<?> type,
ApplicationContext context, String hostFieldName);
}
多个增强器按 order() 排序后依次执行:
TransactionEnhanceManager (order=10)
↓
CacheEnhanceManager (order=30)
↓
AopEnhanceManager (order=100)
好处:增强器之间解耦,可以灵活组合。
1.4.4 模板方法模式(Template Method)
应用场景:Bean 实例构建流程
PrototypeBeanDefinition 中定义了构建 Bean 的模板流程:
1. 检测循环依赖
2. 创建原始实例(子类可扩展)
3. 执行依赖注入
4. 调用初始化方法
5. 执行 AOP 增强
好处:固定算法骨架,具体步骤可定制。
1.4.5 观察者模式(Observer Pattern)
应用场景:容器初始化通知
// AwareContextInited 接口
public interface AwareContextInited {
void aware(ApplicationContext applicationContext);
default int order() { return 0; }
}
实现该接口的 Bean 会在容器初始化完成后收到通知。
好处:解耦容器初始化和业务逻辑。
1.4.6 装饰器模式(Decorator Pattern)
应用场景:AOP 增强
通过字节码生成技术,为原始类动态生成增强子类:
原始类: UserService
↓
增强类: UserService$$Jfire$$1 extends UserService
- 包含拦截逻辑
- 调用原始方法
好处:不修改原始类代码,动态添加功能。
1.4.7 单例模式(Singleton Pattern)
应用场景:单例 Bean、工厂实例
// ClassBeanFactory 使用单例
public class ClassBeanFactory implements BeanFactory {
public static final ClassBeanFactory INSTANCE = new ClassBeanFactory();
private ClassBeanFactory() {}
}
好处:节省资源,保证全局唯一。
1.4.8 设计模式总结
| 设计模式 | 应用位置 | 核心作用 |
|---|---|---|
| 工厂模式 | BeanFactory | 封装对象创建 |
| 策略模式 | InjectHandler | 多种注入方式 |
| 责任链模式 | EnhanceManager、ContextPrepare | 链式处理 |
| 模板方法 | BeanDefinition | 固定构建流程 |
| 观察者模式 | AwareContextInited | 事件通知 |
| 装饰器模式 | AOP 增强 | 动态增强 |
| 单例模式 | SingletonBeanDefinition | 对象复用 |
1.5 快速上手:第一个 Jfire 应用
理论讲得再多,不如动手实践。让我们通过源码来理解 Jfire 的启动过程。
1.5.1 启动方式
Jfire 提供了两种启动方式,定义在 ApplicationContext 接口中:
// 源码:ApplicationContext.java
/**
* 启动应用上下文
* @param bootClass 启动类(必须标注 @Configuration)
* @return 应用上下文实例
*/
static ApplicationContext boot(Class<?> bootClass) {
return new DefaultApplicationContext(bootClass);
}
/**
* 启动应用上下文(无启动类,编程式)
* @return 应用上下文实例
*/
static ApplicationContext boot() {
return new DefaultApplicationContext();
}
1.5.2 配置类启动
这是最常用的启动方式:
@Configuration // 标记为配置类
@ComponentScan("com.example.app") // 扫描组件
@PropertyPath("application.yml") // 加载配置
public class AppConfig {
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
}
// 启动容器
public class Application {
public static void main(String[] args) {
ApplicationContext context = ApplicationContext.boot(AppConfig.class);
UserService userService = context.getBean(UserService.class);
userService.doSomething();
}
}
1.5.3 启动流程源码解析
让我们深入 DefaultApplicationContext 的构造函数:
// 源码:DefaultApplicationContext.java
public DefaultApplicationContext(Class<?> bootStarpClass) {
// 1. 校验启动类必须有 @Configuration 注解
if (AnnotationContext.isAnnotationPresent(Configuration.class, bootStarpClass) == false) {
throw new IllegalArgumentException("启动的配置类,一定要有Configuration注解");
}
// 2. 注册启动类
register(bootStarpClass);
}
当调用 getBean() 时,会触发容器的完整初始化:
// 源码:DefaultApplicationContext.java
@Override
public <E> E getBean(Class<E> ckass) {
makeAvailable(); // 确保容器已初始化
// ... 获取 Bean 逻辑
}
@Override
public void makeAvailable() {
if (!freshed) {
refresh(); // 执行容器刷新
}
}
1.5.4 容器刷新流程(核心!)
refresh() 方法是整个容器的核心,让我们逐行分析:
// 源码:DefaultApplicationContext.java
private void refresh() {
freshed = true;
// 步骤1:注册内部类(框架自身的组件)
registerInternalClass();
// 步骤2:处理 @Import 注解,导入其他配置类
processImports();
// 步骤3:执行准备器链(可能循环执行)
if (processContextPrepare() == FoundNewContextPrepare.YES) {
log.debug("执行ContextPrepare接口,发现需要刷新容器");
refresh(); // 递归刷新
return;
}
// 步骤4:执行 AOP 增强扫描
log.debug("准备获取所有的EnhanceManager,执行增强扫描");
enhanceScan();
// 步骤5:完成所有 Bean 定义
beanRegisterInfoMap.values().stream()
.filter(info -> info instanceof DefaultBeanRegisterInfo)
.forEach(info -> ((DefaultBeanRegisterInfo) info).complete());
// 步骤6:触发容器初始化完成回调
log.debug("准备获取所有的AwareContextInited接口实现,执行aware方法");
awareContextInit();
}
让我们用流程图来展示:
┌─────────────────┐
│ refresh() │
└────────┬────────┘
│
▼
┌────────────────────────┐
│ registerInternalClass │
│ 注册框架内部组件 │
└────────────┬───────────┘
│
▼
┌────────────────────────┐
│ processImports │
│ 处理 @Import 导入 │
└────────────┬───────────┘
│
▼
┌────────────────────────┐
│ processContextPrepare │◄──────┐
│ 执行准备器链 │ │
└────────────┬───────────┘ │
│ │
发现新准备器? │
┌────┴────┐ │
│ │ │
YES NO │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ enhanceScan │ │
│ │ AOP 增强扫描 │ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ complete() │ │
│ │ 完成Bean定义 │ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │awareContextInit│ │
│ │ 初始化回调 │ │
│ └──────┬───────┘ │
│ │ │
└─────────┼──────────────┘
│
▼
┌─────────────┐
│ 完成启动 │
└─────────────┘
1.5.5 内部组件注册
registerInternalClass() 注册了框架必需的内部组件:
// 源码:DefaultApplicationContext.java
private void registerInternalClass() {
// 1. 注册容器自身(方便其他 Bean 注入 ApplicationContext)
registerBeanRegisterInfo(new OutterBeanRegisterInfo(this, "applicationContext"));
// 2. 注册三个内置增强管理器
register(AopEnhanceManager.class); // 普通 AOP
register(TransactionEnhanceManager.class); // 事务增强
register(CacheEnhanceManager.class); // 缓存增强
// 3. 注册配置处理器
register(ConfigurationProcessor.class);
}
1.5.6 获取 Bean 的方式
Jfire 提供了多种获取 Bean 的方式:
// 源码:ApplicationContext.java
// 1. 按类型获取(最常用)
<E> E getBean(Class<E> ckass);
// 2. 按名称获取
<E> E getBean(String beanName);
// 3. 获取所有匹配的 Bean
<E> Collection<E> getBeans(Class<E> ckass);
实现逻辑:
// 源码:DefaultApplicationContext.java
@Override
public <E> E getBean(Class<E> ckass) {
makeAvailable(); // 确保容器初始化
// 从注册表中查找匹配的 Bean
Optional<BeanDefinition> any = beanRegisterInfoMap.values().stream()
.filter(info -> ckass.isAssignableFrom(info.getType()))
.map(info -> info.get())
.findAny();
if (any.isPresent()) {
return (E) any.get().getBean();
} else {
throw new BeanDefinitionCanNotFindException(ckass);
}
}
1.5.7 完整示例
下面是一个完整的 Jfire 应用示例:
// 1. 定义服务接口
public interface UserService {
void saveUser(String name);
}
// 2. 实现服务
@Resource // 标记为 Bean
public class UserServiceImpl implements UserService {
@Resource // 注入依赖
private UserDao userDao;
@Override
public void saveUser(String name) {
userDao.save(new User(name));
}
}
// 3. 定义 DAO
@Resource
public class UserDaoImpl implements UserDao {
@Override
public void save(User user) {
System.out.println("保存用户: " + user.getName());
}
}
// 4. 配置类
@Configuration
@ComponentScan("com.example")
public class AppConfig {
}
// 5. 启动应用
public class Application {
public static void main(String[] args) {
// 启动容器
ApplicationContext context = ApplicationContext.boot(AppConfig.class);
// 获取 Bean
UserService userService = context.getBean(UserService.class);
// 使用
userService.saveUser("张三");
}
}
运行结果:
保存用户: 张三
1.6 Jfire vs Spring:设计理念对比
| 对比维度 | Jfire | Spring |
|---|---|---|
| 定位 | 轻量级学习框架 | 企业级生产框架 |
| 代码量 | 数千行 | 数十万行 |
| 启动速度 | 毫秒级 | 秒级 |
| 依赖数量 | 3个 | 数十个 |
| Bean 作用域 | 单例、原型 | 单例、原型、Request、Session 等 |
| AOP 实现 | 字节码生成 | JDK 动态代理/CGLIB |
| 配置方式 | 注解为主 | 注解 + XML + JavaConfig |
| 生态系统 | 有限 | 极其丰富 |
Jfire 的优势:
- 代码精简:核心原理一目了然
- 学习成本低:适合入门学习 IOC/AOP
- 启动极快:适合轻量级场景
Jfire 的局限:
- 功能有限:不支持复杂的企业级特性
- 生态缺乏:没有成熟的周边框架
- 社区小:遇到问题难以找到解决方案
1.7 本章小结
本章我们学习了:
-
IOC 与 DI 的概念
- IOC 是一种设计思想,强调控制权的转移
- DI 是实现 IOC 的具体方式
-
Jfire 的整体架构
- 容器核心、Bean 管理、工厂、注入、AOP、准备器六大模块
- 模块之间职责清晰、依赖合理
-
核心设计模式
- 工厂、策略、责任链、模板方法、观察者、装饰器、单例
-
容器启动流程
refresh()方法的六个步骤- 从注册到初始化的完整链路
-
快速上手实践
- 配置类、组件扫描、Bean 获取
下一章预告:我们将深入 Bean 定义体系,探索 BeanDefinition 和 BeanRegisterInfo 的设计,理解单例与原型作用域的实现原理,以及 Jfire 是如何检测和处理循环依赖的。
思考题
-
为什么
refresh()方法中processContextPrepare()可能会导致递归调用?这样设计的目的是什么? -
registerInternalClass()为什么要先注册ApplicationContext自身? -
如果让你设计一个 IOC 容器,你会选择哪些核心接口?
核心源码清单
| 文件 | 路径 | 核心内容 |
|---|---|---|
| ApplicationContext.java | core/ | 容器接口定义 |
| DefaultApplicationContext.java | core/ | 容器默认实现、refresh() 流程 |
| BeanDefinition.java | core/bean/ | Bean 定义接口 |
| BeanRegisterInfo.java | core/bean/ | Bean 注册信息接口 |
| BeanFactory.java | core/beanfactory/ | Bean 工厂接口 |
| InjectHandler.java | core/inject/ | 注入处理器接口 |
| EnhanceManager.java | core/aop/ | 增强管理器接口 |
| ContextPrepare.java | core/prepare/ | 准备器接口 |
| AwareContextInited.java | core/ | 初始化感知接口 |
| Configuration.java | core/prepare/annotation/ | 配置类注解 |
专栏以轻量级 Java 框架 Jfire 为蓝本,带你从零手写一个完整的 IOC 容器。专栏共 10 章,涵盖 IOC 容器核心原理:Bean 定义与生命周期、Bean 工厂设计、五种依赖注入策略、循环依赖解决方案;深入 AOP 实现:五种增强方式、字节码动态生成技术;以及企业级特性:声明式事务管理(四种传播级别)、声明式缓存框架、条件注解与自动配置机制。

凡岛公司福利 263人发布