sanguispring:我的简易版 Spring IoC 容器 从源码到发布

sanguispring:我的简易版 Spring IoC 容器

—— 从源码到发布的学习之旅

作者:三桂 日期:2025-05-30


一、背景与动机

在学习 Spring 框架的过程中,IoC(控制反转)与依赖注入(DI)一直是最核心、也最玄妙的部分。虽然大量现成框架能帮我们快速上手,但要真正理解它的内部原理,最有效的方式就是“手写”一个最简化版本。

于是,我决定从零开始,手写一个——sanguispring,它既能帮我巩固反射、XML 解析与设计模式等基础知识,也能在完成后有一个可展示的开源项目,记录我的学习轨迹。


二、核心实现概览

1. 项目结构

com.sangui.sanguispring.core
├── ApplicationContext.java             # 容器顶层接口
└── ClassPathXmlApplicationContext.java # XML 加载与属性注入实现

示例 Bean 与测试:
com.sangui.bean.Vip.java
com.sangui.dao.OrderDao.java
com.sangui.service.OrderService.java
com.sangui.test.SanguiTest.java

配置文件:
src/main/resources/sanguispring.xml

2. ApplicationContext 接口

public interface ApplicationContext {
   Object getBean(String name);
}
  • 定义最简的 getBean(String) 方法,用于按 ID 返回单例 Bean。

3. ClassPathXmlApplicationContext 实现

主要步骤:

  1. 解析 XML:用 dom4j 读取 sanguispring.xml,提取所有 <bean> 节点。

  2. 实例化 Bean:反射调用无参构造器,放入 singletonMap

  3. 属性注入:遍历每个 <property>,根据 valueref 属性,调用对应的 setter 方法。

    • 支持 16 种简单类型及其包装类型:boolean/Booleanbyte/Byteshort/Shortint/Integerlong/Longfloat/Floatdouble/Doublechar/Character

    • 其他类型默认当作 String 处理。

switch (field.getType().getSimpleName()) {
 case "int":
   setMethod.invoke(bean, Integer.parseInt(value));
   break;
 // ……
 default:
   setMethod.invoke(bean, value);
}

三、演示示例

配置文件 sanguispring.xml

<beans>
 <bean id="vip" class="com.sangui.bean.Vip">
   <property name="name" value="lisi"/>
   <property name="age"  value="44"/>
   <property name="height" value="1.77"/>
 </bean>
 <bean id="orderDao" class="com.sangui.dao.OrderDao"/>
 <bean id="orderService" class="com.sangui.service.OrderService">
   <property name="orderDao" ref="orderDao"/>
 </bean>
</beans>

测试代码 SanguiTest.java

ApplicationContext ctx = new ClassPathXmlApplicationContext("sanguispring.xml");
OrderService service = (OrderService) ctx.getBean("orderService");
System.out.println(service);
service.save();

Vip vip = (Vip) ctx.getBean("vip");
System.out.println(vip);

运行结果:

OrderService{orderDao=com.sangui.dao.OrderDao@6d06d69c}
数据库正在新增用户!!
Vip{name='lisi', age=44, height=1.77}

四、局限与思考

sanguispring 目前只是一个学习原型,主要存在以下不足:

  1. 仅支持属性注入,不支持构造器注入或复杂注入逻辑。

  2. 只支持简单类型(及包装类型)注入,对数组、集合、枚举、甚至 Date 等类型都无能为力。

  3. 无循环依赖检测与解决,存在循环引用时容易出现 NPE 或 StackOverflowError。

  4. 所有 Bean 都为单例,无作用域(prototype、request 等)概念。

  5. 异常处理粗糙,反射或加载出错时只打印堆栈,无自定义异常或友好提示。

  6. 无生命周期钩子、无 AOP 支持,离完整框架仍有较大差距。

这些痛点正是我下一步要钻研和改进的方向,比如加入构造器注入、支持集合注入、实现 BeanPostProcessor、AOP 拦截等。


五、开源发布

  1. 将代码托管在 GitHub: https://github.com/Wusangui571/sanguispring

  2. 提交 README.mdLICENSE(MIT),并附上使用示例、TODO 列表。

  3. 构建一个可发布的 Jar:

    mvn clean package
  4. 发布到 GitHub Releases 或私服,方便下载;也可以改造为 Maven Central 坐标。


六、总结与展望

从最初的“手写一个 IoC 容器”想法,到如今能解析 XML、反射注入,整个过程让我深刻理解了 Spring 容器的核心机制。未来,我计划逐步将 sanguispring 打造成支持:

  • 注解驱动的配置(@Component@Autowired

  • 构造器注入与多构造重载选择

  • Bean 生命周期回调与后处理器

  • AOP 切面编程支持

  • 扩展作用域管理(prototype、request、session)

  • 更完备的异常与日志体系

欢迎大家在 GitHub 上提出 Issue、PR,一起交流、一起进步!


免 责 声 明:本项目仅作记录学习之用

  • 微信
  • 赶快加我聊天吧
  • QQ
  • 赶快加我聊天吧
  • weinxin
三桂

发表评论 取消回复 您未登录,登录后才能评论,前往登录