# SpringBoot 前置知识
作者:Ethan.Yang
博客:https://blog.ethanyang.cn (opens new window)
在深入 SpringBoot 特性及源码分析之前,先弄清楚两个问题:
- 为什么在已有 Spring 框架的前提下,还需要 SpringBoot?
- SpringBoot 的核心特性是什么?
# 为什么要SpringBoot ?
在传统 Spring 框架中,开发一个 Web 项目繁琐且模板化。例如:
- 创建 Maven/Gradle 项目结构
- 引入 Spring、Spring MVC、Servlet API 等依赖
- 配置
web.xml,注册DispatcherServlet - 配置
DispatcherServlet.xml或 Spring MVC 配置类 - 编写
Controller处理请求 - 部署到 JSP/Servlet 容器
实际开发中,只有第 5 步是研发人员真正需要关注的业务逻辑,其他步骤都是重复工作。 SpringBoot 诞生的目标,就是约定优于配置,帮助开发者快速创建 Spring 项目,减少配置和模板化步骤。
# 什么是SpringBoot?
SpringBoot 是基于 Spring 的一套快速开发脚手架,核心思想是约定优于配置。
特点:
- 内置容器
引入
spring-boot-starter-web后自动内置 Tomcat,无需手动配置外部容器。 - 固定的项目结构 提供默认的目录结构规范,降低项目搭建门槛。
- 简化的配置文件
提供
application.properties或application.yml,结合@Value、@ConfigurationProperties读取配置。 - Starter 启动依赖
引入
starter即可自动拉取一系列相关依赖,避免手动管理版本和配置冲突。
# SpringBoot 核心特性
| 特性 | 说明 |
|---|---|
| EnableAutoConfiguration 自动装配 | 自动加载符合条件的 Bean,减少手动配置。 |
| Starter 启动依赖 | 模块化依赖管理,直接引入 Starter,即可快速集成功能模块。 |
| Actuator 监控 | 提供丰富的健康检查和指标监控端点,支持 HTTP/JMX 访问。 |
| Spring Boot CLI | 命令行工具,支持 Groovy 脚本快速开发。 |
上文中的特性都依赖于注解驱动的发展, 如果没有注解仍然以配置文件的形式, SpringBoot项目依旧会相当繁琐。
# Spring 注解驱动的发展历程
# Spring1.x
Java 5 刚发布,Spring 引入注解如
@Transactional,但配置方式仍然依赖 XML。<bean id="userService" class="com.example.UserService"/>1
# Spring 2.x 时代
- 核心注解:
@Autowired、@Qualifier、@Component、@Service、@Repository、@Controller - 依旧需要
context:annotation-config、context:component-scan配置。
<context:annotation-config/>
<context:component-scan base-package="com.example"/>
2
# Spring 3.x 时代
- 推出
@Configuration注解,支持 Java 配置类,逐渐取代 XML。 @Import: 将传入的类注册到 Spring IOC 容器中,可以是普通类、配置类、实现特定接口的类,最终都会被解析为 BeanDefinition 并注册。- 开始全面拥抱 JavaConfig。
@Configuration
@ComponentScan("com.example")
public class AppConfig {
@Bean
public UserService userService() {
return new UserService();
}
}
2
3
4
5
6
7
8
9
Enable 模块驱动
- 通过
@EnableXXX注解实现模块化启用

