看springboot源码的时候感觉SpringApplication.run会被执行两次

诺浅 发布于 03/09 14:03
阅读 299
收藏 0

我们在启动一个springboot项目的时候会运行如下代码

public static void main(String[] args) {
	SpringApplication.run(HppaApplication.class, args);
}

这段代码的内部大概是去做了如下几件事

  • 加载系统环境变量和配置文件
  • 启动tomcat
  • 通知对应的监听器等

然后我们知道servlet3.0规范里面是说当我们web容器启动阶段会回调ServletContainerInitializer的onStartup方法。springboot中有一个ServletContainerInitializer的实现类SpringServletContainerInitializer,这个类采用

@HandlesTypes(WebApplicationInitializer.class)

这段代码注入了WebApplicationInitializer的实例,根据代码逻辑,最终会调用到SpringBootServletInitializer的onStartup方法

@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		// Logger initialization is deferred in case an ordered
		// LogServletContextInitializer is being used
		this.logger = LogFactory.getLog(getClass());
		WebApplicationContext rootAppContext = createRootApplicationContext(servletContext);
		if (rootAppContext != null) {
			servletContext.addListener(new ContextLoaderListener(rootAppContext) {

				@Override
				public void contextInitialized(ServletContextEvent event) {
					// no-op because the application context is already initialized
				}

			});
		}
		else {
			this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not "
					+ "return an application context");
		}
	}

这个又调用了createRootApplicationContext方法

protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
		SpringApplicationBuilder builder = createSpringApplicationBuilder();
		builder.main(getClass());
		ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
		if (parent != null) {
			this.logger.info("Root context already created (using as parent).");
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
			builder.initializers(new ParentContextApplicationContextInitializer(parent));
		}
		builder.initializers(new ServletContextApplicationContextInitializer(servletContext));
		builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
		builder = configure(builder);
		builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
		SpringApplication application = builder.build();
		if (application.getAllSources().isEmpty()
				&& MergedAnnotations.from(getClass(), SearchStrategy.TYPE_HIERARCHY).isPresent(Configuration.class)) {
			application.addPrimarySources(Collections.singleton(getClass()));
		}
		Assert.state(!application.getAllSources().isEmpty(),
				"No SpringApplication sources have been defined. Either override the "
						+ "configure method or add an @Configuration annotation");
		// Ensure error pages are registered
		if (this.registerErrorPageFilter) {
			application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
		}
		return run(application);
	}

createRootApplicationContext最后一段又会去调用SpringApplication的run方法,那么是不是说明springboot应用在启动过程中,SpringApplication的run方法会被执行两次呢?这样是不是有什么问题呢?

加载中
0
chentao106
chentao106

main里调用run启动,是jar包的拉起方式;

SpringBootServletInitializer是部署在Web容器(如Tomcat)里面使用Servlet3.0机制拉起的方式,参考

二者互不相干,是为了兼容不同的部署方式。

诺浅
诺浅
明白了,之前我一直以为main里面run启动的内置tomcat就会去加载SpringBootServletInitializer
0
SpringBoot中文社区
SpringBoot中文社区

一个是实例方法
public ConfigurableApplicationContext run(String... args)

一个是静态方法

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args)

 

诺浅
诺浅
但是静态方法最终还是会调用到实例方法的。 public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部