SpringBoot默认使用TomCat作为嵌入式的Servlet容器
-
如何定制和修改Servlet容器的相关配置
- 修改和Servlet有关的配置(ServerProperties)
//通用的Servlet容器设置 server.xxx= //Tomcat的设置 server.tomcat.xxx=
- 编写一个WebServerFactoryCustomizer
@Bean public WebServerFactoryCustomizer webServerFactoryCustomizer(){ return new WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>(){ //定制嵌入式的Servlet容器相关的规则 @Override public void customize(ConfigurableServletWebServerFactory factory) { factory.setPort(8083); } }; }
-
注册Servlet三大组件
由于SpringBoot默认是以jar包的方式启动嵌入式的Servlet容器来启动SpringBoot的Web项目,没有web.xml文件- ServletRegistrationBean
public class MyServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().write("Hello MyServlet"); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } }
@Bean public ServletRegistrationBean myServlet(){ ServletRegistrationBean<Servlet> registrationBean = new ServletRegistrationBean<>(new MyServlet(),"/myServlet"); return registrationBean; }
- FilterRegistration
public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("MyFilter process...."); filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy() { } }
@Bean public FilterRegistrationBean myFilter(){ FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>(); filterFilterRegistrationBean.setFilter(new MyFilter()); filterFilterRegistrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet")); return filterFilterRegistrationBean; }
- ServletListenerRegistrationBean
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项目销毁"); } }
@Bean public ServletListenerRegistrationBean myListener(){ ServletListenerRegistrationBean<EventListener> eventListenerServletListenerRegistrationBean = new ServletListenerRegistrationBean<>(new MyListener()); return eventListenerServletListenerRegistrationBean; }
- SpringBoot帮我们自动配置SpringMVC的时候,自动注册SpringMVC的前端控制器;DispatcherServlet
@Bean( name = {"dispatcherServletRegistration"} ) @ConditionalOnBean( value = {DispatcherServlet.class}, name = {"dispatcherServlet"} ) public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) { DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath()); //默认拦截:"/" 所以请求,包括静态资源,但是不拦截jsp请求;"/*"会拦截jsp //可以通过server.servletPath来修改SpringMVC前端控制器默认拦截的请求路径 registration.setName("dispatcherServlet"); registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup()); multipartConfig.ifAvailable(registration::setMultipartConfig); return registration; }
SpringBoot能不能支持其他的Servlet容器
- TomCat(默认)
- Jetty
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入其他的Servlet容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
- Undertow
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入其他的Servlet容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
嵌入式Servlet容器自动配置原理
嵌入式Servlet容器启动原理
使用外置的Servlet容器
-
嵌入式Servlet容器:jar
- 优点:简单,便捷
- 缺点:默认不支持JSP、优化定制比较复杂(使用定制器【ServerProperties、自定义WebServerFactoryCustomizer】),自己编写嵌入式Servlet容器创建工厂【ConfigurableServletWebServerFactory 】)
-
外置的Servlet容器:外面安装Tomcat–应用war包的方式打包
- 必须创建一个war项目
- 将嵌入式Tomcat指定为provided
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency>
- 必须编写一个SpringBootServletInitializer,并调用configure方法
public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { //传入SpringBoot应用主程序 return application.sources(DemoApplication.class); } }
- 启动服务器就可以使用
-
原理
-
jar包:执行Spring Boot主类的main方法,启动IOC容器,创建嵌入式的Servlet容器
-
war包:启动服务器,服务器启动SpringBoot应用【SpringBootServletInitializer】,启动ioc容器
-
规则:
- 服务器启动(web应用启动)会创建当前web应用里面每一个jar包里面ServletContainerInitializer实例
- ServletContainerInitializer的实现放在jar包的META-INF/service文件夹下,有一个名为javax.servlet.ServletContainerInitializer的文件,内容就是ServletContainerInitializer实现类的全类名
- 还可以使用@HandlesTypes,在应用启动的时候加载我们感兴趣的类
-
流程
-
启动Tomcat
-
org\springframework\spring-web\5.3.1\spring-web-5.3.1.jar!\META-INF\services\javax.servlet.ServletContainerInitializer;文件内容:org.springframework.web.SpringServletContainerInitializer
-
SpringServletContainerInitializer将@HandlesTypes({WebApplicationInitializer.class})标注的所有这个类型的类都传入到onStartup的Set<Class<?>>,为这些WebApplicationInitializer类型的类创建实例
-
每一个WebApplicationInitializer都调用自己的onStartup
-
相当于我们的SpringBootServletInitializer的类会被创建对象,并执行onStartup方法
-
SpringBootServletInitializer示例执行onStartup的时候会createRootApplicationContext创建容器
protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) { //创建SpringApplicationBuilder构建器 SpringApplicationBuilder builder = this.createSpringApplicationBuilder(); builder.main(this.getClass()); ApplicationContext parent = this.getExistingRootWebApplicationContext(servletContext); if (parent != null) { this.logger.info("Root context already created (using as parent)."); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, (Object)null); builder.initializers(new ApplicationContextInitializer[]{new ParentContextApplicationContextInitializer(parent)}); } builder.initializers(new ApplicationContextInitializer[]{new ServletContextApplicationContextInitializer(servletContext)}); builder.contextFactory((webApplicationType) -> { return new AnnotationConfigServletWebServerApplicationContext(); }); //调用configure方法,子类重写了这个方法,将SpringBoot的主程序类传入进来 builder = this.configure(builder); builder.listeners(new ApplicationListener[]{new SpringBootServletInitializer.WebEnvironmentPropertySourceInitializer(servletContext)}); //使用builder创建一个Spring应用 SpringApplication application = builder.build(); if (application.getAllSources().isEmpty() && MergedAnnotations.from(this.getClass(), SearchStrategy.TYPE_HIERARCHY).isPresent(Configuration.class)) { application.addPrimarySources(Collections.singleton(this.getClass())); } Assert.state(!application.getAllSources().isEmpty(), "No SpringApplication sources have been defined. Either override the configure method or add an @Configuration annotation"); if (this.registerErrorPageFilter) { application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class)); } application.setRegisterShutdownHook(false); //启动Spring应用 return this.run(application); }
- Spring的应用就启动了,并且创建IOC容器
- 先启动Servlet容器,在启动SpringBoot应用
-
-