在SpringBoot中,SpringApplication.run()方法执行了两遍。具体原因是暂时不明,我们来看一下是为什么会造成这种情况。
执行两遍的起点是这行代码:
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
1、这个方法中包含两个属性,分别是SpringApplicationRunListeners和一个我们启动的时候传入的参数,默认是没有参数传入的。
这个SpringApplicationRunListeners借鉴了Spring的ApplicationListener的方式。
传入的Listeners中只有一个Listener,就是EventPublishingRunListener,这个类的作用就是Publish SpringApplication相关的Listener
2、我们进入看看执行方法是什么样子的:
private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (this.webApplicationType == WebApplicationType.NONE) { environment = new EnvironmentConverter(getClassLoader()) .convertToStandardEnvironmentIfNecessary(environment); } ConfigurationPropertySources.attach(environment); return environment; }
这个方法中的ConfigurableEnvironment environment = getOrCreateEnvironment();这一行代码会根据是Servlet的方式选择创建不同的环境。
configureEnvironment(environment, applicationArguments.getSourceArgs());方法是做了环境的配置。默认是空的,没有配置的。
listeners.environmentPrepared(environment);
这行代码做了环境的前置配置,也是从这里开始进入SpringApplication的run()方法的。
3、EventPublishingRunListener的environmentPrepared
这个方法如下:
@Override public void environmentPrepared(ConfigurableEnvironment environment) { this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent( this.application, this.args, environment)); }
这个方法覆盖了父类,SpringApplicationRunLIstener类的方法,关于SpringApplicationRunListen类型,是SpringApplication的run()方法执行的过程中贯穿始终的时间监听器,有兴趣的可以去看下它的文档说明。
SpringApplicationRunLIstener的environmentPrepared(ConfigurableEnvironment environment)是在环境已经准备好,ApplicationContext还未创建的时候执行的。
这个项目中创建了ApplicationEnvironmentPreparedEvent事件,并发布了这个事件。
4、接着进入了SimpleApplicationEventMulticaster的multicastEvent方法
SimpleApplicationEventMulticaster类是查找订阅对应event的Listenr并执行Listener的类,其代码如下:
@Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) { Executor executor = getTaskExecutor(); if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } }
getApplicationListeners(event, type)方法就是查找了订阅这个event的listener,然后执行了invokeListener(listener, event);方法,
找到的对应这个事件的Listener为BootstrapApplicationListener.
5、然后进入到了BootstrapApplicationListener类的onApplicationEvent(ApplicationEnvironmentPreparedEvent event)方法,真正执行的是下面这行代码:
if (context == null) { context = bootstrapServiceContext(environment, event.getSpringApplication(), configName); }
这个方法也是区分,为什么SpringApplication的run()方法只会进来一次的方法。也是初始化ApplicationContext的方法。其中event.getSpringApplication()方法获取到的是SpringApplicationRunListener类中的SpringApplication
6、然后在bootstrapServiceContext()方法中进行了环境的设置,在这个方法中使用到了一个类型叫做SpringApplicationBuilder,最后调用了这个Builder类型的run()方法,如下:
public SpringApplication build(String... args) { configureAsChildIfNecessary(args); this.application.addPrimarySources(this.sources); return this.application; }
这个application是在创建SpringApplicationBuilder类型的时候载入的,如下:
public SpringApplicationBuilder(Class<?>... sources) { this.application = createSpringApplication(sources); }
是一个新的SpringApplication。
自此就解析完了,为什么SpringApplication为什么会走两遍,同时注意的是BootstrapApplicationListener类是spring-cloud-contextjar包中的一个类型,如果没有引入spring-cloud也是不会走两遍的。