SpringBoot启动流程分析(二)- SpringApplication#run方法的执行逻辑

SpringApplication#run方法用于启动Spring应用,创建和刷新ApplicationContext

org.springframework.boot.SpringApplication#run(String...)

接收一个参数,即命令行参数

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;
}

同样的,我们逐行分析

StopWatch stopWatch = new StopWatch(); stopWatch.start();

这两行代码创建了一个StopWatch,秒表,并且启动了这个秒表,来看一下这个类

public class StopWatch {

	/**
	 * Identifier of this {@code StopWatch}.
	 * <p>Handy when we have output from multiple stop watches and need to
	 * distinguish between them in log or console output.
	 */
	private final String id;

	private boolean keepTaskList = true;

    /** 一系列任务 */
	private final List<TaskInfo> taskList = new LinkedList<>();

	/** Start time of the current task. */
	private long startTimeNanos;

	/** Name of the current task. */
	@Nullable
	private String currentTaskName;

	@Nullable
	private TaskInfo lastTaskInfo;

	private int taskCount;

	/** Total running time. */
	private long totalTimeNanos;
    
    // methods...
}

该类是一个非线程安全的类,用于记录一系列任务的执行时间和所有任务的总执行时间

Collection exceptionReporters = new ArrayList<>();

创建一个元素类型为SpringBootExceptionReporter的集合

/**
 * 回调接口,用于记录SpringApplication在启动过程中的错误
 * 它通过SpringFactoriesLoader来加载
 * 并且它的实现类必须要提供一个只有一个参数,该参数的类型为ConfigurableApplicationContext的构造方法
 */
@FunctionalInterface
public interface SpringBootExceptionReporter {

	/**
	 * 记录用户启动过程中的错误信息
	 * @param failure 错误信息
	 * @return 如果有错误被记录,返回true,否则返回false
	 */
	boolean reportException(Throwable failure);

}

该类是一个函数式接口,它只提供了一个方法

configureHeadlessProperty();

private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";

private boolean headless = true;

private void configureHeadlessProperty() {
    System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
                       System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

这个方法设置了一个名为java.awt.headless的系统属性,首先取系统属性中的该属性的值,如果没有则给予其默认值true

这个属性是什么意思呢?

可以简单的理解为没有显示器、鼠标或键盘的环境下使用的模式,如果你的服务器没有显示设备,但是你又需要生成一些图像等,那这个模式下就允许你在没有显示设备的情况下使用canvas、panel等awt相关的方法,我们日常编写的服务器端就是这种模式

SpringApplicationRunListeners listeners = getRunListeners(args);

这行代码用于获取一系列的监听器

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger,
                                             getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
  • 调用getSpringFactoriesInstance方法来获取SpringApplicationRunListener接口的实现类
  • 然后构造一个SpringApplicationRunListeners

SpringApplicationRunListener

这是一个SpringApplicaiton#run方法的监听器,它提供了一系列的生命周期方法,这些生命周期方法会随着run方法的执行而被调用

public interface SpringApplicationRunListener {

	/**
     * 当run方法第一次被执行时该方法被执行,可以用于做一些早期的初始化工作
	 */
	default void starting() {
	}

	/**
     * 在environment准备好之后且ApplicationContext被创建之前,该方法被调用
	 */
	default void environmentPrepared(ConfigurableEnvironment environment) {
	}

	/**
     * 在ApplicationContext倍创建并且做好了准备工作之后被调用,但此时sources还未被加载
	 */
	default void contextPrepared(ConfigurableApplicationContext context) {
	}

	/**
     * 在ApplicationContext被加载后,并且还没有被刷新之前,该方法被调用
	 */
	default void contextLoaded(ConfigurableApplicationContext context) {
	}

	/**
     * ApplicationContext被刷新并且应用已启动,但是CommandLineRunner和ApplicationRunner还没有被调用时该方法被调用
	 */
	default void started(ConfigurableApplicationContext context) {
	}

	/**
     * 当run方法被执行完即当ApplicationContext、CommandLineRunner和ApplicationRunner都被调用后,该方法立即被调用
	 */
	default void running(ConfigurableApplicationContext context) {
	}

	/**
     * 如果应用启动失败,该方法被调用
	 * @param context 传入ApplicationContext或者null(如果ApplicationContext还没有被创建的话)
     * @param exception 异常信息
	 */
	default void failed(ConfigurableApplicationContext context, Throwable exception) {
	}

}

再来看一下SpringApplicationRunListeners

class SpringApplicationRunListeners {

	private final List<SpringApplicationRunListener> listeners;

	SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
		this.log = log;
		this.listeners = new ArrayList<>(listeners);
	}
}

该类就是封装了一系列的SpringApplicationRunListener,在每次需要调用监听器的时候,就统一调用所有的监听器,并执行其方法,我们来分析其中的一个方法,其他的方法逻辑也是一样的

	void starting() {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}

该方法最终会调用到SpringApplicationRunListerner的实现类EventPublishingRunListener中的starting方法

来看一下EventPublishingRunListener这个类以及其starting方法

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

	private final SpringApplication application;

	private final String[] args;

	private final SimpleApplicationEventMulticaster initialMulticaster;

    /**
     * 构造方法接收两个参数
     * @param application SpringApplication实例
     * @param args 命令行参数
     */
	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}
}

该类通过使用ApplicationEventMulticaster来发布一些SpringApplicationEvent, 很典型的 事件发布机制

ApplicationEventMulticaster

这是一个接口,用于管理一系列的ApplicationListener并将SpringApplicationEvent发布给这些Listener,这里用到了它的实现类SimpleAppliationEventMulticaster

