使用spring boot很方便,一个jar包就可以启动了,因为它里面内嵌了tomcat等服务器。

但是spring boot也提供了部署到独立服务器的方法。

如果你看文档的话,从jar转换为war包很简单,pom.xml的配置修改略去不讲。

只看source的修改,很简单,只要一个配置类,继承自SpringBootServletInitializer, 并覆盖configure方法。

  1. @SpringBootApplication
  2. public class TestApplication extends SpringBootServletInitializer{
  3. @Override
  4. protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
  5. return builder.sources(TestApplication .class);
  6. }
  7. public static void main(String[] args) {
  8. SpringApplication.run(TestApplication.class, args);
  9. }
  10. }
@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包里正好有这个文件,它注册的恰恰就是这个类

写道
org.springframework.web.SpringServletContainerInitializer

这个类有个注解@HandlesTypes(WebApplicationInitializer.class)。

调用SpringServletContainerInitializer.onStartup方法时,会把所有的WebApplicationInitializer类以及子类都传过来。

然后再通过条件过滤一下。

  1. if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
  2. WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
  3. try {
  4. initializers.add((WebApplicationInitializer) waiClass.newInstance());
  5. }
  6. catch (Throwable ex) {
  7. throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
  8. }
  9. }
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服务器的分支是在哪儿呢?

顺着这条线往下走。

  1. SpringApplication.run(String...)
  2. SpringApplication.createAndRefreshContext(SpringApplicationRunListeners, ApplicationArguments)
  3. SpringApplication.refresh(ApplicationContext)
  4. AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).refresh()
  5. AnnotationConfigEmbeddedWebApplicationContext(AbstractApplicationContext).refresh()
  6. AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).onRefresh()
  7. AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).createEmbeddedServletContainer()
SpringApplication.run(String...)
SpringApplication.createAndRefreshContext(SpringApplicationRunListeners, ApplicationArguments)
SpringApplication.refresh(ApplicationContext)
AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).refresh()
AnnotationConfigEmbeddedWebApplicationContext(AbstractApplicationContext).refresh()
AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).onRefresh()
AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).createEmbeddedServletContainer()

有下面一个分支代码

  1. if (localContainer == null && localServletContext == null) {
  2. EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
  3. this.embeddedServletContainer = containerFactory
  4. .getEmbeddedServletContainer(getSelfInitializer());
  5. }
		if (localContainer == null && localServletContext == null) {
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
this.embeddedServletContainer = containerFactory
.getEmbeddedServletContainer(getSelfInitializer());
}

localContainer在初始化的时候没有赋值过程,一直会是null,主要是localServletContext,看看什么时候为null,什么时候有值。

它的赋值有三个地方,两个构造函数,一个set方法

  1. public GenericWebApplicationContext(ServletContext servletContext) {
  2. this.servletContext = servletContext;
  3. }
  4. public GenericWebApplicationContext(DefaultListableBeanFactory beanFactory, ServletContext servletContext) {
  5. super(beanFactory);
  6. this.servletContext = servletContext;
  7. }
  8. public void setServletContext(ServletContext servletContext) {
  9. this.servletContext = servletContext;
  10. }
	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方法,过程如下

  1. SpringApplication.run(String...)
  2. SpringApplication.createAndRefreshContext(SpringApplicationRunListeners, ApplicationArguments)
  3. SpringApplication.applyInitializers(ConfigurableApplicationContext)
  4. ServletContextApplicationContextInitializer.initialize(ConfigurableApplicationContext)
  5. ServletContextApplicationContextInitializer.initialize(ConfigurableWebApplicationContext)
  6. AnnotationConfigEmbeddedWebApplicationContext(GenericWebApplicationContext).setServletContext(ServletContext)
SpringApplication.run(String...)
SpringApplication.createAndRefreshContext(SpringApplicationRunListeners, ApplicationArguments)
SpringApplication.applyInitializers(ConfigurableApplicationContext)
ServletContextApplicationContextInitializer.initialize(ConfigurableApplicationContext)
ServletContextApplicationContextInitializer.initialize(ConfigurableWebApplicationContext)
AnnotationConfigEmbeddedWebApplicationContext(GenericWebApplicationContext).setServletContext(ServletContext)

你会发现,至少到SpringApplication.applyInitializers(ConfigurableApplicationContext)这一步,部署不部署到tomcat,都会执行这个方法的,那么区别在哪儿呢?

先看看这个方法的内容

  1. protected void applyInitializers(ConfigurableApplicationContext context) {
  2. for (ApplicationContextInitializer initializer : getInitializers()) {
  3. Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
  4. initializer.getClass(), ApplicationContextInitializer.class);
  5. Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
  6. initializer.initialize(context);
  7. }
  8. }
	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的注入过程。

  1. builder.initializers(new ServletContextApplicationContextInitializer(servletContext));
builder.initializers(new ServletContextApplicationContextInitializer(servletContext));

内部实现就是

  1. public SpringApplicationBuilder initializers(
  2. ApplicationContextInitializer<?>... initializers) {
  3. this.application.addInitializers(initializers);
  4. return this;
  5. }
	public SpringApplicationBuilder initializers(
ApplicationContextInitializer<?>... initializers) {
this.application.addInitializers(initializers);
return this;
}

至于spring的御用servlet——DispatcherServlet,则是通过动态添加方式添加到ServletContext里的。类EmbeddedWebApplicationContext

  1. private void selfInitialize(ServletContext servletContext) throws ServletException {
  2. --------省略------------
  3. for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
  4. beans.onStartup(servletContext);
  5. }
  6. }
	private void selfInitialize(ServletContext servletContext) throws ServletException {
--------省略------------
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}

