一起来看SpringBoot2源码(1)

用了很久的spring全家桶,一直没有时间深入挖掘一下,趁着年关将近,督促自己每天拿出一小时来研究这个工具内部的原理。
本文参考了battcn唐亚峰的学习路径,在此表示感谢。

1.

我们都知道SpringBoot2的故事都是从一个函数开始的:

@RestController
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @GetMapping("/demo1")
    public String demo1() {
        return "Hello demo1 in DemoApplication1";
    }

}

那么我们在此打上断点,一步一步往下看。

2.

     /**
	 * 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 SpringApplication from the specified sources using default settings and user supplied arguments.

看来和第一步的函数没有什么差别,那么为什么要重载这样一个函数呢?我们先把这个问题放放,继续往下看。

3.

    /**
	 * 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);
	}

终于到正主了,这个函数干了两件事,一是以我们提供的DemoApplication这个class生成了一个SpringApplication对象,二是以我们传入的args为参数执行了SpringApplication对象的run函数。

3.1

我们首先来看SpringApplication的构造函数,SpringApplication有两个构造函数,具体是现在第二个:

     /**
	 * Create a new {@link SpringApplication} instance. The application context will load
	 * beans from the specified primary sources (see {@link SpringApplication class-level}
	 * documentation for details. The instance can be customized before calling
	 * {@link #run(String...)}.
	 * @param resourceLoader the resource loader to use
	 * @param primarySources the primary bean sources
	 * @see #run(Class, String[])
	 * @see #setSources(Set)
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	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();
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

3.1.1

首先将SpringApplication对象的resourceLoader属性赋值为null,而后检测了参数非空并将参数primarySources列表中的多个class放入了一个Set并赋值给对象的primarySources属性。而后通过WebApplicationType的静态方法deduceFromClasspath()来确定应用的类型并赋给webApplicationType属性,方法内是通过判断某些特定的类是否存在来判断此springboot应用是REACTIVE的webservice、普通servlet的webservice还是普通应用。

3.1.2

接下去是将
getSpringFactoriesInstances(ApplicationContextInitializer.class)
函数的返回值赋给SpringApplication对象的initializers属性,我们来看看getSpringFactoriesInstances函数的实现:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
	return getSpringFactoriesInstances(type, new Class<?>[] {});
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = getClassLoader();
	// Use names and ensure unique to protect against duplicates
	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names);
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}

可以看到其中的SpringFactoriesLoader类型已经是spring core包中的类了,这个类将各种上下文情况所需的工厂类名从spring-boot-autoconfigure包和spring-boot包中的spring.factories文件中读取保存在一个map中,通过loadFactoryNames函数以type参数为key从map中取出一个类名列表,而后createSpringFactoriesInstances函数创建了上述一系列对象并将结果存在一个list中,需要注意的是此处创建对象使用的是各个类无参的构造函数,之后用AnnotationAwareOrderComparator对这个list进行排序,这个比较器也是spring core包中的,主要是按照对象的类型是否是PriorityOrdered接口的实现来排序的,如果都是PriorityOrdered类,那么按照spring中的OrderUtils根据类型的而各项属性计算出一个order值并进行排序。

3.1.3

setListeners((Collection)getSpringFactoriesInstances(ApplicationListener.class));

函数和

setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class));

函数类似,只不过是以ApplicationListener作为key去获取列表并生成对应工厂类并赋值给SpringApplication对象的listeners属性。
这样springboot所需的工厂类对象就准备完毕了。

最后deduceMainApplicationClass();函数将我们启动类赋给了mainApplicationClass属性。
到此SpringApplication对象生成完毕。

3.2

我们再来看SpringApplication的run函数。

/**
	 * Run the Spring application, creating and refreshing a new
	 * {@link ApplicationContext}.
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return a running {@link ApplicationContext}
	 */
	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;
	}

3.2.1

首先通过

StopWatch stopWatch = new StopWatch();
stopWatch.start();

这两行代码初始化了一个id和currentTaskName为"",startTimeMillis为当前时间戳的StopWatch对象。

3.2.2

然后通过

configureHeadlessProperty();

函数将系统参数改为无界面的。

3.2.3

SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();

这里获取了应用的监听器实例,我们来看内部实现

private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
				SpringApplicationRunListener.class, types, this, args));
	}

