看springboot源码的时候感觉SpringApplication.run会被执行两次
我们在启动一个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方法会被执行两次呢?这样是不是有什么问题呢?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
但是静态方法最终还是会调用到实例方法的。 public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
一个是实例方法
public ConfigurableApplicationContext run(String... args)
一个是静态方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args)
明白了,之前我一直以为main里面run启动的内置tomcat就会去加载SpringBootServletInitializer
main里调用run启动,是jar包的拉起方式;
SpringBootServletInitializer是部署在Web容器(如Tomcat)里面使用Servlet3.0机制拉起的方式,参考;
二者互不相干,是为了兼容不同的部署方式。