【spring boot2】第8篇:spring boot 中的 servlet 容器及如何使用war包部署
嵌入式 servlet 容器
在 spring boot 之前的web开发,我们都是把我们的应用部署到 Tomcat 等servelt容器,这些容器一般都会在我们的应用服务器上安装好环境,但是 spring boot 中并不需要外部应用服务器安装这些servlet容器,spring boot自带了嵌入式的servlet容器。
如何修改和定制嵌入式servlet容器
- 在application.yaml文件中配置修改
#修改服务端口号 server.port=8081 #配置统一请求路径 server.context‐path=/crud #配置tomcat相关 server.tomcat.uri‐encoding=UTF‐8
这些配置相关的属性都定义在org.springframework.boot.autoconfigure.web.ServerProperties
类中
- 编写一个嵌入式的servlet容器的定制器来修改相关的配置
@Component public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> { @Override public void customize(ConfigurableServletWebServerFactory server) { server.setPort(9000); } }
- 直接定制具体的servlet容器配置,比如 tomcat 容器
@Configuration public class ApplicationConfig implements WebMvcConfigurer { @Bean public ConfigurableServletWebServerFactory configurableServletWebServerFactory() { TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); factory.setPort(8585); return factory; } }
注册 Servlet、Filter、Listener
spring boot默认是以 jar 包的方式启动嵌入式的 servlet 容器来启动web应用,应用中并没有 web.xml 文件,我们注册 servlet, filter, 和 listener
三大组件可以使用以下方式
利用
ServletRegistrationBean
注册servlet
- 定义自己的
servlet
public class MyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().print("servlet doGet"); } }
- 给容器中注册自己的
servlet
@Configuration public class ApplicationConfig implements WebMvcConfigurer { @Bean public ServletRegistrationBean myServlet() { ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new MyServlet(), "/hello/servlet"); return servletRegistrationBean; } }
- 定义自己的
利用
FilterRegistrationBean
注册filter
- 定义
filter
`
public class MyFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("do myFilter"); chain.doFilter(request, response); } }
- 容器中注册
filter
@Configuration public class ApplicationConfig implements WebMvcConfigurer { @Bean public FilterRegistrationBean myFilter() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); //注册自己的 filter filterRegistrationBean.setFilter(new MyFilter()); //注册拦截路径 filterRegistrationBean.setUrlPatterns(Arrays.asList("/hello", "/hello/servlet")); return filterRegistrationBean; } }
访问
"/hello", "/hello/servlet"
请求时拦截器就会起作用。- 定义
利用
ServletListenerRegistrationBean
注册listener
- 定义一个
listener
public class MyListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("contextInitialized ... web应用启动"); } @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("contextDestroyed ... web应用销毁"); } }
- 容器中注册
listener
@Configuration public class ApplicationConfig implements WebMvcConfigurer { @Bean public ServletListenerRegistrationBean myListener() { ServletListenerRegistrationBean<MyListener> listenerRegistrationBean = new ServletListenerRegistrationBean(new MyListener()); return listenerRegistrationBean; } }
- 定义一个
切换其他 servlet 容器
spring boot 中默认使用的是 tomcat 容器,那么如何切换使用其他容器呢,比如 jetty。那么如何切换成其他的呢?只需要修改 pom.xml 文件
<!‐‐ 引入web模块 ‐‐> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring‐boot‐starter‐web</artifactId> <exclusions> <exclusion> <artifactId>spring‐boot‐starter‐tomcat</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions> </dependency> <!‐‐引入其他的servlet容器‐‐> <dependency> <artifactId>spring‐boot‐starter‐jetty</artifactId> <groupId>org.springframework.boot</groupId> </dependency>
使用外部 servlet 容器
部署项目的服务器安装 servlet 容器,比如 tomcat、jetty 容器,然后项目打成 war 包的形式进行部署
如何创建一个 war 包启动的 spring boot 项目呢?
- 创建一个war打包形式的web项目,其目录结构如下
修改 pom.xml 文件
修改打包方式
pom.xml 文件中的打包方式设置成 war 即可, ```<packaging>war</packaging>```
修改嵌入的 servlet 容器
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency>
- 编写一个类继承
SpringBootServletInitializer
类
public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(SpringBootWarApplication.class); } }
使用外部 servlet 容器原理
jar包和war的启动过程
- jar包: 执行spring boot 主类的main方法,启动ioc容器,然后创建嵌入式的servlet容器
- war包: 启动tomcat服务器,服务器启动spring boot应用,通过
SpringBootServletInitializer
实现,启动ioc容器
spring boot使用外部servlet过程
servlet3.0标准中规定
- web应用启动会创建当前web应用里面每一个jar包里面
ServletContainerInitializer
类型的实例 ServletContainerInitializer
的实现放在jar包的META-INF/services文件夹下,有一个名为javax.servlet.ServletContainerInitializer
的文件,文件内容就是ServletContainerInitializer
实现类的全类名- 使用@HandlesTypes注解在应用启动的时候加载我们需要的类
spring boot使用war启动原理
- 重写
SpringBootServletInitializer
的configure(SpringApplicationBuilder application)
方法,调用SpringApplicationBuilder
的sources(Class<?>... sources)
方法 启动原理
- Servlet3.0标准ServletContainerInitializer扫描所有jar包中
META- INF/services/javax.servlet.ServletContainerInitializer
文件指定的类并加载 - 加载spring web包下的
org.springframework.web.SpringServletContainerInitializer
- 扫描
@HandleType(WebApplicationInitializer)
`,即扫描WebApplicationInitializer
的所有实现类 - 加载
SpringBootServletInitializer
并运行onStartup
方法 - 加载
@SpringBootApplication
主类,启动容器
- Servlet3.0标准ServletContainerInitializer扫描所有jar包中