本篇文章研究的SpringBoot版本为2.3.3-RELEASE,主要记录一下Web类型SpringBoot项目的启动流程。
一般的springboot的启动类长这个样子。
@SpringBootApplication
public class SampleApplication {
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
}
}
可以看到这个main方法里面只有简单的一行code,这一行就是理解springboot启动过程的入口了。其中run方法的参数有两个,一个是Class类型,用于定位basePackage,一个是String[]类型,单纯的对运行参数进行转发。点进去看一看它做了什么事情,大致上可以认为分为两个部分,首先是new了一个SpringApplication对象,然后调用了它的run方法。
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified source using default settings.
* @param primarySource the primary source to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class> primarySource, String... args) {
return run(new Class>[] { primarySource }, args);
}
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified sources using default settings and user supplied arguments.
* @param primarySources the primary sources to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
new SpringApplication 阶段做了哪些事情
结合code我们看一下在new SpringApplication时,SpringBoot框架做了哪些事情
public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//设置primarySources
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//确定application类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//读取配置在spring.factories文件中的ApplicationContextInitializer,需要注意一下,在这个方法里面,springboot会开始读取spring.factories里面的配置并且根据ClassLoader的不同,缓存到内存中。这个之后再展开讲。
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//读取配置在spring.factories文件中的ApplicationListener。
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//确定入口类
this.mainApplicationClass = deduceMainApplicationClass();
}
首先就是配置了primarySources这个属性
这个属性主要用于之后确定扫描的basePackage。具体的原理我们可以之后再讲。
其次,就是确定一下我们这个项目的类型
判断方法比较简单粗暴,就是看一下你项目中有没有对应类型的标志性的类。举个例子,如果你有WEBFLUX_INDICATOR_CLASS这个类的话,就认为你的项目类型是REACTIVE。
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
然后就是通过SPI机制获取spring.factories的配置
在构造器中,我们可以很明显的看到有两次对于getSpringFactoriesInstances方法的调用,这个方法兜兜转转,最后会调用到SpringFactoriesLoader的loadSpringFactories的方法。在这个方法中,对于相同的classsloader,只会尝试load一次spring.factories文件中的配置。因此,一般情况下,这个阶段已经完成了对spring.factories的扫描和加载。
private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
从load好的spring.factorie中获取配置好的ApplicationContextInitializer和ApplicationListener
这个就没啥好说了。需要注意的是,这些ApplicationListener是在调用run方法之前就已经加载进来的,所以这些ApplicationListener不仅仅能够接收ApplicationStartedEvent以及之后的ApplicationEvent,之前的比如ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent等等事件类型也都可以接收到。
确定主类
这个的逻辑也比较简单,首先获取当前的方法调用栈(这点还是有疑问为啥不通过Thread.currentThread().getStackTrace()这种看起来更加正式的方法来获取stackTrace)。然后从栈顶一个一个找下去,直到找到一个名字为main的方法,这个方法所对应的类就可以认为是主类了。
private Class> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
为了验证我也写个了sample
public class Test {
public static void main(String[] args) {
SampleApplication.main(args);
}
}
@SpringBootApplication
public class SampleApplication {
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
}
}
发现调用Test.main方法之后,定位出来的mainclass确实是SampleApplication。
run()方法做了哪些事情
还是先看这些稍微加了点注释的源码
public ConfigurableApplicationContext run(String... args) {
//起了个timer,用于计时,不用管
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection exceptionReporters = new ArrayList<>();
//这个就是配置个系统环境变量 java.awt.headless
configureHeadlessProperty();
//获取SpringApplicationRunListeners,这个和之前提到的ApplicationListener是两个不同的接口。两者间的联系在于,
//在springboot的默认实现中,有一个特殊的类EventPublishingRunListener会继承SpringApplicationRunListeners,
//他会在springboot的特殊阶段发布对应的ApplicationEvent,并且被ApplicationListener接收。
SpringApplicationRunListeners listeners = getRunListeners(args);
//进入starting阶段,发布ApplicationStartingEvent事件。
listeners.starting();
try {
//包装启动参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备environment,确定propertiesSource。发布ApplicationEnvironmentPreparedEvent。
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//配置环境变量spring.beaninfo.ignore
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
//创建应用上下文
context = createApplicationContext();
//获取SpringBootExceptionReporter
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//发布ApplicationContextInitializedEvent事件和ApplicationPreparedEvent事件
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
/划重点,进行bean的初始化工作
refreshContext(context);
//暂时是个空function。
afterRefresh(context, applicationArguments);
//关闭计算器,也就是前面创建的那个timer
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//发布ApplicationStartedEvent事件
listeners.started(context);
//运行ApplicationRunner和CommandLineRunner
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//发布ApplicationReadyEvent事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
可以看到这个方法比较复杂,基本上每一行语句都对应着一个函数。为了更加方便地捋清楚整个flow,我们跟着SpringApplicationRunListener,看一下它的每个方法都在什么时候被trigger,两个方法之间都做了什么事情。
首先看一下SpringApplicationRunListener(s)有哪些方法
SpringApplicationRunListener并没有直接被SpringApplication调用,而是作为一个整体SpringApplicationRunListeners被统一调用,它有starting、environmentPrepared、contextPrepared、contextLoaded、started、running、failed等七个方法。由于这一篇我们只讲SpringBoot的启动流程,因此只讨论前六个方法,看他们的调用时机。
public interface SpringApplicationRunListener {
/**
* Called immediately when the run method has first started. Can be used for very
* early initialization.
*/
default void starting() {
}
/**
* Called once the environment has been prepared, but before the
* {@link ApplicationContext} has been created.
* @param environment the environment
*/
default void environmentPrepared(ConfigurableEnvironment environment) {
}
/**
* Called once the {@link ApplicationContext} has been created and prepared, but
* before sources have been loaded.
* @param context the application context
*/
default void contextPrepared(ConfigurableApplicationContext context) {
}
/**
* Called once the application context has been loaded but before it has been
* refreshed.
* @param context the application context
*/
default void contextLoaded(ConfigurableApplicationContext context) {
}
/**
* The context has been refreshed and the application has started but
* {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
* ApplicationRunners} have not been called.
* @param context the application context.
* @since 2.0.0
*/
default void started(ConfigurableApplicationContext context) {
}
/**
* Called immediately before the run method finishes, when the application context has
* been refreshed and all {@link CommandLineRunner CommandLineRunners} and
* {@link ApplicationRunner ApplicationRunners} have been called.
* @param context the application context.
* @since 2.0.0
*/
default void running(ConfigurableApplicationContext context) {
}
/**
* Called when a failure occurs when running the application.
* @param context the application context or {@code null} if a failure occurred before
* the context was created
* @param exception the failure
* @since 2.0.0
*/
default void failed(ConfigurableApplicationContext context, Throwable exception) {
}
}
在starting方法之前
springboot在调用run方法后,基本上第一时间就trigger了listener的starting方式,在这之前,基本上没做啥事。
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
一个是创建了一个timer(StopWatch)用来之后计算启动耗时,下面这段日志就是它功能的体现。
2021-09-22 16:29:38.241 INFO 8044 --- [ main] com.sample.app.SampleApplication : Started SampleApplication in 32.023 seconds (JVM running for 121.055)
一个是调用configureHeadlessProperty方法来设置java.awt.headless的系统环境变量,这个默认值是true。
private boolean headless = true;
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
再有就是从之前解析好的spring.factories中读取所有的SpringApplicationRunListeners。
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class>[] types = new Class>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
environmentPrepared之前
对应的代码在run方法里对应两行,其中第一行是把系统参数包装一下,使得用起来更加方便。第二行则是进行一些环境的准备工作。
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
我们重点看prepareEnvironment这个方法中做了哪些工作。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
可以清楚的看到,在调用listeners.environmentPrepared(environment);
方法之前,只有三行code,分别做了三件事情。
getOrCreateEnvironment根据应用类型来创建ConfigurableEnvironment
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
configureEnvironment 来配置propertiesSource和profiles
这里面主要完成了ropertiesSource和profiles之的配置。
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
其中,configurePropertySources是配置environment中的propertySources属性。propertySources是用于记录系统的配置参数可以从哪里读取,读取的结果是什么。各个propertySource在其中的先后顺序体现了他们彼此的优先级。默认的propertySources包括systemProperties和systemEnvironment两个。在configurePropertySources步骤中根据代码或者参数的不同,可能会额外加上defaultProperties和commandLineArgs两个propertySource。
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
MutablePropertySources sources = environment.getPropertySources();
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
PropertySource> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(
new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
configureProfiles则是试图从现有的propertySources中尝试获取spring.profiles.active参数。需要注意的是,此时的propertySources并不包含与你的配置文件(比如application.properties)相对应的propertySource,所以无论你的配置文件怎么配置,对这个阶段都不造成影响。
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
Set profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
ConfigurationPropertySources.attach(environment)将自己置于propertySources的第一个的位置
前面也讲了,各个PropertySource在propertySources中的先后顺序决定了优先级,现在把ConfigurationPropertySources放到第一位,也就意味着它的优先级是最高的。细心的人应该也会注意到ConfigurationPropertySources.attach(environment)这一行前后调用了两次,这个也是为了确保ConfigurationPropertySources优先加载的顺序。
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
PropertySource> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
if (attached != null && attached.getSource() != sources) {
sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
attached = null;
}
if (attached == null) {
sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
new SpringConfigurationPropertySources(sources)));
}
}
在environmentPrepared方法
EventPublishingRunListener发布ApplicationEnvironmentPreparedEvent事件
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
订阅了这个事件的ApplicationListener也找到了几个,我们先记录下来。
- ConfigFileApplicationListener
- AnsiOutputApplicationListener
- LoggingApplicationListener
- ClasspathLoggingApplicationListener
- BackgroundPreinitializer
- DelegatingApplicationListener
- FileEncodingApplicationListener
其中比较有意思的是ConfigFileApplicationListener,因此我们展开讲一下,剩下的等有空了再说。
在接收到ApplicationEnvironmentPreparedEvent之后,做了这件事
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
// 加载所有EnvironmentPostProcessor并排序
List postProcessors = loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
//依次调用EnvironmentPostProcessor的postProcessEnvironment方法
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
在这块逻辑中,会调用它自身的postProcessEnvironment方法,从而实现配置文件的加载
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
}
/**
* Add config file property sources to the specified environment.
* @param environment the environment to add source to
* @param resourceLoader the resource loader
* @see #addPostProcessors(ConfigurableApplicationContext)
*/
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
new Loader(environment, resourceLoader).load();
}
其中,loader的load方法我们也记录一下
void load() {
FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
(defaultProperties) -> {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
initializeProfiles();
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
if (isDefaultProfile(profile)) {
addProfileToEnvironment(profile.getName());
}
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
applyActiveProfiles(defaultProperties);
});
}
contextPrepared之前
这部分在SpringApplication对应的代码主要是这么三行
//初始化
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
第一行是根据应用类型,创建ApplicationContext的实例
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 create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
第二行就是获取SpringBootExceptionReporter的实例。
第三行所对应的方法比较复杂,和我们这个阶段对应的主要是这么几行。
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
....
}
其中applyInitializers(context)
这行就是对我们在new SpringApplication()
时找到的ApplicationContextInitializer进行初始化工作。如果我们要增加自己的BeanFactoryPostProcessor,就可以创建一个ApplicationContextInitializer来做。BeanFactoryPostProcessor这个类很多同学可能并不清楚,这也是Spring的拓展点之一,我们可以使用它来读取并覆盖应用程序上下文中配置的bean属性。
contextLoaded之前
这个部分只对应了run 方法中的一行,和上面一段代码块属于同一个私有方法。对应到这个阶段,主要就是注册了一些特殊的sington的bean,然后尝试去注册primarySource的definition,为之后的componentScan时找basePackage做好准备。
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
...
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set
其中的getAllResource长这个样子
public Set
load(context, sources.toArray(new Object[0]))
这一行就不展开讲了,就是取sources的第一个元素,尝试去注册其对应的definition,之后找basePackage的时候会用到
started之前
这个在run方法中只对应了一行代码refreshContext(context);
。它会间接的对ApplicationContext的refresh方法进行调用。由于我们现在debug的是一个web项目,因此走进去对应的ApplicationContext的实现类是ServletWebServerApplicationContext。它的refresh方法可以简单的视为对父类refresh方法的调用,它的onRefresh方法则在父类(AbstractApplicationContext)的方法之上创建了WebServer。
@Override
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
}
catch (RuntimeException ex) {
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.stop();
}
throw ex;
}
}
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
我们看一下在父类的refresh方法中做了哪些事情,其中有几个点需要注意一下,一个是扫描并注册beanDefinition的行postProcessBeanFactory
,一个是创建WebServer的行onRefresh
,一个是根据definition初始化bean的行finishBeanFactoryInitialization
,一个是启动WebServer的行finishRefresh
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//这个步骤会清除缓存,检查是否缺少环境参数,保存前期加载的ApplicationListener实例(从applicationListeners属性到earlyApplicationListeners)
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//这个步骤通过调用ignoreDependencyInterface,registerResolvableDependency,addBeanPostProcessor等方法完成beanFactory的一些配置。其中addBeanPostProcessor方法会被调用至少两次,传参分别为ApplicationContextAwareProcessor和ApplicationListenerDetector
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
//这个步骤会加载bean的definition
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
//Web类型的App将在这个步骤创建WebServer,但是WebServer此时并不会立刻启动
// Initialize other special beans in specific context subclasses.
onRefresh();
//注册监听
// Check for listener beans and register them.
registerListeners();
//这个时候会根据之前获取的的bean的definition,进行bean的初始化
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
//Web类型的App将在这个步骤启动之前创建好的WebServer
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
...
}
}
}
running之前
这个部分就比较简单了,主要是invoke了ApplicationRunner和CommandLineRunner
callRunners(context, applicationArguments);
function的内容长这个样子
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
最后
在这之后,我们的web类型的application就启动成功了!