可以看到这里构造了一个SpringApplicationRunListeners对象,而构造的参数是一个日志对象和一个对象列表,对象列表是由我们在3.1.2中详细解释的getSpringFactoriesInstances函数获得的,此时获取类名列表的key为SpringApplicationRunListener,且在实例化对象时是调用了以SpringApplication类和一个String数组为参数的构造函数的。也就是说将之前生成的SpringApplication对象作为每一个listener的私有属性。
之后调用了SpringApplicationRunListenersstarting()函数

public void starting() {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}

启动了全部SpringApplicationRunListener
通过调试我们发现其实只是启动了一个listener EventPublishingRunListener,查看其starting()函数

@Override
	public void starting() {
		this.initialMulticaster.multicastEvent(
				new ApplicationStartingEvent(this.application, this.args));
	}

可以看到创建了一个应用开始运行事件,并将事件进行广播

@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			Executor executor = getTaskExecutor();
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				invokeListener(listener, event);
			}
		}
	}

可以看到,广播后,spring会获取所有订阅了该类型事件的listener并进行对应listener的处理。在这里,我们在3.1.3节注册的listener将会收到广播,知道应用已经启动了,并进行执行各自的onApplicationPreparedEvent(event);函数进行初始化。

3.2.4

接下来try块中的

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments;

是将main函数传入的参数进行了封装并和之前实例化的listeners一起作为参数来进行环境的初始化。

private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

首先ConfigurableEnvironment environment = getOrCreateEnvironment();函数依据3.1.1节确定的webApplicationType属性来创建一个ConfigurableEnvironment的实现,之前我们看到webApplicationType为SERVLET,此处返回的就是一个StandardServletEnvironment对象。
而后configureEnvironment函数将创建的StandardServletEnvironment进行配置

/**
	 * Template method delegating to
	 * {@link #configurePropertySources(ConfigurableEnvironment, String[])} and
	 * {@link #configureProfiles(ConfigurableEnvironment, String[])} in that order.
	 * Override this method for complete control over Environment customization, or one of
	 * the above for fine-grained control over property sources or profiles, respectively.
	 * @param environment this application's environment
	 * @param args arguments passed to the {@code run} method
	 * @see #configureProfiles(ConfigurableEnvironment, String[])
	 * @see #configurePropertySources(ConfigurableEnvironment, String[])
	 */
	protected void configureEnvironment(ConfigurableEnvironment environment,
			String[] args) {
		if (this.addConversionService) {
			ConversionService conversionService = ApplicationConversionService
					.getSharedInstance();
			environment.setConversionService(
					(ConfigurableConversionService) conversionService);
		}
		configurePropertySources(environment, args);
		configureProfiles(environment, args);
	}

首先将一个单例的ApplicationConversionService对象赋给StandardServletEnvironment对象的conversionService属性。
configurePropertySources(environment, args);函数中如果SpringApplication对象的defaultProperties属性非空,那么将其中的内容增加到StandardServletEnvironment的PropertySources列表中;如果我们在run函数上传入的args非空,那么也会将其中的内容增加到StandardServletEnvironment的PropertySources列表。
configureProfiles(environment, args);函数中将会把我们SpringApplication对象的additionalProfiles属性以及我们配置文件中的spring.profiles.active属性整合后赋给StandardServletEnvironment对象的activeProfiles属性。但是在这个函数中我们发现一个疑问

/**
	 * Configure which profiles are active (or active by default) for this application
	 * environment. Additional profiles may be activated during configuration file
	 * processing via the {@code spring.profiles.active} property.
	 * @param environment this application's environment
	 * @param args arguments passed to the {@code run} method
	 * @see #configureEnvironment(ConfigurableEnvironment, String[])
	 * @see org.springframework.boot.context.config.ConfigFileApplicationListener
	 */
	protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
		environment.getActiveProfiles(); 
		Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
		profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
		environment.setActiveProfiles(StringUtils.toStringArray(profiles));
	}

可以看到environment.getActiveProfiles();这个函数执行了两次,且其内部实现并没有特殊之处,doc中的说明如下:

Return the set of profiles explicitly made active for this environment. Profiles are used for creating logical groupings of bean definitions to be registered conditionally, for example based on deployment environment. Profiles can be activated by setting “spring.profiles.active” as a system property or by calling ConfigurableEnvironment.setActiveProfiles(String…).
If no profiles have explicitly been specified as active, then any default profiles will automatically be activated.

代码中的注释说明如下

//ensure they are initialized
//But these ones should go first (last wins in a property key clash)

