SpringApplication.run做了哪些事情

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) {
            // ------1
			this.sources.addAll(Arrays.asList(sources));
		}
    // ----------2
		this.webEnvironment = deduceWebEnvironment();
    //--------3
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
    //-----------4
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //----------5
		this.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();
    	//-----------1
		SpringApplicationRunListeners listeners = getRunListeners(args);
    	//-----------2
		listeners.starting();
		try {
            //----------3
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
            // ----------4
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
            //--------5
			Banner printedBanner = printBanner(environment);
            //----------6
			context = createApplicationContext();
			analyzers = new FailureAnalyzers(context);
            //---7
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			//---8
             refreshContext(context);
            // -----------9
			afterRefresh(context, applicationArguments);
            //--------10
			listeners.finished(context, null);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
            // --------11
			return 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 environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		listeners.environmentPrepared(environment);
		if (!this.webEnvironment) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertToStandardEnvironmentIfNecessary(environment);
		}
		return environment;
	}
  • run方法解析:
  1. 此处先调用getRunListeners方法,同之前一样,是获取META_INF/Spring.factories文件中的配置信息,配置信息类型是:SpringApplicationRunListeners
  2. 在得到对应的配置信息后,调用gstarting方法,改方法会去循环调用刚才获取到的所有配置,并且触发SpringApplicationRunListener中的starting事件,并且是异步的形式触发
  3. 接着封装我们的命令行参数,也就是我们main方法中传入的args参数封装成对应的ApplicationArguments,
  4. 此处步骤会先准备对应环境,主要是我们在配置的配置信息的驾照,在完成环境准备之后在listeners.environmentPrepared(environment); 方法中会回调SpringApplicationRunListener中的environmentPrepared 事件,也是同样的异步的方式触发
  5. 这个步骤pringBanner,启动时候banner的打印,可以自定义自步骤中的图形
  6. createApplicationContext方法会更具我们之前环境判断信来是web环境还是其他决定创建web还是普通的IOC容器
  7. 此步骤中有多个功能
    1. 将environment上下文环境信息保存早ioc容器中
    2. 执行applyInitializers 方法,改方法会遍历之前保存的ApplicationContextInitializer的initialize()事件
    3. 在contextPerpared中遍历SpringApplicationRunListener中的ContextPrepared()事件
    4. 最后在contextLoaded方法中回调SpringApplicationRunListener中的ContextLoaded()事件
  8. refreshContext顾名思义,刷新容器,进行组件的扫描创建,加载等
  9. afterRefresh从IOC容器中获取所有ApplicationRunner和CommandLineRunner遍历所有runner并且调用callRunner方法回调用,先调用ApplicationRunner,在调用CommandLineRunner
  10. finished方法中遍历SpringApplicationRunListener中的finished()事件
  11. 最后返回context ioc容器
总结
  • SpringApplication.run一共做了两件事,一件是创建SpringApplication对象,在该对象初始化时,找到配置的事件监听器,并保存起来.第二件事就是运行run方法,此时会将刚才保存的事件监听器根据当前时机触发不同的事件,比如容器初始化,容器创建完成等.同时也会刷新IoC容器,进行组件的扫描、创建、加载等工作.

猜你喜欢

转载自blog.csdn.net/liaojiamin0102/article/details/93606770