URL
https://mp.weixin.qq.com/s/uP4seo__qYMJMzmbWyUUnA?tdsourcetag=s_pctim_aiomsg
SpringApplication.run
- 总共做了两件事情,如下源码
- 穿件SpringApplication对象
- 利用创建好的对象调用run方法
// SpringApplication.run(Application.class, args);进入 --- 1
public static ConfigurableApplicationContext run(Object source, String... args) {return run(new Object[] { source }, args);}//以上run 方法进去
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {return new SpringApplication(sources).run(args);}
- 此处开始分成两个部分,一部分:SpringApplication(sources)进行SpringApplication的初始化工作,包括各种配置的初始化,listener初始化等 二部分:run(args)执行run方法启动
//初始化开始
public SpringApplication(Object... sources) {initialize(sources);}
//initialize 方法
private void initialize(Object[] sources) {if (sources != null && sources.length > 0) {// ------1this.sources.addAll(Arrays.asList(sources));}// ----------2this.webEnvironment = deduceWebEnvironment();//--------3setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//-----------4setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//----------5this.mainApplicationClass = deduceMainApplicationClass();}//deduceMainApplicationClass 代码
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;}
- 在初始化步骤中
- 步骤1 保存主配置信息
- 步骤2 判断当前是否是web应用
- 步骤3中可以看到方法getSpringFactoriesInstances作用在于通过到路径META_INF/spring.factories配置中所有ApplicationContextinitializer配置信息的list,接着通过initializers 将配置信息保存
- 步骤4 中同步骤3 一致,只不过这次获取的配置类型不同,连调用的方法都是一样的,这次获取的是ApplicationListener类型的配置,之后保存
- 步骤5 中的deduceMainApplicationClass 方法在寻址一个包含main方法的配置类,如上部分代码,也就是我们自己写的启动类
- 第二部分则是运行对应的run方法:
public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;FailureAnalyzers analyzers = null;configureHeadlessProperty();//-----------1SpringApplicationRunListeners listeners = getRunListeners(args);//-----------2listeners.starting();try {//----------3ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// ----------4ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);//--------5Banner printedBanner = printBanner(environment);//----------6context = createApplicationContext();analyzers = new FailureAnalyzers(context);//---7prepareContext(context, environment, listeners, applicationArguments,printedBanner);//---8refreshContext(context);// -----------9afterRefresh(context, applicationArguments);//--------10listeners.finished(context, null);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}// --------11return context;}catch (Throwable ex) {handleRunFailure(context, listeners, analyzers, ex);throw new IllegalStateException(ex);}}// 4步骤中流程:
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {// Create and configure the environmentConfigurableEnvironment environment = getOrCreateEnvironment();configureEnvironment(environment, applicationArguments.getSourceArgs());listeners.environmentPrepared(environment);if (!this.webEnvironment) {environment = new EnvironmentConverter(getClassLoader()).convertToStandardEnvironmentIfNecessary(environment);}return environment;}
- run方法解析:
- 此处先调用getRunListeners方法,同之前一样,是获取META_INF/Spring.factories文件中的配置信息,配置信息类型是:SpringApplicationRunListeners
- 在得到对应的配置信息后,调用gstarting方法,改方法会去循环调用刚才获取到的所有配置,并且触发SpringApplicationRunListener中的starting事件,并且是异步的形式触发
- 接着封装我们的命令行参数,也就是我们main方法中传入的args参数封装成对应的ApplicationArguments,
- 此处步骤会先准备对应环境,主要是我们在配置的配置信息的驾照,在完成环境准备之后在listeners.environmentPrepared(environment); 方法中会回调SpringApplicationRunListener中的environmentPrepared 事件,也是同样的异步的方式触发
- 这个步骤pringBanner,启动时候banner的打印,可以自定义自步骤中的图形
- createApplicationContext方法会更具我们之前环境判断信来是web环境还是其他决定创建web还是普通的IOC容器
- 此步骤中有多个功能
- 将environment上下文环境信息保存早ioc容器中
- 执行applyInitializers 方法,改方法会遍历之前保存的ApplicationContextInitializer的initialize()事件
- 在contextPerpared中遍历SpringApplicationRunListener中的ContextPrepared()事件
- 最后在contextLoaded方法中回调SpringApplicationRunListener中的ContextLoaded()事件
- refreshContext顾名思义,刷新容器,进行组件的扫描创建,加载等
- afterRefresh从IOC容器中获取所有ApplicationRunner和CommandLineRunner遍历所有runner并且调用callRunner方法回调用,先调用ApplicationRunner,在调用CommandLineRunner
- finished方法中遍历SpringApplicationRunListener中的finished()事件
- 最后返回context ioc容器
总结
- SpringApplication.run一共做了两件事,一件是创建SpringApplication对象,在该对象初始化时,找到配置的事件监听器,并保存起来.第二件事就是运行run方法,此时会将刚才保存的事件监听器根据当前时机触发不同的事件,比如容器初始化,容器创建完成等.同时也会刷新IoC容器,进行组件的扫描、创建、加载等工作.