使用spring boot很方便,一个jar包就可以启动了,因为它里面内嵌了tomcat等服务器。
但是spring boot也提供了部署到独立服务器的方法。
如果你看文档的话,从jar转换为war包很简单,pom.xml的配置修改略去不讲。
只看source的修改,很简单,只要一个配置类,继承自SpringBootServletInitializer, 并覆盖configure方法。
@SpringBootApplication public class TestApplication extends SpringBootServletInitializer{ @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(TestApplication .class); } public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } }
对,你没看错,就这么简单。
但是,我觉得但凡有点儿好奇心的人都不甘于就这么用它,总会想知道为啥这样就行了?
那么我们根据调用关系来弄个究竟。
SpringBootServletInitializer.configure
<-createRootApplicationContext
<-onStartup
<-SpringServletContainerInitializer.onStartup
SpringServletContainerInitializer这个类比较特殊,实现的是interface ServletContainerInitializer,这个类的onStartup方法,是由tomcat调用了。
那么tomcat是怎么找到它的呢?是搜寻的这个资源文件META-INF/services/javax.servlet.ServletContainerInitializer
而在spring的包spring-web-xxxx.jar包里正好有这个文件,它注册的恰恰就是这个类
这个类有个注解@HandlesTypes(WebApplicationInitializer.class)。
调用SpringServletContainerInitializer.onStartup方法时,会把所有的WebApplicationInitializer类以及子类都传过来。
然后再通过条件过滤一下。
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) { try { initializers.add((WebApplicationInitializer) waiClass.newInstance()); } catch (Throwable ex) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); } }
也就是只要是非interface,且非抽象类,并都是WebApplicationInitializer的字类的话,就会被实例化,并最终调用。
然后,在SpringBootServletInitializer的createRootApplicationContext方法里,最终会初始化SpringApplication,调用其run方法,跟直接运行入口的main方法是一样的了。
既然从,SpringApplication.run方法以后走的逻辑是一样的,那么是不是需要启动内嵌web服务器的分支是在哪儿呢?
顺着这条线往下走。
SpringApplication.run(String...) SpringApplication.createAndRefreshContext(SpringApplicationRunListeners, ApplicationArguments) SpringApplication.refresh(ApplicationContext) AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).refresh() AnnotationConfigEmbeddedWebApplicationContext(AbstractApplicationContext).refresh() AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).onRefresh() AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).createEmbeddedServletContainer()
有下面一个分支代码
if (localContainer == null && localServletContext == null) { EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory(); this.embeddedServletContainer = containerFactory .getEmbeddedServletContainer(getSelfInitializer()); }
localContainer在初始化的时候没有赋值过程,一直会是null,主要是localServletContext,看看什么时候为null,什么时候有值。
它的赋值有三个地方,两个构造函数,一个set方法
public GenericWebApplicationContext(ServletContext servletContext) { this.servletContext = servletContext; } public GenericWebApplicationContext(DefaultListableBeanFactory beanFactory, ServletContext servletContext) { super(beanFactory); this.servletContext = servletContext; } public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; }
查找一下,发现构造函数并没有地方调用,调用的是这个set方法,过程如下
SpringApplication.run(String...) SpringApplication.createAndRefreshContext(SpringApplicationRunListeners, ApplicationArguments) SpringApplication.applyInitializers(ConfigurableApplicationContext) ServletContextApplicationContextInitializer.initialize(ConfigurableApplicationContext) ServletContextApplicationContextInitializer.initialize(ConfigurableWebApplicationContext) AnnotationConfigEmbeddedWebApplicationContext(GenericWebApplicationContext).setServletContext(ServletContext)
你会发现,至少到SpringApplication.applyInitializers(ConfigurableApplicationContext)这一步,部署不部署到tomcat,都会执行这个方法的,那么区别在哪儿呢?
先看看这个方法的内容
protected void applyInitializers(ConfigurableApplicationContext context) { for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } }
也就是说,如果注入的initializers里是否包含了ServletContextApplicationContextInitializer,就能决定是否会调用以后的逻辑。
那么返回到文章的开头,看看抽象类SpringBootServletInitializer,就会发现在方法createRootApplicationContext里,类ServletContextApplicationContextInitializer的注入过程。
builder.initializers(new ServletContextApplicationContextInitializer(servletContext));
内部实现就是
public SpringApplicationBuilder initializers( ApplicationContextInitializer<?>... initializers) { this.application.addInitializers(initializers); return this; }
至于spring的御用servlet——DispatcherServlet,则是通过动态添加方式添加到ServletContext里的。类EmbeddedWebApplicationContext
private void selfInitialize(ServletContext servletContext) throws ServletException { --------省略------------ for (ServletContextInitializer beans : getServletContextInitializerBeans()) { beans.onStartup(servletContext); } }
类ServletRegistrationBean
@Override public void onStartup(ServletContext servletContext) throws ServletException { Assert.notNull(this.servlet, "Servlet must not be null"); String name = getServletName(); if (!isEnabled()) { logger.info("Servlet " + name + " was not registered (disabled)"); return; } logger.info("Mapping servlet: '" + name + "' to " + this.urlMappings); Dynamic added = servletContext.addServlet(name, this.servlet); if (added == null) { logger.info("Servlet " + name + " was not registered " + "(possibly already registered?)"); return; } configure(added); }
相关推荐
Spring Boot 使创建独立的、产品级的、基于Spring的应用变得更容易,你只需要运行run即可...你可以使用Spring Boot创建Java应用,用java-jar或更传统的war包来部署应用。我们也提供了运行”Spring脚本”的命令行工具。
Tomcat 、Jetty 或者Undertow 等服务器,并且不需要传统的WAR 文件进行部署,也就是说搭建Spring Boot 项目并不需要单独下载Tomcat 等传统的服务器:同时提供通过Maven (或者Grandle )依赖的 starter ,这些...
Spring Boot 2 官方指导手册译文 ...您可以使用 Spring Boot 来创建 Java 应用程序,这些应用程序可以通过使用 java -jar或更传统的 war 部署来启动。我们还提供了一个运行“spring 脚本”的命令行工具。
Spring Boot 入门:详细介绍了 Spring Boot 框架的基本概念和用法。包括如何使用 Spring Boot 初始化和配置应用程序、自动配置、起步依赖等内容。 Spring Cloud 入门:介绍了 Spring Cloud 框架及其组件的基本原理...
4? 第1章会对Spring Boot进行概述,内容涵盖最基本的自动配置、起步依赖、命令行界面和Actuator。? 第2章会进一步深入... 第8章讨论了各种部署Spring Boot应用程序的方法,包括传统的应用程序服务器部署和云部署。
您可以使用Spring Boot创建可以使用java -jar或更传统的war部署启动的Java应用程序 。我们还提供了一个运行“spring脚本”的命令行工具。 我们的主要目标是: 为所有Spring开发提供从根本上更快且可广泛访问的入门...
希望对大家有帮助,Spring Boot可以基于Spring轻松创建可以“运行”的、独立的、生产级的应用程序。 对Spring平台和第三方类库...您可以使用Spring Boot创建可以使用java -jar或传统 war 包部署启动的Java应用程序。
Spring Boot使开发独立的,产品级别的基于Spring的应用变得非常简单,你只...你可以使用Spring Boot创建Java应用,并使用 java -jar 启动它或采用传统的war部署方式。我们也提供了一个运行"spring 脚本"的命令行工具。
该系统采用Java语言开发,利用Spring Boot框架简化了传统Spring应用的复杂配置和部署过程,使得项目快速启动和易于维护。系统主要特点:模块化设计:系统按照功能划分为多个模块,如客户管理、预约服务、疗养项目、...
使得基于 Spring 框架的 Java 企业级应用开发“极简化” 相比于传统的 Spring/Spring MVC架的企业级应用开发(Spring 的各种配置太复杂了,我们之前是用“生命”在搞这些配置),Spring Boot 用简单的注解和 ...
基于Spring Boot + Vue3 前后端分离商城系统,下载下来即可以使用,可以作为毕业设计和课程设计。 项目是一套电商系统,包括 newbee-mall 商城系统及 newbee-mall-admin 商城后台管理系统,基于 Spring Boot 和 Vue...
你可以使用Spring Boot创建Java应用,并使用 java -jar 启动它或采用传统的 war部署方式。我们也提供了一个运行"spring脚本"的命令行工具。 我们主要的目标是: 为所有Spring开发提供一个从根本上更快,且随处可得的...
您可以使用 SpringBoot 创建 Java 应用程序,通过使用 Java -jar 或更传统的 war 包进行部署启动。我们还提供了一个运行 spring 脚本的命令行工具。 我们的主要目标是: 为所有 Spring 开发提供一个非常快速和广泛...
您可以使用Spring Boot创建可以使用java -jar或更传统的war部署启动的Java应用程序。 我们还提供了运行“ spring脚本”的命令行工具。 有关Spring Boot的更多信息: : 解剖学 该应用程序由一个控制器组成,该控制...
《Spring Boot实战》全书分为8章。 第1章会对Spring Boot进行概述,内容涵盖最基本的自动配置、起步依赖、命令行界面... 第8章讨论了各种部署Spring Boot应用程序的方法,包括传统的应用程序服务器部署和云 部署。
此外,我们将实现另一个Spring Boot应用程序,该应用程序侦听这些事件并将其保存在 。 最后,我们将使用运行集成测试。 注意:在存储库中,显示了如何在Kubernetes ( Minikube )中部署此项目。项目架构应用领域...
由于Spring Boot自带了spring-boot-starter-web,spring-boot-starter-jdbc,spring-boot-starter-actuator等子项目,它使得编码,配置,部署和监控等方面都变得简单,相比传统框架的开发,使用Spring Boot将大大...
Spring Boot使您能够以最小的麻烦创建独立的,生产级的,基于Spring的应用程序和服务。... 您可以使用它来创建独立的Java应用程序或更传统的WAR部署。 还提供了一个命令行工具,使您可以运行spring脚本。