private final SimpleApplicationEventMulticaster initialMulticaster;

@Override
public void starting() {
    this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}

@Override
public void multicastEvent(ApplicationEvent event) {
    multicastEvent(event, resolveDefaultEventType(event));
}
  • 首先调用一个参数的multicastEvent方法,传入ApplicationStartingEvent
  • 再调用两个参数的multicastEvent,传入event,然后获取当前event的type并传入
  • 接着就是获取对ApplicationStartingEvent事件感兴趣的Listener并调用他们的方法

看一下这里具体是怎么做的
image.png
先看一下resolveDefaultEventType方法,这是一个私有方法,里面会调用ResolvableType#forInstance方法来获取ResolvableType

private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
    return ResolvableType.forInstance(event);
}

ResolvableType#forInstance

public static ResolvableType forInstance(Object instance) {
    Assert.notNull(instance, "Instance must not be null");
    if (instance instanceof ResolvableTypeProvider) {
        ResolvableType type = ((ResolvableTypeProvider) instance).getResolvableType();
        if (type != null) {
            return type;
        }
    }
    return ResolvableType.forClass(instance.getClass());
}

该方法用于返回给定实例的resolvableType

  • 首先判断给定实例是否是ResolvableTypeProvider类型
  • 如果是,则将其转换为ResolvableTypeProvider,再调用#getResolvableType来获取其type
  • 如果不是,则通过Resolvable#forClass来获取其type

然后就是两个参数的multicastEvent方法

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    Executor executor = getTaskExecutor();
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        if (executor != null) {
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            invokeListener(listener, event);
        }
    }
}
  • 首先判断传入的eventType是否为null,不为null则直接取该type,否则再次调用#resolvableDefaultEventType来获取event的type
  • 接着调用#getApplicationListeners来获取对给定的eventType感兴趣的listener
  • 如果线程池存在,则这里会通过异步的方式来执行listener的方法,否则就同步执行

listeners.starting();

这里就是开启listeners的starting方法

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

这里就是创建了一个ApplicationArguments的默认实现类DefaultApplicationArguments,用于访问命令行参数的

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

ConfigurableEnvironment,接口,继承了Environment接口,提供了用于设置active和default profile和操作properties的方法,该接口被大多数的Environment实现。允许客户端来设置和验证所需要的properties,也可以通过ConfigurablePropertyResolver对其进行定制化

prepareEnvironment

该方法用于创建、配置好一个Environment

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
                                                   ApplicationArguments applicationArguments) {
    // Create and configure the environment
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    ConfigurationPropertySources.attach(environment);
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                                                                                               deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}
ConfigurableEnvironment environment = getOrCreateEnvironment();

根据webApplicationType来决定返回哪种类型的Environment,其类型有SERVLET,REACTIVE,NONE三种

private ConfigurableEnvironment getOrCreateEnvironment() {
    if (this.environment != null) {
        return this.environment;
    }
    switch (this.webApplicationType) {
        case SERVLET:
            return new StandardServletEnvironment();
        case REACTIVE:
            return new StandardReactiveWebEnvironment();
        default:
            return new StandardEnvironment();
    }
}
  • 首先判断this.environment是否存在,如果存在则直接返回
  • 不存在则根据this.webApplicationType来返回对应的Environment实现类
configureEnvironment(environment, applicationArguments.getSourceArgs());

该方法是一个模版方法,子类可以重写这个方法来做一些定制化的操作,这里主要就是配置environment,配置profiles和propertySources

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
    if (this.addConversionService) {
        ConversionService conversionService = ApplicationConversionService.getSharedInstance();
        environment.setConversionService((ConfigurableConversionService) conversionService);
    }
    configurePropertySources(environment, args);
    configureProfiles(environment, args);
}
ConfigurationPropertySources.attach(environment);

该方法将ConfigurationPropertySource附加到environment上

listeners.environmentPrepared(environment);

调用监听器的environmentPrepared方法,通知监听器environment已经准备好了,可以进行响应的处理了

configureIgnoreBeanInfo(environment);

该方法用于设置忽略beanInfo的属性

该方法用于Banner打印相关操作,这一步操作之后就能在控制台看到Spring输出的信息了

context = createApplicationContext();

这里开始创建ApplicationContext了

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                    break;
                case REACTIVE:
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default:
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
            }
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
  • 根据webApplicationType来找到对应的Context的类
  • 然后调用BeanUtils#instantiateClass方法来实例化Context

exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] , context);

该方法跟之前分析的逻辑一样,从spring.factories文件中找到SpringBootExceptionReporter的实现类,并实例化

prepareContext(context, environment, listeners, applicationArguments, printedBanner);

准备Context,为Context设置一些属性

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
                            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    context.setEnvironment(environment);
    // 对context进行后置处理
    postProcessApplicationContext(context);
    // 在context刷新之前应用initializers
    applyInitializers(context);
    // 调用监听器的方法,这里开始,context就准备好了
    listeners.contextPrepared(context);
    // 记录启动信息
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory) beanFactory)
        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    if (this.lazyInitialization) {
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }
    // Load the sources
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[0]));
    // 这里,context加载完成了,但是还没有被刷新
    listeners.contextLoaded(context);
}

refreshContext(context);

刷新Context,将更新的值刷新到context中并应用

afterRefresh(context, applicationArguments);

当context被刷新后可以做一些操作,由子类自定义实现

stopWatch.stop();

计时结束,ApplicationContext配置完成,Environment配置完成

listeners.started(context);

调用监听器的started方法

callRunners(context, applicationArguments);

调用对应的Runner

listeners.running(context);

调用监听器的running方法