我们在之前已经了解SpringBoot源码分析------mvc容器的创建和tomcat的启动过了,SpringBoot的启动是通过内部new出对象,在进行run()方法的。
现在我们来了解一下run()方法中的每一个步骤。
SpringApplication.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;
}
-
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
声明IOC容器 -
SpringApplicationRunListeners listeners = getRunListeners(args);
获取SpringApplicationRunListeners,内部只有一个EventPublishingRunListener,我们可以看看这个方法是执行的什么
想必都已经很熟悉这个getSpringFactoriesInstances()方法了,他就是读取META-INF/factories文件中的某个类,我们可以拿着SpringApplicationRunListener去springboot的包中搜索一下,可以发现只有一个
-
listeners.starting();
回调所有的获取SpringApplicationRunListener.starting()方法
目前我们没有自定义的SpringApplicationRunListener子类的方法,所以只有一个EventPublishingRunListener.starting()被回调。我们也可以看看内部的代码,就是一个for循环进行了回调
-
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
将args进行包装 -
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
重点
进入代码
这里可以看到只是进行了一些数据的初始化。再回到前面,看load()方法。
不是我说,包装的是真的多
此处还有一个names,我们去查看一下getSearchNames()方法。
并且查看这个常量。
这里有个foreach,我们可以查看一下这个Lambda表达式具体什么意思。进去看看getSearchLocations()方法。
可以查看到DEFAULT_SEARCH_LOCATIONS这个常量的数据为
然而这个数据又是下面的参数location
在这个方法中可以先看看loader、location、name这三个变量。
1.loader
里面有着xml、yaml、yml、properties,这几个变量我们应该都很熟悉,这些都是配置文件的后缀。
2.location
上面已经提到过
3.name
上面已经提到过
从这三个小点我们可以知道,默认读取的配置文件名为application
,其中配置文件可以存在于classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/
目录下,配置文件的类型可以为xml、yaml、yml、properties
。
回到前面,继续看这一重重的封装
可以先提出来,在这一个方法中,开始获取到配置文件中的信息,并且马上要写入到springboot中。
在List<Document> documents = loadDocuments(loader, name, resource);
此处获取配置文件中的信息。目前我的配置文件中只有一个数据
我们可以debug到这个位置。可以查看到此行代码运行后。
就已经拿到了数据。
此处拿完数据之后就会回到
中的addLoadedPropertySources()方法,看看这个方法。
如果使用过Properties文件读取的朋友,应该很熟悉。这是将资源添加到项目中
之后运行到addLoadedPropertySource(destination, lastAdded, source);
这段代码。
将数据添加到SpringBoot项目中。至此。ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
已经结束。 -
Banner printedBanner = printBanner(environment);
输出SpringBoot的标志图标 -
context = createApplicationContext();
创建SpringBoot上下文 -
refreshContext(context);
刷新上下文。这里面进行的就是tomcat的启动,加载springmvc
我们前面的文章提到过这两个方法。可以在其中打上断点,查看一下它的调用链。
-
afterRefresh(context, applicationArguments);
定义一个空的模板方法给其他子类实现 -
listeners.started(context);
使用广播和回调机制通知监听器SpringBoot容器启动成功 -
listerens.running(context)
使用广播和回调机制通知监听器 SpringBoot容器启动成功并可执行其他的请求 -
返回当前上下文。
到这里就算结束了,是不是在读取配置文件那一部分很懵逼?懵逼就对了,等会来个简易版本的(SpringBoot源码分析------简易的配置文件读取),再回过头来看一遍就可以了解了。
在这之前,询问个问题。
classpath读取配置文件和file读取配置文件有什么区别?
classpath读取的配置文件是编译后的文件,而file的配置文件是没有经过编译的文件。