/**
	 * Return the set of active profiles as explicitly set through
	 * {@link #setActiveProfiles} or if the current set of active profiles
	 * is empty, check for the presence of the {@value #ACTIVE_PROFILES_PROPERTY_NAME}
	 * property and assign its value to the set of active profiles.
	 * @see #getActiveProfiles()
	 * @see #ACTIVE_PROFILES_PROPERTY_NAME
	 */
	protected Set<String> doGetActiveProfiles() {
		synchronized (this.activeProfiles) {
			if (this.activeProfiles.isEmpty()) {
				String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME);
				if (StringUtils.hasText(profiles)) {
					setActiveProfiles(StringUtils.commaDelimitedListToStringArray(
							StringUtils.trimAllWhitespace(profiles)));
				}
			}
			return this.activeProfiles;
		}
	}

按照我的理解调用两次是确保environment的activeProfiles属性确实从之前配置好的PropertySources中读取了spring.profiles.active属性,也就是说对PropertySourcesPropertyResolver类的getProperty函数的实现是悲观的。如果有什么不对的地方请各位斧正。

之后是将environment对象配置给各个SpringApplicationRunListeners并执行每个listener的environmentPrepared函数。在这里我们只有一个EventPublishingRunListener,它将发布一个ApplicationEnvironmentPreparedEvent事件广播,将
environment对象通报给SpringApplication的所有listener,listener收到广播后会调用各自的onApplicationEnvironmentPreparedEvent函数进行初始化。

接下来bindToSpringApplication(environment);函数将运行环境和SpringApplication对象绑定。具体实现上是以environment对象的propertySources为基础生成一个Binder对象,而后将相关信息绑定到SpringApplication对象上,若没有额外的配置,这一步将不做任何改变。

之后如果environment不是自定义的,那么会用EnvironmentConverter将environment对象转换为StandardEnvironment类型。

最后的ConfigurationPropertySources.attach(environment);函数将environment对象的propertySources属性封装成一个SpringConfigurationPropertySources对象,并以configurationProperties为key加入到propertySources属性列表的首位。

到此environment对象配置完成。

3.2.5

接下去的configureIgnoreBeanInfo(environment);函数将系统变量spring.beaninfo.ignore配置为environment对象propertySources属性列表中名为spring.beaninfo.ignore的属性值,在这里,由于没有相应属性,此系统变量默认为true。

3.2.6

接下来的printBanner(environment);函数就是大家启动springboot时在控制台看到的这个图案了
在这里插入图片描述
函数中会按照你配置的Banner.Mode来决定是打在Console里还是打在日志里,亦或是什么都不打。如果想改变启动时的图案,那么可以在项目的
resouces 目录下添加banner.txt、banner.jpg等等文件即可,支持大部分图片格式。

3.2.7

然后调用createApplicationContext()函数,依据3.1.1节确定的webApplicationType属性反射创建对应的ConfigurableApplicationContext对象,这里我们的webApplicationType=SERVLET,所以返回了一个AnnotationConfigServletWebServerApplicationContext对象。

3.2.8

接下来的函数

exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);

也是通过3.1.2getSpringFactoriesInstances函数,以SpringBootExceptionReporter为key获取类名列表,并以上节生成的ConfigurableApplicationContext对象作为参数构造各个reporter对象。

3.2.8

之后的prepareContext函数配置了应用运行的上下文。

private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
		applyInitializers(context);
		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);
		}
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		listeners.contextLoaded(context);
	}

postProcessApplicationContext函数中,如果SpringApplication对象配置了beanNameGenerator、resourceLoader或addConversionService,那么将对应的内容配置到上下文中。
接下来listeners.contextPrepared(context);函数中调用了所有SpringApplicationRunListener的contextPrepared函数。在这里我们只有一个EventPublishingRunListener,它将发布一个ApplicationContextInitializedEvent事件广播,订阅了对应事件的listener将会调用各自的onApplicationEvent函数进行对应的操作。
之后的代码中把我们run函数的参数applicationArguments注册为一个名为springApplicationArguments的单例bean,把之前的Banner对象注册为一个名为springBootBanner的单例bean。最后将上下文中的beanFactory配置为同名bean不可覆盖。
之后load(context, sources.toArray(new Object[0]));函数将我们在main函数中传给run函数的XXX.class实例化并注册成bean。
最后的listeners.contextLoaded(context);函数将调用所有SpringApplicationRunListener的contextLoaded函数。在这里我们只有一个EventPublishingRunListener,它将SpringApplication对象所有的listeners都赋给上下文对象的applicationListeners属性,并把上下文对象赋给部分实现了ApplicationContextAware接口需要用到上下文的listener的context属性,最后EventPublishingRunListener发布一个ApplicationPreparedEvent事件广播,订阅了对应事件的listener将会调用各自的onApplicationEvent函数以及onApplicationPreparedEvent函数进行对应的操作。

