Spring Boot深入理解:启动过程解析(从console入手)(2/3)

版权声明:转载请务必注明出处并注明“武汉AI算法研习” https://blog.csdn.net/qq_36931982/article/details/84104268

一下Spring Boot启动的第二阶段是调用run()方法,其中会返回一个ConfigurableApplicationContext对象,ConfigurableApplicationContext其中继承了接口ApplicationContext、Lifecycel和Closeable,是Spring运行过程中的一个上下文环境,在ApplicationContext的基础上增加了一系列配置应用上下文的功能。配置应用上下文和控制应用上下文生命周期的方法在此接口中被封装起来,以免客户端程序直接使用。

/**
 * SPI interface to be implemented by most if not all application contexts.
 * Provides facilities to configure an application context in addition
 * to the application context client methods in the
 * {@link org.springframework.context.ApplicationContext} interface.
 *
 * <p>Configuration and lifecycle methods are encapsulated here to avoid
 * making them obvious to ApplicationContext client code. The present
 * methods should only be used by startup and shutdown code.
 *
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 03.11.2003
 */
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {}
	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;
	}

以上为run()方法的详细执行步骤,其中第一步是启动计时工具StopWatch.start()其中StopWatch是一个简单的秒表,同时还初始化两个对象,ConfigrableApplicationContexxt和exceptionReporters。之后进行一个系统参数的设置,由于是web项目没有图像化,设置java.awt.headless系统属性为true。

之后,getRunListeners(),获取SpringApplicationRunListeners。SpringApplicationRunListener 可以监听springboot应用启动过程中的一些生命周期事件,并做一些处理。SpringApplicationRunListeners包含了多个SpringApplicationRunListener。

/**
 * Listener for the {@link SpringApplication} {@code run} method.
 * {@link SpringApplicationRunListener}s are loaded via the {@link SpringFactoriesLoader}
 * and should declare a public constructor that accepts a {@link SpringApplication}
 * instance and a {@code String[]} of arguments. A new
 * {@link SpringApplicationRunListener} instance will be created for each run.
 *
 * @author Phillip Webb
 * @author Dave Syer
 * @author Andy Wilkinson
 */
public interface SpringApplicationRunListener {
     //刚执行run方法时
    void started();
     //环境建立好时候
    void environmentPrepared(ConfigurableEnvironment environment);
     //上下文建立好的时候
    void contextPrepared(ConfigurableApplicationContext context);
    //上下文载入配置时候
    void contextLoaded(ConfigurableApplicationContext context);
    //上下文刷新完成后,run方法执行完之前
    void finished(ConfigurableApplicationContext context, Throwable exception);
}

SpringApplicationRunListener的实现类EventPublishingRunListener,其具有广播事件的功能。负责发布SpringApplicationEvent事件的,它会利用一个内部的ApplicationEventMulticaster在上下文实际被刷新之前对事件进行处理。

之后、new DefaultApplicationArguments(args)会初始化一个ApplicationArguments,既然对象初始化有了,接下来肯定得往里面进行参数的填充了,

之后、prepareEnvironment(listeners,applicationArguments),此时进行参数环境的配置,Web应用而言,得到的environment变量是一个StandardServletEnvironment的实例,得到实例后,会调用前面RunListeners中的environmentPrepared方法(发布一个ApplicationEnvironmentPreparedEvent事件,这个事件的发布过后就能够给已有的监听器进行相应的处理,在初始化SpringApplication的过程中就初始化了部分的监听器)。

之后、打印banner

之后,createApplicationContext()进行上下文的创建。org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext,同时调用BeanUtils进行初始化类

之后、prepareContext(context, environment, listeners, applicationArguments,printedBanner) 进行上下文处理,其中会忘context(ApplicationContext)中设置其环境setEnvironment(environment),注意其中包含有两个出发监听操作,分别对应前面的监听器。

private void prepareContext(ConfigurableApplicationContext context,
        ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments, Banner printedBanner) {
    // 将环境和上下文关联起来
    context.setEnvironment(environment);
    // 为上下文配置Bean生成器以及资源加载器(如果它们非空)
    postProcessApplicationContext(context);
    // 调用初始化器
    applyInitializers(context);
    // 触发Spring Boot启动过程的contextPrepared事件
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    // 添加两个Spring Boot中的特殊单例Beans - springApplicationArguments以及springBootBanner
    context.getBeanFactory().registerSingleton("springApplicationArguments",
            applicationArguments);
    if (printedBanner != null) {
        context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
    }
    // 加载sources - 对于DemoApplication而言,这里的sources集合只包含了它一个class对象
    Set<Object> sources = getSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    // 加载动作 - 构造BeanDefinitionLoader并完成Bean定义的加载
    load(context, sources.toArray(new Object[sources.size()]));
    // 触发Spring Boot启动过程的contextLoaded事件
    listeners.contextLoaded(context);
}

之后、refreshContext(context)进行上下文的刷新。

之后、afterRefresh(context, applicationArguments),上下文后置处理

自此,整个启动过程结束,在整个过程SpringApplicationRunListeners就充当了一个中介的作用,不断的传递事件,context启动过程中不断的调用Listeners进行事件广播,以相应的监听器进行处理。

1、Spring中的Run Listeners和Application Listeners监听的区别?

SpringApplicationRunListener

猜你喜欢

转载自blog.csdn.net/qq_36931982/article/details/84104268