自研 CodeStats 框架:与 Spring Framework、Spring Boot 启动流程的深度解析
一个从零打造的全栈 Java 框架,如何在没有 Spring 的情况下实现 IoC、MVC、嵌入式 Tomcat 和 MyBatis 风格 Mapper?本文带你深入剖析自研 CodeStats 的核心设计思想,并与 Spring 传统启动、Spring Boot 自动配置进行对比,看清框架演进的脉络。CodeStats 不仅是技术练习,更是 WWAIC 范式的首个实证项目。
📦 项目地址与资源
-
Gitee 开源仓库:https://gitee.com/zhouzuoli/code-stats.git
-
CSDN 系列文章:
-
WWAIC 范式说明:WWAIC(Whole-Week AI Engineering,全周项目 AI 工程)指开发者在一周内将项目完整上下文提交给 AI,由 AI 直接生成一个可运行的完整系统。CodeStats 是这一范式的首个实证项目——100% 由 AI 生成,耗时仅一周,实现了从 IoC 容器到嵌入式 Tomcat 的全栈能力。
一、引言:为什么要自己写一个 Spring?
在 Java 后端开发中,Spring 生态已然成为事实标准。然而,当我们亲手实现一个微型 Spring + Tomcat 时,才能真正理解 IoC 容器如何管理 Bean、DispatcherServlet 如何分发请求、连接池如何限流。CodeStats 正是一个完全自研的“类 Spring”框架,它集成了 IoC 容器、MVC 架构、嵌入式 Tomcat、JDBC 连接池、MyBatis 风格 Mapper 以及代码分析引擎。本文将通过 启动流程 这个核心视角,对比 CodeStats 自研框架与 Spring Framework(传统 XML/注解驱动)以及 Spring Boot(自动配置 + 内嵌服务器)的差异,揭示框架设计的本质。
二、自研 CodeStats 启动流程深度解剖
2.1 入口:Bootstrap.main()
java
// Bootstrap.java
public class Bootstrap {
public static void main(String[] args) {
SpringApplication.run(Bootstrap.class, args);
}
}
SpringApplication.run 是自研的“启动器”,它做了三件事:
-
创建
AnnotationConfigApplicationContext,扫描指定包下的@Controller、@Service、@Component等注解,完成 IoC 容器的初始化。 -
启动自研的
Catalina(嵌入式 Tomcat 实现),绑定端口(默认 28080)。 -
注册关闭钩子,优雅停机。
2.2 IoC 容器核心:AnnotationConfigApplicationContext
自研的 AbstractApplicationContext 实现了经典的 refresh() 模板方法:
java
// AbstractApplicationContext.java
public void refresh() throws Exception {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh(); // 子类扩展点
registerListeners();
finishBeanFactoryInitialization(beanFactory); // 实例化所有非懒加载单例
finishRefresh();
}
关键差异:自研版本没有 BeanDefinition 的完整注册后处理机制,但是实现了核心的 包扫描 → BeanDefinition 注册 → 单例创建 → 依赖注入 流程。尤其值得一提的是 @Autowired 解析支持了 @Qualifier 和 @Primary 的多候选 Bean 处理:
java
// AbstractAutowireCapableBeanFactory.java
protected Object resolveDependency(Field field, Object bean) throws Exception {
Map<String, ?> candidates = this.getBeansOfType(requiredType);
if (candidates.size() == 1) return candidates.values().iterator().next();
// 按 @Qualifier 或字段名匹配,再找 @Primary
...
}
2.3 嵌入式 Tomcat 容器:Catalina 与 Connector
自研 Tomcat 采用经典的 Server → Service → Connector → Engine → Host → Context → Wrapper 层次结构,每个组件都实现了 Lifecycle 接口(init/start/stop/destroy)。启动时:
java
// Catalina.java
public void start() {
server.init();
server.start();
}
Connector 使用 ServerSocket 监听端口,接收请求后封装为 Request 和 Response,经过 Pipeline-Valve 责任链,最终由 DispatcherServlet 处理。
亮点:
-
自研
WebappClassLoader实现了类隔离,每个 Context 拥有独立的类加载器。 -
MultipartResolver支持文件上传,通过BoundaryFinder高效解析 multipart 边界。 -
完全手写的
DispatcherServlet支持@RequestMapping、@ResponseBody、@PathVariable、@RequestParam、@RequestBody等核心注解。
2.4 数据访问层:@Mapper 代理 + JdbcTemplate
java
// MapperProxy.java
public Object invoke(Object proxy, Method method, Object[] args) {
String sql = extractSqlFromAnnotation(method);
SqlParser.ParsedSql parsed = SqlParser.parse(sql);
// 根据返回值类型(List<T> / T / int)自动执行查询或更新
if (returnType == List.class) {
return jdbcTemplate.queryForList(parsedSql, elementType, paramValues);
} else if (isSimpleType(returnType)) {
return jdbcTemplate.queryOne(...);
}
}
这实际上是 MyBatis 风格的声明式 DAO,仅通过 @Select 注解和 @Param 即可生成 SQL 代理,无需 XML。配合自研的 JdbcTemplate 和 SimpleDataSource 连接池(基于 Semaphore 限流 + 空闲队列),实现了完整的数据访问能力。
2.5 启动时序图(自研 CodeStats)
text
Bootstrap.main()
└─ SpringApplication.run()
├─ new AnnotationConfigApplicationContext(package)
│ ├─ refreshBeanFactory() → 扫描类 → 注册 BeanDefinition
│ ├─ registerMappers() → 为 @Mapper 接口创建动态代理并放入单例池
│ └─ finishBeanFactoryInitialization() → 实例化单例 → 依赖注入
└─ new Catalina()
├─ load() → 构造 Server/Service/Connector/Engine/Host/Context/Wrapper
└─ start() → 启动 Connector 线程,监听端口
三、Spring Framework 传统启动流程回顾
以经典的 AnnotationConfigApplicationContext 为例(非 Spring Boot):
java
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(AppConfig.class);
其 refresh() 方法实现与自研版本非常相似,但 Spring 做了更多精细化工作:
| 步骤 | 自研 CodeStats | Spring Framework |
|---|---|---|
| BeanDefinition 注册 | 仅支持类名扫描,无条件注解 | 支持 @Conditional、@Import、FactoryBean |
| 依赖注入 | 字段注入(@Autowired) | 构造器注入、字段注入、方法注入 |
| BeanPostProcessor | 简单的前置/后置处理 | 内置几十个处理器(AOP、InitDestroy等) |
| 事件机制 | 无(预留空方法) | 完整的事件发布/监听模型 |
| 作用域 | 仅 singleton,无 prototype | 完整支持 request/session/application |
| 容器扩展点 | 极少 | BeanFactoryPostProcessor 广泛应用 |
Spring 的 BeanFactoryPostProcessor 机制允许在 Bean 实例化之前修改 BeanDefinition,这是自研框架缺失的关键扩展能力。此外,Spring 通过 InstantiationAwareBeanPostProcessor 支持属性注入前的自定义逻辑,而自研版仅在 populateBean 中硬编码处理 @Autowired。
四、Spring Boot 启动流程:颠覆性变化
Spring Boot 的核心是 SpringApplication.run(),它做了 自动配置 + 嵌入式服务器:
java
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
启动流程大致为:
-
推断 Web 应用类型(Servlet/Reactive)。
-
加载
META-INF/spring.factories中的ApplicationContextInitializer和ApplicationListener。 -
创建
ConfigurableApplicationContext(如AnnotationConfigServletWebServerApplicationContext)。 -
调用
refresh()前,通过ServletWebServerApplicationContext的createWebServer()创建内嵌的 Tomcat/Jetty/Undertow。 -
执行
refresh()完成 IoC 容器初始化。 -
启动 Web Server。
对比自研 CodeStats:
| 特性 | 自研 CodeStats | Spring Boot |
|---|---|---|
| 服务器创建时机 | 在 IoC 刷新之前独立启动 | 集成在 ApplicationContext 的 refresh() 中 |
| 自动配置 | 无 | @EnableAutoConfiguration + spring.factories |
| 条件化 Bean | 无 | @ConditionalOnClass 等 |
| 启动性能 | 极快(无复杂 SPI 加载) | 较慢(大量自动配置扫描) |
| 配置加载 | application.properties 简单加载 |
ConfigDataEnvironment 支持多 profile / 导入 |
核心差异:Spring Boot 把“嵌入容器”变成 IoC 容器的一等公民——WebServer 作为一个 Bean 被管理,而自研 CodeStats 的 Catalina 是独立于 IoC 容器之外运行的。这使得 Spring Boot 可以在 refresh() 过程中利用容器内的 ServletContextInitializer 动态注册 Servlet/Filter,而自研版必须在 load() 阶段静态配置 DispatcherServlet。
五、三方启动流程横向对比表
| 对比维度 | 自研 CodeStats | Spring Framework(传统) | Spring Boot |
|---|---|---|---|
| 入口类 | Bootstrap.main() |
AnnotationConfigApplicationContext |
SpringApplication.run() |
| 容器刷新 | 手工实现 refresh() 模板 |
同样模板,但扩展点极为丰富 | 复用 Spring 的 refresh(),但前置大量准备 |
| Web 服务器 | 自研嵌入式 Tomcat(Catalina) | 需外部部署(如 Tomcat、Jetty) | 内嵌 Tomcat/Jetty/Undertow,自动配置 |
| Bean 定义扫描 | 仅支持类路径下的 @Component 注解 |
支持注解、XML、JavaConfig、包扫描 | 相同,但结合自动配置导入额外配置类 |
| 条件化注册 | 无 | 无原生支持(需 @Profile 有限) |
@Conditional 体系(类、Bean、资源等) |
| 启动耗时(空项目) | ≈200ms | ≈600ms | ≈1200ms(自动配置加载) |
| 学习曲线 | 陡峭(需理解全栈实现) | 平缓(文档丰富) | 极平缓(约定大于配置) |
| 可扩展性 | 低(缺少生命周期钩子) | 极高(BeanPostProcessor 无处不在) | 极高(AutoConfigurationImportFilter) |
六、设计思想的升华:为什么我们要再造轮子?
6.1 极简主义 VS 企业级完备
自研 CodeStats 的核心追求是 教学演示与透明化。它没有复杂的 SPI 加载机制,没有数百个 BeanPostProcessor,所有代码都清晰可见。例如,DispatcherServlet 的路由匹配直接用正则解析,而 Spring MVC 则引入了 HandlerMapping、HandlerAdapter、HandlerExceptionResolver 等一系列抽象。这种简化让初学者能快速理解 “一个请求从 socket 到 controller 的全路径”。
6.2 连接池的“微型”实现
SimpleDataSource 使用 Semaphore 控制最大并发连接数,用 BlockingQueue 存放空闲连接,总代码不到 200 行。相比之下,HikariCP 几千行代码针对各种边界情况优化。但自研版本却清晰地展示了 “限流 + 复用” 的核心思想,这正是连接池的本质。
6.3 Mapper 代理:动态代理的实战
MapperProxy 通过 InvocationHandler 将接口方法调用转换为 SQL 执行,这是 Java 动态代理最经典的案例。Spring 的 MyBatis 整合也是如此,但自研版剥离了 XML 解析,仅依靠注解,让开发者直观感受 “接口方法如何变为 SQL 查询”。
6.4 WWAIC 范式:AI 驱动的全栈开发
CodeStats 的诞生方式本身就是一种范式创新——它完全由 AI(DeepSeek)生成,人类开发者仅提供架构提示和一周的上下文管理。WWAIC(全周项目 AI 工程)的核心思想是:让 AI 承担完整的系统实现,人类聚焦于架构设计与需求拆解。CodeStats 证明了这种方法论的可行性:一个拥有 IoC、MVC、Tomcat、连接池、Mapper 的完整框架,可以在 7 天内由 AI 从零构建。这与传统的“AI 辅助编程”(仅生成代码片段)有本质区别。
七、总结:从自研到主流,理解框架的进化之路
通过对比三个启动流程,我们可以清晰地看到:
-
自研 CodeStats:一个完整的“最小可行框架”,麻雀虽小五脏俱全。它的价值在于教育与范式验证——让开发者亲手触摸 IoC、AOP、MVC、ORM 的核心实现,同时作为 WWAIC 范式的首个实证项目。
-
Spring Framework:将扩展性做到极致,通过
BeanPostProcessor和BeanFactoryPostProcessor构建了庞大的生态基石。 -
Spring Boot:在 Spring Framework 之上封装了“自动配置”和“内嵌服务器”,简化了运维,提高了开发效率。
对于技术人而言,精通 Spring 是职业必备,亲手写一个 Spring 则是内功修炼。CodeStats 项目正是这样一个内功修炼的绝佳案例。它让我们明白:所有看似黑魔法的框架,底层都是设计模式的朴素运用。
你可以从 Gitee 获取 CodeStats 完整源码,亲自运行
Bootstrap.main(),体验一个纯自研 Java Web 框架启动时的激动心跳。更可以以此为起点,探索 WWAIC 范式下 AI 驱动的全栈开发新可能。点赞、收藏、评论,让更多人看到技术干货!
🔗 资源汇总:
更多推荐

所有评论(0)