导言
现在许多项目都广泛采用了Spring Boot,你只需要引入相应的starter,例如spring-boot-starter-web,然后启动应用程序,就会自动启动Tomcat Web服务器并开始接收HTTP请求。那么,这是如何实现的呢?它是如何知道要启动Tomcat而不是Undertow? 另外,如果我希望使用Undertow吗,要如何切换?本文将深入剖析背后的原理。
简单的说,就是Spring Boot提供了一种自动配置(auto-configuration)机制:当项目引入一个包含自动配置的jar包时,根据特定的条件和规则,它会注册不同的Bean到Spring容器中,从而启动不同的功能特性。
那么,具体什么是自动配置,它是如何工作的?有哪些条件和规则?这些条件和规则又是如何匹配和应用的?本文将分三个部分帮你全面了解自动配置的工作原理:
- 核心概念:@AutoConfiguration(自动配置类)和@Conditional注解(条件匹配)
- 案例分析:Spring Boot是怎么自动启动Tomcat服务器的?
- 常见问题和FAQ
本文基于Spring Boot 3.0.x版本,同时也适用于Spring Boot 2.7.x版本。
核心概念 - 自动配置类@AutoConfiguration
什么是自动配置类
使用过Spring框架的开发者应该对@Configuration注解非常熟悉了。在项目中,我们经常使用它来进行自定义的Bean配置。
而@AutoConfiguration是专门用于自动配置类的注解,而这些加了AutoConfiguration注解的自动配置类就是自动配置的入口。@AutoConfiguration本身也使用了@Configuration注解,表明自动配置类也是一个标准的配置类。
与标准的配置类相同,自动配置类的核心内容也是配置Bean,但是它会在此基础上,添加各种条件和规则,只有满足特定的条件和规则,这些Bean才会生效。另外,这些条件规则也可以应用到自动配置类本身,控制整个自动配置类的开启与否。
通常一个特定的自包含特性功能会对应一个自动配置类,但是配置本身不一定要都要全写在这一个类里,可以分解为多个普通的@Configuration配置类,然后通过@Import引入。
例如,Servlet Web服务器相关功能的自动配置,入口就是一个自动配置类ServletWebServerFactoryAutoConfiguration,它将每个可选的Web服务器配置都拆分到各自的@Configuration配置类中,部分代码如下所示:
Java复制代码@AutoConfiguration(after = SslAutoConfiguration.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({
ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
// ... 其它Bean配置 ...
}
我们来详细分析下这个自动配置类上的注解。
- @AutoConfiguration(after = SslAutoConfiguration.class):告诉Spring框架这个类是用于自动配置的。有些自动配置的初始化是有先后依赖关系,可以通过after,before来声明这种依赖关系。
- @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE):设置自动配置类加载的顺序。
- @ConditionalOnClass和@ConditionalOnWebApplication:这两个注解就是自动配置生效的条件和规则,后面会详细说明。
- @EnableConfigurationProperties(ServerProperties.class):自动配置提供的自定义参数,比如server.port等。
- @Import({...}):自动配置类一般是作为入口,简单的配置可以直接写在自动配置类里。而复杂的配置建议按功能或范围拆分成子配置,然后通过@Import引入。注意,引入的顺序会影响条件的匹配,尤其是选项类的配置(比如选择Tomcat,Jetty还是Undertow)。
查找自动配置类
我们现在有了自动配置类,那么Spring Boot是如何知道要加载这个自动配置类的呢?要知道,我们只是单纯引入了一个jar包而已,并没有做任何设置。
答案是,Spring定义了一套自动配置专用的发现机制,就是jar包里的META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件。该文件的每一行就是一个自动配置类的完全限定名,比如下面是spring-boot-autoconfigure包里该文件的部分内容:
shell复制代码## 其它自动配置类
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
## 其它自动配置类
可以看到ServletWebServerFactoryAutoConfiguration就在这个文件里。具体的加载逻辑可以查看源码AutoConfigurationImportSelector#getCandidateConfigurations。
imports文件是Spring 2.7新引入的发现机制,之前版本使用的是sprin