Spring Boot 加载过程(自动配置及web服务)
运行环境
IntelliJ IDEA 2019.2 (Community Edition)
Java 1.8.0_131
Maven apache-maven-3.5.4
Spring Boot spring-boot-2.2.14.BUILD-SNAPSHOT
引入Spring Boot 依赖
pom.xml
<!-- 第一种方式 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.14.BUILD-SNAPSHOT</version>
</parent>
<!-- 第二种方式 -->
<dependencyManagement>
<dependencies>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.14.BUILD-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
项目入口
importorg.springframework.boot.SpringApplication;
importorg.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
publicclassTestApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
@SpringBootApplication
@SpringBootApplication
的源码,可以看到里面组合了三个注解:@ComponentScan
,@SpringBootConfiguration
,@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters= { @Filter(type=FilterType.CUSTOM, classes=TypeExcludeFilter.class),
@Filter(type=FilterType.CUSTOM, classes=AutoConfigurationExcludeFilter.class) })
public@interfaceSpringBootApplication {
// ...
}
@ComponentScan
/**may be specified to define specific packages to scan. If specific
* packages are not defined, scanning will occur from the package of the
* class that declares this annotation.
可以指定来定义要扫描的特定包。如果未定义特定的包,则将从声明此注释的类的包中进行扫描。
*/
public@interfaceComponentScan {
//...
}
@SpringBootConfiguration
/**
* Indicates that a class provides Spring Boot application
* {@link Configuration @Configuration}. Can be used as an alternative to the Spring's
* standard {@code @Configuration} annotation so that configuration can be found
* automatically (for example in tests).
指示类提供Spring引导应用程序。可以作为Spring的标准{@code@Configuration}注释的替代,以便可以找到自动配置(例如在测试中)。
*/
@Configuration
public@interfaceSpringBootConfiguration {
//...
}
@EnableAutoConfiguration
@Import(AutoConfigurationImportSelector.class)
public@interfaceEnableAutoConfiguration {
//...
}
@EnableAutoConfiguration
是springboot实现自动化配置的核心注解,通过这个注解把spring应用所需的bean注入容器中。@EnableAutoConfiguration
源码通过@Import
注入了一个ImportSelector
的实现类 AutoConfigurationImportSelector
,这个ImportSelector
最终实现动态加载。 AutoConfigurationImportSelector
完成动态加载过程如下:
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations
org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames
publicfinalclassSpringFactoriesLoader {
publicstaticfinalStringFACTORIES_RESOURCE_LOCATION="META-INF/spring.factories";
privatestaticMap<String, List<String>>loadSpringFactories(@NullableClassLoaderclassLoader) {
MultiValueMap<String, String>result=cache.get(classLoader);
if (result!=null) {
returnresult;
}
try {
Enumeration<URL>urls= (classLoader!=null?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result=newLinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URLurl=urls.nextElement();
UrlResourceresource=newUrlResource(url);
Propertiesproperties=PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?>entry : properties.entrySet()) {
StringfactoryTypeName= ((String) entry.getKey()).trim();
for (StringfactoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
returnresult;
}
catch (IOExceptionex) {
thrownewIllegalArgumentException("Unable to load factories from location ["+
FACTORIES_RESOURCE_LOCATION+"]", ex);
}
}
}
// 在classpath下所有的META-INF/spring.factories文件中查找org.springframework.boot.autoconfigure.EnableAutoConfiguration的值,并将其封装到一个List中返回,并将其加载到Spring 容器中。
ServletWebServerFactory 加载过程
首先在META-INF/spring.factories
文件中可以找到 org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
类,Spring会在项目启动的时候,将其加载到Spring容器中。
@Configuration(proxyBeanMethods=false)
@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 })
publicclassServletWebServerFactoryAutoConfiguration {
}
ServletWebServerFactoryAutoConfiguration
类通过Import的方式将EmbeddedTomcat加载到容器中。
@Configuration(proxyBeanMethods=false)
classServletWebServerFactoryConfiguration {
@Configuration(proxyBeanMethods=false)
//根据条件进行加载 tomcatServletWebServerFactory
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value=ServletWebServerFactory.class, search=SearchStrategy.CURRENT)
staticclassEmbeddedTomcat {
@Bean
TomcatServletWebServerFactorytomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer>connectorCustomizers,
ObjectProvider<TomcatContextCustomizer>contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>>protocolHandlerCustomizers) {
TomcatServletWebServerFactoryfactory=newTomcatServletWebServerFactory();
factory.getTomcatConnectorCustomizers()
.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatContextCustomizers()
.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatProtocolHandlerCustomizers()
.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
returnfactory;
}
}
}
项目启动需要初始化SpringApplication类。构造方法会先进行环境类型的判断
org.springframework.boot.SpringApplication#SpringApplication(org.springframework.core.io.ResourceLoader, java.lang.Class<?>...)
@SuppressWarnings({ "unchecked", "rawtypes" })
publicSpringApplication(ResourceLoaderresourceLoader, Class<?>... primarySources) {
this.resourceLoader=resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources=newLinkedHashSet<>(Arrays.asList(primarySources));
// 判断环境类型
this.webApplicationType=WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass=deduceMainApplicationClass();
}
#在通过以下调用过程最终通过createWebServer创建Web服务
org.springframework.boot.SpringApplication#refreshContext
org.springframework.boot.SpringApplication#refresh
org.springframework.context.support.AbstractApplicationContext#refresh
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#createWebServer
privatevoidcreateWebServer() {
WebServerwebServer=this.webServer;
ServletContextservletContext=getServletContext();
if (webServer==null&&servletContext==null) {
// 获取 web 容器
ServletWebServerFactoryfactory=getWebServerFactory();
this.webServer=factory.getWebServer(getSelfInitializer());
}
elseif (servletContext!=null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletExceptionex) {
thrownewApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}