一个从零打造的全栈 Java 框架,如何在没有 Spring 的情况下实现 IoC、MVC、嵌入式 Tomcat 和 MyBatis 风格 Mapper?本文带你深入剖析自研 CodeStats 的核心设计思想,并与 Spring 传统启动、Spring Boot 自动配置进行对比,看清框架演进的脉络。CodeStats 不仅是技术练习,更是 WWAIC 范式的首个实证项目。


📦 项目地址与资源


一、引言:为什么要自己写一个 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 是自研的“启动器”,它做了三件事:

  1. 创建 AnnotationConfigApplicationContext,扫描指定包下的 @Controller@Service@Component 等注解,完成 IoC 容器的初始化。

  2. 启动自研的 Catalina(嵌入式 Tomcat 实现),绑定端口(默认 28080)。

  3. 注册关闭钩子,优雅停机。

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);
    }
}

启动流程大致为:

  1. 推断 Web 应用类型(Servlet/Reactive)。

  2. 加载 META-INF/spring.factories 中的 ApplicationContextInitializer 和 ApplicationListener

  3. 创建 ConfigurableApplicationContext(如 AnnotationConfigServletWebServerApplicationContext)。

  4. 调用 refresh() 前,通过 ServletWebServerApplicationContext 的 createWebServer() 创建内嵌的 Tomcat/Jetty/Undertow。

  5. 执行 refresh() 完成 IoC 容器初始化。

  6. 启动 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 则引入了 HandlerMappingHandlerAdapterHandlerExceptionResolver 等一系列抽象。这种简化让初学者能快速理解 “一个请求从 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 驱动的全栈开发新可能。

点赞、收藏、评论,让更多人看到技术干货!


🔗 资源汇总

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