SpringBoot一般启动方式
main方法中:
SpringApplication.run(XXX.class, args);
过程
SpringApplication的静态方法提供了哪些服务呢?源码如下:
/**
* 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);
}
如注释所写,使用默认的设置和具体的class来创建一个可运行的Spring 上下文对象(英文不好,大体是这个意思)。
其中入参primarySource,即为入口函数所在的class。
是如何创建这个Spring上下文的呢,继续追踪被调用的run方法,源码如下:
/**
* 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,生成一个Spring上下文对象,然后再执行run方法。
这边需要关注两个点:
- 创建这个Spring上下文,过程是什么样的?
- 上下文里面的run方法做了哪些事情?
创建SpringApplication
首先,先来看下是如何创建这个Spring上下文的。查看SpringApplication的构造器:
@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();
}
这里可以看到有两个参数,resourceLoader,外部传值为null,不用关心,primarySources即为main的class,这个也很好理解,从上面方法传入。
可以看到,这边主要做了这么几件事情:
- 初始化WebApplicationType枚举(none,servlet套接字,reactive响应式)
- setInitializers,初始化ApplicationContextInitializer集合,注:继承自ApplicationContextInitializer接口,用以在Spring容易refresh之前执行一个回调函数initialize。
- setListeners,初始化ApplicationListener监听器集合
- 初始化入口函数信息
获取web应用类型
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
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;
}
首先可以看到ClassUtils.isPresent中,所有的ClassLoader实参,均为false,故类型值,只可能是WebApplicationType.SERVLET
初始化构造器集合
可以看到,设置构造器集合的数据来源是getSpringFactoriesInstances方法,入参为ApplicationContextInitializer.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;
}
做了这么几件事情:
- 获取了一个String的names集合
- 看方法名称可知,是根据这个String集合,创建了一堆的sprintFactory的实例对象
- 对所有实例对象进行排序(不清楚排序是为了做啥)
以上三步,2,3两步比较容易理解,不做多阐述,我们来详细讲解下步骤1,不多说,直接上源码:
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> 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);
}
}
源码显示,会先调用loadFactoryNames方法,内部会再调用loadSpringFactories方法,获取一个Map,任何根据当前入参class,获取一个string集合。
那么,这边的factoryType是谁呢?其实就是刚才上面特意提到的ApplicationContextInitializer.class,其getName的结果为org.springframework.context.ApplicationContextInitializer
下面我们再继续研究下这个Map是怎么产生的,里面都有些啥。
第一步,先从cache中读取缓存,第一次进入,肯定为空,继续往下执行
第二步,classLoader.getResources,ClassLoader.getSystemResources,很明显,是从META-INF/spring.factories中读取资源文件
第三步,解析资源文件。
说明:
- SpringBoot会去遍历所有当前jar中所有路径为META-INF/spring.factories的文件,并会将其数据进行汇总
- 默认有两处包含spring.factory文件,分别为spring-boot,spring-boot-autoconfigure,如图:
可以看到,两个文件中,包含org.springframework.context.ApplicationContextInitializer一共有7条记录(5+2),所以上面用name去Map中获取配置的时候,一共可以取出7条记录来
初始化监听器集合
同理,与构造器初始化一致,调用同一个方法,共会初始化出11个监听器实例对象。请记住这个数字11,后面会很有用处。
初始化入口函数信息
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;
}
这个就更简单了,从当前的线程栈中获取main方法所在的class,并反射构建出它的Class对象。
至此,我们终于完成了一个spring上下文的构建,下面该是执行它的run方法了。
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;
}
- 创建一个StopWatch,并启动,来记录时间
- 声明一个应用上下文,此时为null
ConfigurableApplicationContext是applicationContext的子类,在此基础上,增加了一系列配置应用上下文,以及控制上下文生命周期的功能。 - 创建一个异常报告的集合,启动中的异常信息会往里面进行放入
- configureHeadlessProperty,设置一些服务器环境,在没有显示器,外部IO的情况下,也可以启动
- 创建spring运行时的监听器集合,并start
是不是有点懵呢,下面对一些步骤,具体来看下内部实现
创建运行时监听器集合SpringApplicationRunListeners
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
啦啦啦,又见到我们的老朋友getSpringFactoriesInstances了,还记得上面的7,11这两个数字吗,没错,就是这位老兄帮忙创建出来的。很明显,这次也是从我们的spring.factory中寻找key为SpringApplicationRunListener的配置,并将它们进行创建实例对象,我们看下配置文件中的定义:
所以这边会创建一个EventPublishingRunListener的监听器实例,并将该类赋值给一个包装类SpringApplicationRunListeners。
下面,是时候进行RunListener的start方法了:
private final SpringApplication application;
// 上面在创建EventPublishingRunListener时会执行它的构造方法,如下:
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
// 开始strat
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
首先,我们可以看到在这边,创建了一个event事件,传入参数是application成员变量,类型是SpringApplication,没错,这个application,就是我们在最前面开始就创建出来的spring上下文对象。
通过构造方法可知,initialMulticaster是我们new了一个SimpleApplicationEventMulticaster对象,用来监听事件。
下面,我们继续跟进它的multicastEvent方法:
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
我们发现了一个执行器executor,以及遍历了一个getApplicationListeners方法的返回值listener的集合。是不是有点眼熟呢,我们之前好像是有创建过listener呢,这个先放一放,我们进入这个方法内部去看下到底做了些啥:
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = (source != null ? source.getClass() : null);
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// Quick check for existing entry on ConcurrentHashMap...
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
// Fully synchronized building and caching of a ListenerRetriever
synchronized (this.retrievalMutex) {
retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
retriever = new ListenerRetriever(true);
Collection<ApplicationListener<?>> listeners =
retrieveApplicationListeners(eventType, sourceType, retriever);
this.retrieverCache.put(cacheKey, retriever);
return listeners;
}
}
else {
// No ListenerRetriever caching -> no synchronization necessary
return retrieveApplicationListeners(eventType, sourceType, null);
}
}
入参有两个参数,一个event,一个是eventType,值的来源,都是上面multicastEvent方法传下来的ApplicationStartEvent。抛开代码中从缓存中读取数据的逻辑,真正获取listener的逻辑其实只有下面这一句话:
Collection<ApplicationListener<?>> listeners = retrieveApplicationListeners(eventType, sourceType, retriever);
是不是很好奇这方法究竟干了些啥呢?继续往里跟进:
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
ResolvableType eventType, Class<?> sourceType, ListenerRetriever retriever) {
LinkedList<ApplicationListener<?>> allListeners = new LinkedList<ApplicationListener<?>>();
Set<ApplicationListener<?>> listeners;
Set<String> listenerBeans;
synchronized (this.retrievalMutex) {
listeners = new LinkedHashSet<ApplicationListener<?>>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
}
for (ApplicationListener<?> listener : listeners) {
if (supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListeners.add(listener);
}
allListeners.add(listener);
}
}
if (!listenerBeans.isEmpty()) {
BeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : listenerBeans) {
try {
Class<?> listenerType = beanFactory.getType(listenerBeanName);
if (listenerType == null || supportsEvent(listenerType, eventType)) {
ApplicationListener<?> listener =
beanFactory.getBean(listenerBeanName, ApplicationListener.class);
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListenerBeans.add(listenerBeanName);
}
allListeners.add(listener);
}
}
}
catch (NoSuchBeanDefinitionException ex) {
// Singleton listener instance (without backing bean definition) disappeared -
// probably in the middle of the destruction phase
}
}
}
AnnotationAwareOrderComparator.sort(allListeners);
return allListeners;
}
这么一大坨代码,是不是有点慌呢,不着急,慢慢往下看。
略过前面的三行声明变量的代码,看到有一个同步代码块,对两个集合进行了赋值,紧接着便是对两个集合进行了遍历,后面就将listeners的集合进行返回。是不是有点懵,这两个集合,究竟是在什么时候进行赋值的呢?还记得上面说过的EventPublishingRunListener构造方法吗?不记得了没关系,我再贴一下代码:
现在一下子就明白了吧,在构建EventPublishingRunListener时,就已经将最开始的Spring上下文中的创建的那11个Listener赋值给了this.defaultRetriever.applicationListeners,而this.defaultRetriever.applicationListenerBeans是没有做任何赋值的,所以先不考虑遍历listenerBeans的逻辑。我们先记住11这个数字。
ok,现在我们需要对这11个listeners进行遍历了,当supportsEvent方法返回true之后,即使我们需要保留下来的listener。跟进这个方法:
protected boolean supportsEvent(ApplicationListener<?> listener, ResolvableType eventType, Class<?> sourceType) {
GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
(GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}
方法接收一个ResolvableType参数,还记得我上面提到的那个event吗,没错,就是ApplicationStartEvent,通过执行方法获取:
private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
return ResolvableType.forInstance(event);
}
到此处,supportsEvent的逻辑就很明了了,就是判断当前的listener是否能够监听ApplicationStartEvent,如果能,则将该监听器进行保留。
下面,我们欢迎能够幸存下来的4个listener跟大家见面:
前面已经跟大家见过面的执行器executor,因为获取到了listener的幸运儿,他已经安奈不住要登场了。为了方便理解,再重新贴一下代码:
由于我们没有对taskExecutor进行初始化,直接执行invokeListener方法,里面会调用各个linster的onApplicationEvent方法,看了几个linster的实现,有进行事件注册的,也有执行其他非事件相关的事情。
可怜的执行器executor,因为没有人给他实例化,什么事情都干不了…
至此,SpringApplicationRunListener里面start了,并且所有能够监听ApplicationStartEvent事件的监听器,也start了(4个linster中,其实有2个,其实啥事都没有干)。
写到这边,已经跟上面run方法里的代码,间隔有点大了,下面我将run里面剩余的代码单独列出来进行解释
创建ApplicationArguments
run 方法部分代码:
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
这个ApplicationArguments究竟是何方神圣呢?进入ApplicationArguments的构造器,看到有this.source = new Source(args);里面new了一个source对象,入参是args,再次跟进source的super构造器方法:
public SimpleCommandLinePropertySource(String... args) {
super(new SimpleCommandLineArgsParser().parse(args));
}
我的天,在父类的构造器里面,又new了一个对象,按住烦躁不安的心,决定再跟进一次:
class SimpleCommandLineArgsParser {
/**
* Parse the given {@code String} array based on the rules described {@linkplain
* SimpleCommandLineArgsParser above}, returning a fully-populated
* {@link CommandLineArgs} object.
* @param args command line arguments, typically from a {@code main()} method
*/
public CommandLineArgs parse(String... args) {
CommandLineArgs commandLineArgs = new CommandLineArgs();
for (String arg : args) {
if (arg.startsWith("--")) {
String optionText = arg.substring(2, arg.length());
String optionName;
String optionValue = null;
if (optionText.contains("=")) {
optionName = optionText.substring(0, optionText.indexOf("="));
optionValue = optionText.substring(optionText.indexOf("=")+1, optionText.length());
}
else {
optionName = optionText;
}
if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) {
throw new IllegalArgumentException("Invalid argument syntax: " + arg);
}
commandLineArgs.addOptionArg(optionName, optionValue);
}
else {
commandLineArgs.addNonOptionArg(arg);
}
}
return commandLineArgs;
}
}
这次终于没有再创建新的对象了,注意到是有调用parse方法的,看下返回值类型,CommandLineArgs。见名思意,返回的不就是命令行参数嘛,由此可以确定,创建这个类,是为了解析命令行参数,如“java -jar *.jar --server.port=9090”,解析出“server.port=9090”
一个问题,参数解析出来之后,存在哪里呢?一步步追中source的父类,最终定位到PropertySource里面的一个泛型变量:T source,针对命令行参数信息,此时的T即为CommandLineArgs类型。
创建环境ConfigurableEnvironment
run 方法部分代码:
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
一脸懵逼,这个环境指的是啥?跟进代码看下:
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
return environment;
}
首先调用getOrCreateEnvironment方法,用以拿取或者创建出一个环境信息,到底是怎么创建的呢?
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
if (this.webEnvironment) {
return new StandardServletEnvironment();
}
return new StandardEnvironment();
}
可以看到,在方法内部,是直接创建了一个标准的servlet环境,然后发现,它没有构造器!按照spring的一贯个性,很有可能构造器是在其父类中
在其父类AbstractEnvironment中发现了构造器:
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
if (this.logger.isDebugEnabled()) {
this.logger.debug(String.format(
"Initialized %s with PropertySources %s", getClass().getSimpleName(), this.propertySources));
}
}
熟悉的propertySources出现了!内部有调用一个虚方法customizePropertySources,这时在转到StandardServletEnvironment内部查看:
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
}
super.customizePropertySources(propertySources);
}
这里面,便是在前面解析出来的命令行参数的基础上,在添加了一些运行时环境参数
后面再对现在已经收集到的所有参数的集合进行一下简单处理后,下面就应该让这些配置生效了:
listeners.environmentPrepared(environment);
这个linsteners是谁?可不就是前面创建的SpringApplicationRunListeners
下面我们来看看这个方法:
public void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}
是不是感觉方法内部调用的 initialMulticaster.multicastEvent方法很眼熟?没错,之前在进行linster的start方法的时候,也是调用的这个方法,不用的是,start的时候,入参是new一个ApplicationStartedEvent事件,而这次是new一个ApplicationEnvironmentPreparedEvent事件。后面的逻辑也是类似了,在前面的那11个linster中找到能够监听ApplicationEnvironmentPreparedEvent的监听器。在前面的start中,我们11位参赛选手,只有4位最终保留下来,这次我们看看能有几位幸运儿。
哇哦,这次很不错,11位中有7位获得了保留权。
接下来,我们隆重介绍下第一位选手ConfigFileApplicationListener,我们先看下它的onApplicationEvent回调方法:
下面来解释下这位老兄这么一大堆的操作都干了些啥事:
- 在回调方法中调用onApplicationEnvironmentPreparedEvent方法,onApplicationEnvironmentPreparedEvent方法中将自身的引用放入了一个集合,后续遍历这个集合对象,再调用对象的postProcessEnvironment方法,也即是会调用老兄他自身的postProcessEnvironment方法
- 在调用自身的postProcessEnvironment又调用了addPropertySources方法,内部会new 一个Loader对象,并且调用它的load方法。
- 在load方法中,实例化了一个PropertySourcesLoader对象,沟通构造函数可知,又会去spring.factory中去寻找需要加载并实例化的类,一个是PropertiesPropertySourceLoader,另一个是YamlPropertySourceLoader。实例化之后,会执行一个内部的load方法。
- 执行内部load方法是,会调用getAllFileExtensions方法,可逻辑可知,是获取到了刚才实例化的PropertiesPropertySourceLoader和YamlPropertySourceLoader对象,再往下走,会最终执行到这两个对象的load方法。这边以PropertiesPropertySourceLoader为demo:
public class PropertiesPropertySourceLoader implements PropertySourceLoader {
@Override
public String[] getFileExtensions() {
return new String[] { "properties", "xml" };
}
@Override
public PropertySource<?> load(String name, Resource resource, String profile)
throws IOException {
if (profile == null) {
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
if (!properties.isEmpty()) {
return new PropertiesPropertySource(name, properties);
}
}
return null;
}
}**加粗样式**
load方法中,会加载后缀名为"properties","xml"的文件,并解析出其中的配置信息。
也就是说,我们自己定义的配置文件,在此处进行加载,并解析,最终将配置结果,应用到环境中
Banner
Banner printedBanner = printBanner(environment);
打印一个Banner信息,没什么好说的。
创建应用上下文 ConfigurableApplicationContext
context = createApplicationContext();
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
关于Spring的上下文构建,刷新等,详见后续文章
finish
和上面的逻辑类似,先是构建出一个事件ApplicationFailedEvent(启动失败),ApplicationReadyEvent(启动成功),然后又是在11个linster中寻找可以监听这个事件的linster,再进行回调