SpringBoot 是一个基于 Spring 框架的快速开发框架,旨在简化 Spring 应用程序的开发和部署。在本文中,我们将深入分析 SpringBoot 启动过程的源代码,并提供必要的解释和说明。
SpringBoot启动过程简介
SpringBoot应用程序的启动过程可以分为以下几个步骤:
加载应用程序上下文
扫描应用程序中的所有组件
自动配置应用程序环境
启动嵌入式Web服务器
在下面的章节中,我们将逐一分析这些步骤的源代码。
加载应用程序上下文
SpringBoot 应用程序的上下文是一个包含所有应用程序组件的容器。在启动过程中,SpringBoot 会加载并初始化这个容器。
这个步骤的源代码在SpringApplication类中。具体来说,SpringApplication类的run方法是这个过程的入口点。在这个方法中,Spring Boot会通过调用createApplicationContext方法来创建应用程序上下文。
下面是createApplicationContext方法的源代码:
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable to create a default ApplicationContext, " +
"please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
复制代码
在这个方法中,SpringBoot 会根据应用程序类型(Servlet或Reactive)选择合适的上下文类。然后,它会使用 Java 反射机制来实例化这个类并返回一个可配置的应用程序上下文对象。
扫描应用程序中的所有组件
在上一步中,SpringBoot创建了应用程序上下文。在这一步中,SpringBoot会扫描应用程序中的所有组件并将它们注册到应用程序上下文中。
这个步骤的源代码在SpringApplication类中的scan方法中。具体来说,在这个方法中,SpringBoot 会创建一个SpringBootBeanDefinitionScanner对象,并使用它来扫描应用程序中的所有组件。
下面是scan方法的源代码:
private void scan(String... basePackages) {
if (ObjectUtils.isEmpty(basePackages)) {
return;
}
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(
this.includeFilters, this.excludeFilters, this.resourceLoader);
scanner.setResourceLoader(this.resourceLoader);
scanner.setEnvironment(this.environment);
scanner.setIncludeAnnotationConfig(this.useAnnotatedConfig);
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return getExcludeClassNames().contains(className);
}
});
for (String basePackage : basePackages) {
scanner.findCandidateComponents(basePackage).forEach(this.componentDefinitions::add);
}
}
复制代码
在这个方法中,SpringBoot 会创建一个ClassPathScanningCandidateComponentProvider对象,并使用它来扫描应用程序中的所有组件。这个对象会扫描指定包路径下的所有类,并将它们转换为 Spring 的 Bean 定义。这些 Bean 定义将被注册到应用程序上下文中。
自动配置应用程序环境
在上一步中,SpringBoot将应用程序中的所有组件注册到应用程序上下文中。在这一步中,SpringBoot将自动配置应用程序环境,包括配置数据源、事务管理器、JPA等。
这个步骤的源代码在SpringApplication类中的configureEnvironment方法中。在这个方法中,Spring Boot会创建一个SpringApplicationRunListeners对象,并使用它来配置应用程序环境。
下面是configureEnvironment方法的源代码:
private void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addCommandLineProperties) {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
environment.getPropertySources().addLast(new CommandLinePropertySource(applicationArguments));
}
this.listeners.environmentPrepared(environment);
if (this.logStartupInfo) {
this.logStartupInfo(environment);
}
ConfigurationPropertySources.attach(environment);
Binder.get(environment).bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(this.sources));
if (!this.isCustomEnvironment) {
EnvironmentConverter.configureEnvironment(environment, this.deduceEnvironmentClass());
}
this.listeners.environmentPrepared(environment);
}
复制代码
在这个方法中,SpringBoot 会创建一个ApplicationArguments对象,并将其转换为一个命令行属性源。然后,它会调用listeners中的environmentPrepared方法来通知应用程序环境已经准备好了。随后,SpringBoot 会绑定属性源到应用程序环境中,并调用listeners中的environmentPrepared方法来通知应用程序环境已经准备好了。
启动嵌入式Web服务器
在上一步中,SpringBoot 将应用程序环境自动配置完成。在这一步中,SpringBoot 将启动嵌入式Web服务器,以便应用程序能够提供 Web 服务。
这个步骤的源代码在SpringApplication类中的run方法中。具体来说,在这个方法中,SpringBoot 会根据应用程序类型(Servlet或Reactive)选择合适的嵌入式Web服务器,并使用它来启动应用程序。
下面是run方法的源代码:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
复制代码
在这个方法中,SpringBoot 会使用一个StopWatch对象来计算应用程序启动时间。然后,它会调用listeners中的starting方法来通知应用程序即将启动。接着,SpringBoot 会准备应用程序环境,并使用它来创建应用程序上下文。随后,SpringBoot 会调用listeners中的started方法来通知应用程序已经启动。最后,SpringBoot 会调用callRunners方法来运行所有的CommandLineRunner和ApplicationRunner组件。
总结
在本文中,我们深入分析了 SpringBoot 应用程序的启动过程的源代码。我们了解了 SpringBoot 如何加载应用程序上下文、扫描应用程序中的所有组件、自动配置应用程序环境以及启动嵌入式Web服务器。这些步骤的源代码展示了 SpringBoot 如何简化应用程序的开发和部署。