3.2.9

接下来的refreshContext函数将上下文内的属性刷新了一遍,具体如下:

private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}

第一步的refresh函数步骤比较多

    @Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// 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();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

prepareRefresh函数记录了refresh开始时间,设置了开始标记,打印出开始日志,将上下文对象部分未初始化属性初始化,校验所有必要属性的值,并初始化上下文对象的earlyApplicationEvents属性。
obtainFreshBeanFactory函数将上下文对象的id赋值给和上下文对象的beanFactory对象的id属性,并将这个id和beanFactory自身的一个弱引用保存在beanFactory的一个集合serializableFactories中,最后将beanFactory对象返回。
prepareBeanFactory函数中,首先将beanFactory自带的beanClassLoader替换为上下文对象的classLoader;而后将部分为null的属性进行初始化;之后将一系列接口添加到ignoredDependencyInterfaces列表中,该列表中的接口将不会再进行依赖解析,例如EnvironmentAware、EmbeddedValueResolverAware、MessageSourceAware等之前已经通过生成对应对象解析过依赖关系的一些接口以及ResourceLoaderAware、ApplicationEventPublisherAware、ApplicationContextAware等在下一步解析依赖关系的一些接口;然后将上一步提到的BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext等类型的装配依赖关系注册到beanFactory的resolvableDependencies属性中;创建一个ApplicationListenerDetector对象放到beanFactory的bean后处理器集合beanPostProcessors中;如果beanFactory中包含名为loadTimeWeaver的bean,那么创建了一个LoadTimeWeaverAwareProcessor对象增加到beanFactory的bean后处理器集合中,上下文对象会另外创建一个ClassLoader来作为beanFactory的临时ClassLoader,让loadTimeWeaver对象能对所有bean进行后处理;最后把environment相关的bean注册给beanFactory。
postProcessBeanFactory函数是一个抽象函数,为接下来对beanFactory对象进行后处理做准备工作,各个子类实现不同,这里我们看下此处AnnotationConfigServletWebServerApplicationContext的实现。首先是创建了一个WebApplicationContextServletContextAwareProcessor对象增加到beanFactory的bean后处理器集合中,而后将ServletContextAware接口添加到上段提及的ignoredDependencyInterfaces列表中,而后在beanFactory中注册了AnnotationConfigServletWebServerApplicationContext需要的四个域 (“request”, “session”, “globalSession”, “application”)。
invokeBeanFactoryPostProcessors函数中,由于我们的beanFactory是DefaultListableBeanFactory,实现了BeanDefinitionRegistry接口,可以注册bean定义,那么将上下文对象beanFactoryPostProcessors属性中的后处理器分成两组,一组是Bean定义注册表后处理器,将会执行这些类的postProcessBeanDefinitionRegistry函数,另一组是普通后处理器。之后将beanFactory对象beanDefinitionNames属性中所有实现了BeanDefinitionRegistryPostProcessor接口的类的类名取出,按照PriorityOrdered,Ordered,others的顺序,实例化并调用此类对象的postProcessBeanDefinitionRegistry函数,完成这些后处理器对beanDefinitionRegistry对象的后处理。而后调用所有后处理器的postProcessBeanFactory方法,完成这些后处理器对beanFactory对象的后处理。如果beanFactory未实现BeanDefinitionRegistry接口的话,只需要调用所有后处理器的postProcessBeanFactory方法。然后将beanFactory对象beanDefinitionNames属性中所有实现了BeanFactoryPostProcessor接口的类的类名取出,按照PriorityOrdered,Ordered,others的顺序,实例化并调用此类对象的postProcessBeanFactory函数,完成这些后处理器对beanFactory对象的后处理。
registerBeanPostProcessors函数将beanFactory对象beanDefinitionNames属性中所有实现了BeanPostProcessor接口的类的类名取出,按照PriorityOrdered,Ordered,others的顺序,实例化并将这些对象按顺序添加到beanFactory对象的beanPostProcessors属性中。然后将所有实现了MergedBeanDefinitionPostProcessor接口的内部BeanPostProcessor对象再一次依序添加到beanFactory对象的beanPostProcessors列表中,也就是说这部分后处理器将会调用两次。最后在列表的末尾加入一个ApplicationListenerDetector对象,之后这个对象会把之前的内部BeanPostProcessor对象注册为应用的监听器。
initMessageSource函数初始化了上下文对象的messageSource属性。如果beanFactory对象中包含名为messageSource的bean或bean定义,那么将其实例化作为上下文对象的messageSource属性,并将其设置对上下文对象的父类可见,否则新建一个DelegatingMessageSource对象作为上下文对象的messageSource属性,并将其注册给beanFactory对象。
onRefresh函数将会根据我们上下文对象的类型初始化一些特殊的属性。此处我们的AnnotationConfigServletWebServerApplicationContext对象将初始化其themeSource属性。
registerListeners函数将上下文对象的applicationListeners属性中的所有ApplicationListener对象以及beanFactory对象beanDefinitionNames属性中所有实现了ApplicationListener接口的类的实例化引用增加到applicationEventMulticaster属性的defaultRetriever属性的applicationListeners属性中,这样上下文对象的applicationEventMulticaster属性初始化完成,如果之前有ApplicationEvent应用事件没有广播,将会进行广播,前面增加到applicationListeners属性中的所有ApplicationListener都会收到广播并进行处理,最早的EventPublishingRunListener对象完成了它的使命。
finishBeanFactoryInitialization函数首先将beanFactory对象剩余的一些属性进行初始化,并将此时beanFactory对象的beanDefinitionNames属性留下快照保存在frozenBeanDefinitionNames属性中,并且冻结其配置的变化,完成beanFactory对象的所有配置。最后把beanDefinitionNames属性中除了懒加载的所有bean初始化。
finishRefresh函数首先清理了缓存,而后初始化了上下文对象的生命周期处理器并刷新了处理器的状态,利用applicationEventMulticaster广播了一个ContextRefreshedEvent事件,通知所有listener上下文对象已刷新完毕,可以进行相应的处理。如果上下文对象有父类,那么其父类也会进行同样的广播。如果没有做另外配置,我们会把应用注册到MBeanServer并由MBeanServer进行管理。由于此处的上下文对象是ServletWebServerApplicationContext类型的,还需要调用其webServer属性的start函数,启动webserver,由于我们没有进行配置,此处启动的是springboot自带的tomcat。如果启动成功,广播一个ServletWebServerInitializedEvent事件通知所有监听器webserver已启动完毕。
最后将所有涉及的缓存清空。

