使用内置tomcat启动

配置案例

启动方式

IDEA中main函数启动

mvn springboot-run

java -jar XXX.jar 使用这种方式时,为保证服务在后台运行,会使用nohup

使用java -jar默认情况下,不会启动任何嵌入式Application Server,该命令只是启动一个执行jar main的JVM进程,当spring-boot-starter-web包含嵌入式tomcat服务器依赖项时,执行java -jar则会启动Application Server

配置内置tomcat属性

关于Tomcat的属性都在 org.springframework.boot.autoconfigure.web.ServerProperties 配置类中做了定义,我们只需在application.properties配置属性做配置即可。通用的Servlet容器配置都以 server 作为前缀

而Tomcat特有配置都以 server.tomcat 作为前缀

注意:使用内置tomcat不需要有tomcat-embed-jasper和spring-boot-starter-tomcat依赖,因为在spring-boot-starter-web依赖中已经集成了tomcat

原理

从main函数说起

既然我们想知道tomcat在SpringBoot中是怎么启动的,那么run方法中,重点关注创建应用上下文(createApplicationContext)和刷新上下文(refreshContext)。

创建上下文

这里会创建AnnotationConfigServletWebServerApplicationContext类。而AnnotationConfigServletWebServerApplicationContext类继承了ServletWebServerApplicationContext,而这个类是最终集成了AbstractApplicationContext。

刷新上下文

这里ServletWebServerFactory接口有4个实现类,对应着四种容器:

而其中我们常用的有两个:TomcatServletWebServerFactory和JettyServletWebServerFactory。

getWebServer这个方法创建了Tomcat对象,并且做了两件重要的事情:把Connector对象添加到tomcat中,configureEngine(tomcat.getEngine());

getWebServer方法返回的是TomcatWebServer。

使用外置tomcat部署

配置案例

外置Tomcat启动SpringBoot源码点击这里

继承SpringBootServletInitializer

  • 外部容器部署的话,就不能依赖于Application的main函数了,而是要以类似于web.xml文件配置的方式来启动Spring应用上下文,此时需要在启动类中继承SpringBootServletInitializer,并重写configure方法;还添加 @SpringBootApplication 注解,这是为了能扫描到所有Spring注解的bean

方式一:启动类继承SpringBootServletInitializer实现configure:

这个类的作用与在web.xml中配置负责初始化Spring应用上下文的监听器作用类似,只不过在这里不需要编写额外的XML文件了。

方式二:新增加一个类继承SpringBootServletInitializer实现configure:

pom.xml修改tomcat相关的配置

首先需要将 jar 变成war <packaging>war</packaging>

如果要将最终的打包形式改为war的话,还需要对pom.xml文件进行修改,因为spring-boot-starter-web中包含内嵌的tomcat容器,所以直接部署在外部容器会冲突报错。因此需要将内置tomcat排除

在这里需要移除对嵌入式Tomcat的依赖,这样打出的war包中,在lib目录下才不会包含Tomcat相关的jar包,否则将会出现启动错误。

但是移除了tomcat后,原始的sevlet也被移除了,因此还需要额外引入servet的包

注意的问题

此时打成的包的名称应该和 application.properties 的 server.context-path=/test 保持一致

如果不一样发布到tomcat的webapps下上下文会变化

原理

tomcat不会主动去启动springboot应用,, 所以tomcat启动的时候肯定调用了SpringBootServletInitializer的SpringApplicationBuilder, 就会启动springboot。

ServletContainerInitializer的实现放在jar包的META-INF/services文件夹下,有一个名为javax.servlet.ServletContainerInitializer的文件,内容就是ServletContainerInitializer的实现类的全类名。当servlet容器启动时候就会去该文件中找到ServletContainerInitializer的实现类,从而创建它的实例调用onstartUp。这里就是用了SPI机制

HandlesTypes(WebApplicationInitializer.class)

  • @HandlesTypes传入的类为ServletContainerInitializer感兴趣的

  • 容器会自动在classpath中找到 WebApplicationInitializer,会传入到onStartup方法的webAppInitializerClasses中

  • Set<Class<?>> webAppInitializerClasses这里面也包括之前定义的TomcatStartSpringBoot

SpringBootServletInitializer

① 当调用configure就会来到TomcatStartSpringBoot .configure,将Springboot启动类传入到builder.source

② 调用SpringApplication application = builder.build(); 就会根据传入的Springboot启动类来构建一个SpringApplication

③ 调用 return run(application); 就会启动springboot应用

也就相当于Main函数启动:

之后的流程就与上面 使用内置Tomcat的Main函数一致了