Spring-boot启动过程

Spring Boot实现了 auto-configuration 自动配置,降低了项目搭建的复杂度。它主要是为了解决使用Spring框架需要进行大量的配置太麻烦的问题。它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用。

启动类注解

整个启动的过程可以概括为:

  • 读取所有依赖的META-INF/spring/%s%s.imports文件,该文件指明了哪些依赖可以被自动加载
  • 根据importSelector类选择加载哪些依赖,使用conditionOn系列注解排除掉不需要的配置文件
  • 将剩余的配置文件所代表的bean加载到IOC容器中

在spring-boot包中 spring.factories 文件定义了加载哪些类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

# ConfigData Location Resolvers
org.springframework.boot.context.config.ConfigDataLocationResolver=\
org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,\
org.springframework.boot.context.config.StandardConfigDataLocationResolver

# ConfigData Loaders
org.springframework.boot.context.config.ConfigDataLoader=\
org.springframework.boot.context.config.ConfigTreeConfigDataLoader,\
org.springframework.boot.context.config.StandardConfigDataLoader

# Application Context Factories
org.springframework.boot.ApplicationContextFactory=\
org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContextFactory,\
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContextFactory

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

这此内容会被解析为Map<K, List>这种格式,键和值都是一个类的全限定名。整个SpringBoot的启动过程都是通过 @SpringBootApplication 注解和 SpringApplication.run 方法来实现的。

1
2
3
4
5
6
7
8
9
10
11
12
@Target(ElementType.TYPE) // 注解的适用范围,其中TYPE用于描述类、接口(包括包注解类型)或enum声明
@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期,保留到class文件中(三个生命周期)
@Documented // 表明这个注解应该被javadoc记录
@Inherited // 子类可以继承该注解
@SpringBootConfiguration // 继承了Configuration,表示当前是注解类
@EnableAutoConfiguration // 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助
@ComponentScan(excludeFilters = { // 扫描路径设置
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}

几个重要注解说明:

  • @SpringBootConfiguration:继承了Configuration,表示当前是注解类
  • @EnableAutoConfiguration:开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助收集和注册特定场景相关的Bean定义
  • @ComponentScan:扫描路径设置

@EnableAutoConfiguration

功能:从classpath中搜寻所有的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 配置文件,并将其中 org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的配置项通过反射(Java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。

1
2
3
4
5
6
7
8
9
10
@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}

重要的注解与类:

  • @AutoConfigurationPackage
  • AutoConfigurationImportSelector.class

AutoConfigurationPackage

注册当前启动类的根package注册 org.springframework.boot.autoconfigure.AutoConfigurationPackages 的BeanDefinition。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
if (registry.containsBeanDefinition(BEAN)) {
addBasePackages(registry.getBeanDefinition(BEAN), packageNames);
}
else {
RootBeanDefinition beanDefinition = new RootBeanDefinition(BasePackages.class);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
addBasePackages(beanDefinition, packageNames);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
}

最后通过 @Import(AutoConfigurationPackages.Registrar.class) 注册BeanDefinition到IOC窗口中:

1
2
3
4
5
6
7
8
9
10
11
12
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}

@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}

AutoConfigurationImportSelector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class AutoConfigurationImportSelector implements 
DeferredImportSelector,
BeanClassLoaderAware,
ResourceLoaderAware,
BeanFactoryAware,
EnvironmentAware,
Ordered {

private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ClassLoader beanClassLoader;
private ResourceLoader resourceLoader;
private ConfigurationClassFilter configurationClassFilter;

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);

// 去加载各个组件jar下的 META-INF/spring.factories
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}

借助AutoConfigurationImportSelector,让SpringBoot应用将所有符合条件的bean定义都加载到当前SpringBoot创建并使用的IoC容器。

SpringApplication#run()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}

SpringApplication#run方法,首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:

  • 根据classpath里面是否存在某个特征类org.springframework.web.context. ConfigurableWebApplicationContext来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
  • 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。
  • 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
  • 推断并设置main方法的定义类。

SpringApplication实例初始化完成并且完成设置后,会开始执行run方法的逻辑。run方法中会初始化如下模块:

  • SpringApplicationRunListeners:应用启动监听器模块
  • ConfigurableEnvironment:配置环境模块
  • ConfigurableApplicationContext:应用上下文模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public ConfigurableApplicationContext run(String... args) {
Startup startup = Startup.create();
if (this.registerShutdownHook) {
SpringApplication.shutdownHook.enableShutdownHookAddition();
}

DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);

prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
startup.started();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), startup);
}
listeners.started(context, startup.timeTakenToStarted());
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
throw handleRunFailure(context, ex, listeners);
}
try {
if (context.isRunning()) {
listeners.ready(context, startup.ready());
}
}
catch (Throwable ex) {
throw handleRunFailure(context, ex, null);
}
return context;
}

上面代码有几个关键步骤:

  • SpringApplicationRunListeners listeners = getRunListeners(args), 创建了应用的监听器SpringApplicationRunListeners并开始监听
  • 加载SpringBoot配置环境(ConfigurableEnvironment),如果是通过web容器发布,会加载StandardEnvironment,其最终也是继承了ConfigurableEnvironment
  • prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner)配置环境(Environment)加入到监听器对象中(SpringApplicationRunListeners)
  • refreshContext(context) 注册 ConfigurableApplicationContext context 到 spring ioc

Spring-boot启动过程
http://example.com/2025/06/11/spring-boot启动过程/
作者
ares
发布于
2025年6月11日
许可协议