类ServletRegistrationBean

  1. @Override
  2. public void onStartup(ServletContext servletContext) throws ServletException {
  3. Assert.notNull(this.servlet, "Servlet must not be null");
  4. String name = getServletName();
  5. if (!isEnabled()) {
  6. logger.info("Servlet " + name + " was not registered (disabled)");
  7. return;
  8. }
  9. logger.info("Mapping servlet: '" + name + "' to " + this.urlMappings);
  10. Dynamic added = servletContext.addServlet(name, this.servlet);
  11. if (added == null) {
  12. logger.info("Servlet " + name + " was not registered "
  13. + "(possibly already registered?)");
  14. return;
  15. }
  16. configure(added);
  17. }
	@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传统部署的更多相关文章

  1. Spring Boot 系列(六)web开发-Spring Boot 热部署

    Spring Boot 热部署 实际开发中,修改某个页面数据或逻辑功能都需要重启应用.这无形中降低了开发效率,所以使用热部署是十分必要的. 什么是热部署? 应用启动后会把编译好的Class文件加载的虚 ...

  2. 玩转spring boot——war部署

    前言 之前部署spring boot应用是通过直接输入命令“java -jar”来实现的.而有些情况,由于部署环境的制约,只能把项目从jar转换成war才能部署,如新浪云sae的java环境容器.那怎 ...

  3. spring boot: 热部署spring-boot-devtools

    spring boot: 热部署spring-boot-devtools 1引入spring-boot-devtools依赖包 <!-- spring boot devtools 热部署 --& ...

  4. spring boot: 热部署(一) run as – java application (spring-loader-1.2.4.RELEASE.jar)

    spring boot: 热部署(一) run as – java application (spring-loader-1.2.4.RELEASE.jar) 如果使用的run as – java a ...

  5. 从零开始通过idea插件将一个spring boot项目部署到docker容器里运行

    实操:将一个spring boot项目部署到docker容器里运行 实验需要的环境: 腾讯云+Ubuntu 16.04 x64+idea+插件docker integration+daocloud 第 ...

  6. Spring Boot 热部署(转)

    Spring Boot 热部署 实际开发中,修改某个页面数据或逻辑功能都需要重启应用.这无形中降低了开发效率,所以使用热部署是十分必要的. 什么是热部署? 应用启动后会把编译好的Class文件加载的虚 ...

  7. Spring Boot热部署插件

    在实际开发中,我们修改某些代码逻辑功能或页面都需要重启应用,这无形中降低了开发效率,热部署是指当我们修改代码后,服务能自动重启加载新修改的内容,而不需要重启应用,这样大大提高了我们开发的效率. Spr ...

  8. spring boot tomcat 部署

    前几天springboot项目部署到linux中,整个过程就是个坑啊.踩坑的过程中也学到了许多.spring boot 项目部署时由于其内置了tomcat和jdk,而且还都是8. 所以部署的话就分为两 ...

  9. 多个Spring Boot项目部署在一个Tomcat容器无法启动

    转自https://www.cnblogs.com/tomxin7/p/9434085.html 业务介绍 最近用Spring Boot开发了一个翻译的小项目,但是服务器上还跑着其他项目,包括一个同样 ...

随机推荐

  1. sampleviewer add menu item error 'assert'

    可以跟踪到 mfc提供的源代码内部,(注:如果打开了mfc源代码,设置了断点,但是跟不进去,那就需要更新PDB文件,具体网上搜)打开 wincore.cpp文件(D:\Program Files\Mi ...

  2. iOS 之应用性能调优的25个建议和技巧

    注意:每在优化代码之前,你都要注意一个问题,不要养成"预优化"代码的错误习惯. 时常使用Instruments去profile你的代码来发现须要提升的方面.Matt Gallowa ...

  3. amazeui学习笔记--css(HTML元素3)--表单Form

    amazeui学习笔记--css(HTML元素3)--表单Form 一.总结 1.form样式使用:在容器上添加 .am-form class,容器里的子元素才会应用 Amaze UI 定义的样式. ...

  4. Project Euler 501 Eight Divisors (数论)

    题目链接: https://projecteuler.net/problem=501 题意: \(f(n)\) be the count of numbers not exceeding \(n\) ...

  5. HDU 1166 敌兵布阵 树状数组||线段树

    http://acm.hdu.edu.cn/showproblem.php?pid=1166 题目大意: 给定n个数的区间N<=50000,还有Q个询问(Q<=40000)求区间和. 每个 ...

  6. (转)kvm虚拟机中,如何给子系统更换光盘

    转自:http://www.cnblogs.com/york-hust/archive/2012/06/12/2546334.html 启动kvm后,在kvm窗口中,按下CTRL+ALT+2,切换至q ...

  7. AMBA标准

    AMBA标准定义了三种不同的总线 高级高性能总线(AHB): 高级系统总线(ASB):-----用的比较少 高级外设总线(APB). 基于AMBA的典型微控制器: 典型的AMBA AHB系统设计包含以 ...

  8. Maven学习总结(16)——深入理解maven生命周期和插件

    在项目里用了快一年的maven了,最近突然发现maven项目在eclipse中build时非常慢,因为经常用clean install命令来build项目,也没有管那么多,但最近实在受不了乌龟一样的b ...

  9. UVA 11728 - Alternate Task (数论)

    Uva 11728 - Alternate Task 题目链接 题意:给定一个因子和.求出相应是哪个数字 思路:数字不可能大于因子和,对于每一个数字去算出因子和,然后记录下来就可以 代码: #incl ...

  10. BestCoder Round #11 (Div. 2) 前三题题解

    题目链接: huangjing hdu5054 Alice and Bob 思路: 就是(x,y)在两个參考系中的表示演全然一样.那么仅仅可能在这个矩形的中点.. 题目: Alice and Bob ...