# Spring 4.x 时代
- 引入 条件化配置 和更强的 JavaConfig 支持。
- 核心注解:
@Conditional:基于条件动态注册 Bean。@RestController:组合注解,简化 REST 风格接口开发。@EventListener:简化事件监听。- 全面支持 Java 8 特性(Lambda、Streams 等)。
@Configuration
public class ConditionalConfig {
@Bean
@Conditional(OnWindowsCondition.class) // 仅在 Windows 系统下注册
public WindowsService windowsService() {
return new WindowsService();
}
}
2
3
4
5
6
7
8
9
# Spring 5.x 时代
- 强调 函数式编程、响应式编程 和 性能优化。
- 核心注解:
@Indexed:加快组件扫描速度,提升启动性能。@Nullable:增强空值检查提示。- 全面支持 JDK 9 模块系统和 Java 11。
- 引入 Spring WebFlux:基于 Reactor 的响应式编程模型。
@Indexed
@Component
public class FastScannedComponent {
// 使用 @Indexed 后可通过提前生成的 META-INF 文件加快类扫描
}
2
3
4
5
响应式 Controller 示例:
@RestController
@RequestMapping("/api")
public class ReactiveController {
@GetMapping("/hello")
public Mono<String> hello() {
return Mono.just("Hello, Spring 5 WebFlux!");
}
}
2
3
4
5
6
7
8
9
# Spring Bean的装载方式
Spring 的核心思想是 IOC(控制反转),Bean 的创建和生命周期管理都交给容器完成。 在实际开发中,Bean 的装载方式可以分为 IOC 静态装载 和 动态 Bean 装载 两种:
- 静态装载:Bean 定义在项目启动时就确定并加载,适用于绝大多数业务逻辑。
- 动态装载:Bean 可以在运行时按需加载、注册或替换,适用于复杂或可扩展的场景。
# IOC 装载
IOC 静态装载是 Spring 的默认加载方式,容器启动时就会扫描、解析和创建 BeanDefinition,并实例化 Bean。
常见实现方式:
XML 配置方式(早期主流)
<beans xmlns="http://www.springframework.org/schema/beans"> <bean id="userService" class="com.example.UserService"/> </beans>1
2
3注解扫描方式
@Configuration @ComponentScan("com.example") public class AppConfig { } @Service public class UserService { public void hello() { System.out.println("Hello Spring!"); } }1
2
3
4
5
6
7
8
9
10JavaConfig + @Bean 方式
@Configuration public class BeanConfig { @Bean public UserService userService() { return new UserService(); } }1
2
3
4
5
6
7
8
场景:适用于项目核心业务 Bean 的加载,大部分开发只需使用这种方式。
# 动态 Bean 装载
为什么需要动态装载?
- 插件化开发:无需重启服务即可加载新功能模块。
- 多租户/多环境:不同租户或环境需要不同 Bean。
- Starter 自动装配:Spring Boot Starter 底层依赖动态注册机制。
- 灰度发布/热更新:可在线动态替换 Bean,提升系统灵活性。
- 按需加载:延迟加载复杂 Bean,提高启动性能。
实现方式:
BeanDefinitionRegistry 动态注册
在Spring章节
@EnableAspectJAutoProxy中使用的就是BeanDefinitionRegistry 进行动态注册。简单示例
@Component public class DynamicBeanRegistrar implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { RootBeanDefinition beanDefinition = new RootBeanDefinition(UserService.class); registry.registerBeanDefinition("userService", beanDefinition); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {} } @Configuration @Import(DynamicBeanRegistrar.class) public class AppConfig { }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17在容器初始化 BeanDefinition 阶段,手动注册新的 Bean 定义。
ImportSelector 动态导入
public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata metadata) { return new String[]{"com.example.UserService"}; } } @Configuration @Import(MyImportSelector.class) public class AppConfig { }1
2
3
4
5
6
7
8
9
10
11Spring Boot Starter 底层大量使用此机制实现按需加载。可以批量导入
# 常见组合
在 Spring 和 Spring Boot 源码中,@Import、@Conditional 常常与 ImportSelector 或 ImportBeanDefinitionRegistrar 搭配使用,形成一套“组合拳”来完成 Bean 的动态装载。这套机制是 Spring Boot 自动配置、Starter 组件、模块化开发的底层核心。
| 组合方式 | 说明 | 典型场景 |
|---|---|---|
@Import + ImportSelector | 通过 ImportSelector 返回一批要注册的类名,容器自动将这些类转换成 BeanDefinition 注入容器中。 | Spring Boot 自动装配,批量按需注册配置类 |
@Import + @Conditional + ImportSelector | 结合条件注解,在满足特定条件(如类路径存在、配置文件开关、环境变量等)时选择性注册 Bean,实现真正的“按需加载”。 | Spring Boot Starter 自动装配核心 |
@Import + ImportBeanDefinitionRegistrar | 通过 ImportBeanDefinitionRegistrar 直接操作 BeanDefinitionRegistry,实现更灵活、更复杂的 Bean 注册逻辑。 | MyBatis Mapper 扫描、Spring Security 等框架底层 |
@EnableXXX + @Import | 对外封装为一个简洁的开关注解,让用户一键启用模块,而模块内部通过 @Import 完成复杂 Bean 装载。 | @EnableScheduling、@EnableAsync 等 |
@Import + BeanDefinitionRegistryPostProcessor | 在容器刷新前修改 Bean 注册表,是更底层的扩展点,适合框架作者使用。 | Dubbo、Spring Cloud 等中间件框架 |
# SPI 机制
在此处再次强调SPI机制, SpringBoot的starter加载也就是自动装配原理实际上是SPI机制的变形, 本篇文章先介绍SPI的原理
# SPI 是什么
SPI(Service Provider Interface)是一种 服务发现机制,允许框架或平台在运行时动态加载第三方实现。 它通过在类路径下约定文件目录和内容,实现“接口与实现解耦”,使得框架能够在不修改代码的前提下,自动发现和加载实现类。
通俗理解:接口由框架定义,实现由第三方开发,JDK/Spring 通过 SPI 在运行时帮你找实现并加载。
# JDK 原生 SPI 原理
JDK 从 java.util.ServiceLoader 开始提供 SPI 支持,核心机制是 类路径扫描 + 反射实例化:
- 约定路径:
META-INF/services/ - 文件命名:文件名是接口的全类名
- 文件内容:实现类的全类名
- 加载逻辑:
ServiceLoader.load(接口.class)会扫描文件并加载实现类实例
示例:
// 接口
public interface Logger {
void log(String msg);
}
// 实现类
public class ConsoleLogger implements Logger {
@Override
public void log(String msg) {
System.out.println("Console: " + msg);
}
}
// META-INF/services/com.example.Logger 文件内容:
com.example.ConsoleLogger
// 使用
ServiceLoader<Logger> loader = ServiceLoader.load(Logger.class);
for (Logger logger : loader) {
logger.log("Hello SPI");
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Spring Boot Starter 与 SPI
Spring Boot 的自动装配原理是 SPI 的升级版,区别在于它做了更多优化:
| 对比项 | JDK SPI | Spring Boot SPI(变形) |
|---|---|---|
| 配置文件路径 | META-INF/services/接口全类名 | META-INF/spring.factories(Spring Boot 2.x 前) META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(Spring Boot 3.x) |
| 加载方式 | ServiceLoader | SpringFactoriesLoader |
| 加载内容 | 实现类全类名 | 自动配置类全类名 |
| 加载时机 | 手动调用加载 | 启动时自动加载 |
| 额外功能 | 无 | 条件化加载(@Conditional)、按需注入等 |