SpringBoot源码剖析

源码剖析 - 依赖管理

问题 1:为什么导入 dependency 时不需要指定版本?

在 Spring Boot 项目中,pom.xml 核心依赖为 spring-boot-starter-parentspring-boot-starter-web,其中 spring-boot-starter-parent 是版本管理的核心:

1. spring-boot-starter-parent 核心作用
复制代码
<!-- Spring Boot父项目依赖管理 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.9.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

spring-boot-starter-parent 底层做了三件核心事:

  • properties 节点:定义 Java 版本(1.8)、编码(UTF-8)、Maven 编译版本等基础配置;
  • build 节点 :配置资源过滤(支持多环境配置文件如 application-dev.yml)、插件版本管理;
  • 继承 spring-boot-dependencies :这是真正的版本管理核心,通过 dependencyManagement 锁定所有 Spring Boot 组件版本。
2. spring-boot-dependencies 核心配置
复制代码
<!-- 版本属性定义 -->
<properties>
    <activemq.version>5.15.13</activemq.version>
    <antlr2.version>2.7.7</antlr2.version>
    <!-- 所有组件版本定义 -->
</properties>

<!-- 依赖版本管理 -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot</artifactId>
            <version>${revision}</version>
        </dependency>
        <!-- 所有核心组件版本锁定 -->
    </dependencies>
</dependencyManagement>

结论 :项目继承 spring-boot-starter-parent 后,自动继承了 spring-boot-dependencies 的版本锁定,因此无需手动指定依赖版本。

问题 2:项目运行依赖的 JAR 包从何而来?

spring-boot-starter-web 是场景化依赖启动器,核心作用是依赖传递

复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

spring-boot-starter-web 内部封装了 Web 开发所需的所有底层依赖:

  • spring-boot-starter-tomcat:内置 Tomcat 服务器
  • spring-boot-starter-validation:参数校验
  • spring-web + spring-webmvc:Spring MVC 核心

结论 :场景启动器通过依赖传递引入所有相关 JAR 包,版本由 spring-boot-starter-parent 统一管理。第三方启动器(如 druid-spring-boot-starter)需手动指定版本。

源码剖析 - 自动配置

核心问题:Spring Boot 如何实现自动配置?

Spring Boot 自动配置核心是 @SpringBootApplication 注解,其本质是组合注解:

复制代码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration     // 标识配置类
@EnableAutoConfiguration     // 核心:开启自动配置
@ComponentScan(...)          // 组件扫描
public @interface SpringBootApplication {
    Class<?>[] exclude() default {};          // 排除自动配置类
    String[] excludeName() default {};        // 排除自动配置类名
    String[] scanBasePackages() default {};   // 自定义扫描包
}

1. @SpringBootConfiguration

本质是 @Configuration,标识当前类为 Spring 配置类,等价于 XML 配置文件。

2. @EnableAutoConfiguration(核心)

复制代码
@AutoConfigurationPackage       // 自动配置包
@Import(AutoConfigurationImportSelector.class) // 导入自动配置选择器
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}
(1)@AutoConfigurationPackage

通过 @Import(AutoConfigurationPackages.Registrar.class) 注册当前主类所在包为自动配置基础包,用于扫描用户自定义组件。

(2)AutoConfigurationImportSelector 核心逻辑

自动配置的核心流程:

  1. 加载候选配置类 :从 META-INF/spring.factories 读取 EnableAutoConfiguration 对应的自动配置类;
  2. 排除指定类 :排除 exclude 属性指定的配置类;
  3. 条件过滤 :通过 @ConditionalOnClass/@ConditionalOnBean 等注解过滤不符合条件的配置类;
  4. 注册有效配置类:将最终符合条件的配置类注入 IOC 容器。

3. 条件注解(@Conditional 系列)

|------------------------------|------------------|
| 注解 | 作用 |
| @ConditionalOnBean | 容器存在指定 Bean 时生效 |
| @ConditionalOnClass | 类路径存在指定类时生效 |
| @ConditionalOnMissingBean | 容器不存在指定 Bean 时生效 |
| @ConditionalOnWebApplication | Web 环境下生效 |
| @ConditionalOnProperty | 配置文件存在指定属性时生效 |

