随着我们ctrl加鼠标左键的使用,最终我们会在一个springboot项目中发现启动过程由以下两个过程组成
构造过程:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//有些会指定额外的classpath目录,会有传入这个东西的情况
//比如写入了一大堆配置文件,放在某处,那么启动时需要传入一个这个东西来识别自己的路径
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//启动主类的class 对象
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
通过自己加载的类中含有哪个类型的dispatcher来判断是哪种web应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//我们写一个spring boot的 starter,需要在其中加入一个spring.factories文件,
//这一步就加载我们实现的那些Listenner也好Initializer也好,就是容器启动时,外部starter需要做的工作
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//获取主类的方式,机巧,可以一学
this.mainApplicationClass = deduceMainApplicationClass();
}
run方法:
public ConfigurableApplicationContext run(String... args) {
//这个是个秒表,用来告诉你boot过程花了多少时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//有些情况下需要无显示器启动,这是对这个无显示器的情况进行的一些设置
configureHeadlessProperty();
//创建一组Listenner用来处理启动参数,也就是说启动参数是通过构造Listenner来实现的
SpringApplicationRunListeners listeners = getRunListeners(args);
//Listnner开始监听一些启动事件
listeners.starting();
try {
//构造启动参数对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//设置启动环境,所谓环境就是一些环境变量的集合,这里的环境变量就是一些启动参数
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
//处理启动参数中要求我们忽略的bean
configureIgnoreBeanInfo(environment);
//就是banner,就是启动时那个Spring的logo,还带一片叶子
Banner printedBanner = printBanner(environment);
//这个很重要,加载了Context,从此我们有了容器这个东西,在spring boot的中,是一个基于注解的上下文
context = createApplicationContext();
//取得一个错误处理的一个什么东西
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//这个也很重要,让我们的容器准备好,大概做了以下事情
//1. 设置了上面的“环境”
//2. 一些设置,其中包括resourceLoader的设置
//3. 把之前加载的initializer全部执行他们的initialize()方法
//4. 环境变量产生的Listenner,即上述的listeners,知会contextPrepared事件
//5. 把参数作为一个bean放在容器中
//6. 把banner作为一个bean放在容器中
//7. 通过load方法生成一个BeanDefinitionLoader,这个是用来解析我们的自定义Bean的
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
//刷新我们的容器,销毁原有的beanfactory,重新生成beanfactory以及生成所有的BeanDefinitions
//beanfactory和BeanDefinition是干什么用的,这个就是经典的IOC原理了,网上很多
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;
}