在完成刷新工作后,springboot还启动了一个守护线程,让jvm关闭时关闭应用进程及相关线程。

3.2.10

完成上下文刷新工作后,调用了afterRefresh函数,函数中没有实现,应该是给之后的更新留坑。

3.2.11

stopWatch.stop()函数记录了之前任务流程的总运行时间。
callRunners(context, applicationArguments);
listeners.started(context);函数调用了之前3.2.3节创建的SpringApplicationRunListeners的listeners列表中每个listener的started函数,我们依然只有一个EventPublishingRunListener监听器,它的started函数利用上下文对象的applicationEventMulticaster广播器广播了一个ApplicationStartedEvent应用启动事件给对应的listener并进行相应的处理。

3.2.12

callRunners(context, applicationArguments);函数挑选出beanFactory对象beanDefinitionNames列表和manualSingletonNames列表中实现了ApplicationRunner接口或者CommandLineRunner接口的类,分别调用了这些类的run方法,启动一些额外配置的线程。

3.2.13

listeners.running(context);函数调用了之前3.2.3节创建的SpringApplicationRunListeners的listeners列表中每个listener的running函数,我们依然只有一个EventPublishingRunListener监听器,它的running函数利用上下文对象的applicationEventMulticaster广播器广播了一个ApplicationReadyEvent应用已准备好事件给对应的listener并进行相应的处理。

到此,SpringBoot的启动流程完成。

最后我们来看下第2节中我们提出的问题,如果我们另外创建一个类DemoApplication2,在main函数中调用另一个run方法

@RestController
@SpringBootApplication
public class DemoApplication2 {

    public static void main(String[] args) {

        SpringApplication.run(new Class<?>[] { DemoApplication.class,DemoApplication2.class }, args);
    }

    @GetMapping("/demo2")
    public String demo2() {
        return "Hello demo2 in DemoApplication2";
    }
}

我们从上文可以看到,springboot对这个参数的处理只在于将它存放在SpringApplication对象的primarySources属性中,作为一组普通的bean来处理。上面的main方法可以正常运行。

这里我们没有看到我们的@RestController是如何起作用了,下面的日志在这里插入图片描述
也是在main函数运行完毕后才打出的。因此,相关解析我们下次再说。

猜你喜欢

转载自blog.csdn.net/sinat_25444819/article/details/86582883