4. 自动配置示例:HttpEncodingAutoConfiguration

复制代码
@Configuration
@EnableConfigurationProperties(HttpEncodingProperties.class)
@ConditionalOnWebApplication          // Web 环境生效
@ConditionalOnClass(CharacterEncodingFilter.class) // 存在字符编码过滤器
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {

    private final HttpEncodingProperties properties;

    @Bean
    @ConditionalOnMissingBean
    public CharacterEncodingFilter characterEncodingFilter() {
        // 构建过滤器并绑定配置属性
    }
}

自动配置核心总结

  1. 加载 spring.factories 中的自动配置类;
  2. 排除指定的配置类;
  3. 条件过滤(@Conditional 系列);
  4. 注册有效配置类到 IOC 容器;
  5. 通过 xxxProperties 绑定配置文件属性。

源码剖析 - Run 方法执行流程

Spring Boot 启动入口

复制代码
@SpringBootApplication
public class MyTestMVCApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyTestMVCApplication.class, args);
    }
}

1. SpringApplication 初始化

复制代码
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 推断应用类型
    setInitializers(getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 初始化器
    setListeners(getSpringFactoriesInstances(ApplicationListener.class)); // 监听器
    this.mainApplicationClass = deduceMainApplicationClass(); // 推断主类
}

2. run () 方法核心流程

复制代码
public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    
    // 1. 初始化监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    
    try {
        // 2. 构建环境(系统变量、配置文件等)
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        
        // 3. 创建应用上下文
        context = createApplicationContext();
        
        // 4. 上下文前置处理
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        
        // 5. 刷新上下文(IOC 容器初始化核心)
        refreshContext(context);
        
        // 6. 上下文后置处理
        afterRefresh(context, applicationArguments);
        
        stopWatch.stop();
        listeners.started(context);
        callRunners(context, applicationArguments);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }
    
    listeners.running(context);
    return context;
}

3. 核心步骤详解

|---------|---------------------------------------------------------|
| 步骤 | 核心操作 |
| 初始化监听器 | 加载 spring.factories 中的 SpringApplicationRunListener |
| 构建环境 | 加载系统环境、配置文件(application.yml/properties ) |
| 创建上下文 | 根据应用类型(SERVLET/REACTIVE/NONE)创建对应上下文 |
| 上下文前置处理 | 注册主类 BeanDefinition、加载初始化器 |
| 刷新上下文 | IOC 容器初始化(BeanDefinition 扫描、加载、注册) |
| 上下文后置处理 | 执行自定义扩展逻辑 |

源码剖析 - 自定义 Starter

1. Starter 机制核心

Starter 是 Spring Boot 「约定大于配置」的体现,通过封装通用功能,实现「引入即生效」。

2. 自定义 Starter 规范

  • 命名:官方 Starter 为 spring-boot-starter-xxx,自定义建议 xxx-spring-boot-starter
  • 核心:自动配置类 + spring.factories 配置。

3. 自定义 Starter 实战

(1)创建 Starter 工程(Maven JAR)

依赖

复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
        <version>2.2.9.RELEASE</version>
    </dependency>
</dependencies>
(2)编写配置属性类
复制代码
@ConfigurationProperties(prefix = "simplebean")
public class SimpleBean {
    private int id;
    private String name;
    // getter/setter/toString
}
(3)编写自动配置类
复制代码
@Configuration
@ConditionalOnBean(ConfigMarker.class) // 热插拔条件
@EnableConfigurationProperties(SimpleBean.class)
public class MyAutoConfiguration {
    static {
        System.out.println("MyAutoConfiguration init....");
    }
    
    @Bean
    public SimpleBean simpleBean() {
        return new SimpleBean();
    }
}
(4)配置 spring.factories

resources/META-INF/spring.factories 中添加:

复制代码
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.lagou.config.MyAutoConfiguration
(5)热插拔实现
  • 标记类:

    public class ConfigMarker {}

  • 启用注解:

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Import(ConfigMarker.class)
    public @interface EnableRegisterServer {}

4. 使用自定义 Starter

(1)引入依赖
复制代码
<dependency>
    <groupId>com.lagou</groupId>
    <artifactId>zdy-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
