源码剖析 - 依赖管理
问题 1:为什么导入 dependency 时不需要指定版本?
在 Spring Boot 项目中,pom.xml 核心依赖为 spring-boot-starter-parent 和 spring-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 核心逻辑
自动配置的核心流程:
- 加载候选配置类 :从
META-INF/spring.factories读取EnableAutoConfiguration对应的自动配置类; - 排除指定类 :排除
exclude属性指定的配置类; - 条件过滤 :通过
@ConditionalOnClass/@ConditionalOnBean等注解过滤不符合条件的配置类; - 注册有效配置类:将最终符合条件的配置类注入 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() {
// 构建过滤器并绑定配置属性
}
}
自动配置核心总结
- 加载
spring.factories中的自动配置类; - 排除指定的配置类;
- 条件过滤(@Conditional 系列);
- 注册有效配置类到 IOC 容器;
- 通过
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 启动核心
TomcatServletWebServerFactory 的 getWebServer() 方法创建并启动 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 注册核心流程
DispatcherServletRegistrationBean继承ServletRegistrationBean;- 通过
onStartup()调用servletContext.addServlet()注册 DispatcherServlet; - 最终在 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():注册 ServletservletContext.addFilter():注册 FilterservletContext.addListener():注册 Listener
这是 Spring Boot 无需 web.xml 即可配置 Web 组件的核心原理。