(2)配置属性
复制代码
simplebean.id=1
simplebean.name=自定义starter
(3)测试
复制代码
@Autowired
private SimpleBean simpleBean;

@Test
public void zdyStarterTest() {
    System.out.println(simpleBean);
}

源码剖析 - 内嵌 Tomcat

1. 默认容器与切换

Spring Boot 引入 spring-boot-starter-web 后默认使用 Tomcat,切换容器只需排除 Tomcat 依赖并引入其他容器:

复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <groupId>org.springframework.boot</groupId>
        </exclusion>
    </exclusions>
</dependency>
<!-- 引入 Jetty -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

2. 内嵌 Tomcat 自动配置原理

(1)自动配置类:ServletWebServerFactoryAutoConfiguration

spring.factories 加载,通过 @Import 导入 Tomcat/Jetty/Undertow 自动配置类:

复制代码
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {}
(2)Tomcat 启动核心

TomcatServletWebServerFactorygetWebServer() 方法创建并启动 Tomcat:

复制代码
public WebServer getWebServer(ServletContextInitializer... initializers) {
    Tomcat tomcat = new Tomcat();
    // 配置 Tomcat 端口、连接器等
    tomcat.start(); // 启动 Tomcat
    return new TomcatWebServer(tomcat);
}
(3)Tomcat 启动触发点

refreshContext() 中调用 onRefresh() -> createWebServer(),最终触发 getWebServer() 启动 Tomcat。

源码剖析 - 自动配置 SpringMVC

1. 核心自动配置类:DispatcherServletAutoConfiguration

复制代码
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {}

2. 自动配置核心逻辑

(1)配置 DispatcherServlet
复制代码
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
    DispatcherServlet dispatcherServlet = new DispatcherServlet();
    // 配置 DispatcherServlet 属性
    return dispatcherServlet;
}
(2)注册 DispatcherServlet 到 ServletContext

通过 DispatcherServletRegistrationBean 实现 Servlet 注册(Servlet 3.0 规范):

复制代码
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
        WebMvcProperties webMvcProperties) {
    DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
            webMvcProperties.getServlet().getPath());
    // 配置加载顺序、名称等
    return registration;
}

3. Servlet 注册核心流程

  1. DispatcherServletRegistrationBean 继承 ServletRegistrationBean
  2. 通过 onStartup() 调用 servletContext.addServlet() 注册 DispatcherServlet;
  3. 最终在 Spring Boot 启动时完成 Spring MVC 核心组件的自动注册。

核心总结

Spring Boot 自动配置 Spring MVC 的本质是:

  • 通过 DispatcherServletAutoConfiguration 配置 DispatcherServlet;
  • 通过 Servlet 3.0 规范的 servletContext.addServlet() 动态注册 DispatcherServlet;
  • 支持通过 application.yml 配置 Spring MVC 核心属性(如路径、加载顺序)。

关键补充

Servlet 3.0 规范支持动态注册三大组件:

  • servletContext.addServlet():注册 Servlet
  • servletContext.addFilter():注册 Filter
  • servletContext.addListener():注册 Listener

这是 Spring Boot 无需 web.xml 即可配置 Web 组件的核心原理。

相关推荐
皮卡龙2 小时前
Spring MVC 接收请求参数的核心
java·spring·mvc
爱笑的眼睛112 小时前
FastAPI 路由系统深度探索:超越基础 CRUD 的高级模式与架构实践
java·人工智能·python·ai
武子康3 小时前
Java-193 Spymemcached 深入解析:线程模型、Sharding 与序列化实践全拆解
java·开发语言·redis·缓存·系统架构·memcached·guava
韩凡3 小时前
HashMap的理解与结构
java·开发语言·哈希算法
hhzz3 小时前
Spring Boot整合Activiti的项目中实现抄送功能
java·spring boot·后端
初心灬3 小时前
Java 对接coze工作流
java
代衡_Monster4 小时前
通过位运算实现Java逻辑的包含关系
java·java-ee
毕设源码-朱学姐4 小时前
【开题答辩全过程】以 基于Java的失物招领系统设计与实现为例,包含答辩的问题和答案
java·开发语言