嵌入式 Servlet 容器
Web 原生组件注入
1、使用嵌入式 Servlet 容器时,可以使用 Spring beans 或扫描 Servlet 组件,从 Servlet 规范中注册 Servlet,Filter、Listener
2、使用Servlet API(建议)
(1)@ServletComponentScan(basePackages = "包路径"):扫描指定包路径下的 Web 原生组件
(2)@WebServlet:注入原生 Servlet,urlPatterns 属性,以数组形式指定 URL 映射,直接响应,不经过 Spring 拦截器
(3)@WebFilter:注入原生 Filter,urlPatterns 属性,以数组形式指定 URL 映射
(4)@WebListener:注入原生 Listener
3、使用 RegistrationBean
(1)使用 ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean 进行完全控制
(2)proxyBeanMethods = true:保证依赖的组件始终是单实例的,若 Web 原生组件之间存在依赖关系,避免重复创建
(3)示例
@Configuration(proxyBeanMethods = true)
public class RegistrationBeanConfig {
@Bean
public ServletRegistrationBean customedServlet(){
CustomedServlet customedServlet = new CustomedServlet();
return new ServletRegistrationBean(customedServlet, "/home", "/main");
}
@Bean
public FilterRegistrationBean customedFilter(){
CustomedFilter customedFilter = new CustomedFilter();
/*
方式一
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(customedFilter);
filterRegistrationBean.setUrlPatterns(Arrays.asList("/static","/css/*"));
return filterRegistrationBean;
*/
//方式二
return new FilterRegistrationBean(customedFilter, customedServlet());
}
@Bean
public ServletListenerRegistrationBean CustomedListener(){
CustomedListener customedListener = new CustomedListener();
return new ServletListenerRegistrationBean(CustomedListener);
}
}
DispatchServlet 注入原理
1、DispatcherServletAutoConfiguration 自动配置类
2、容器中自动配置 DispatcherServlet,属性绑定到 WebMvcProperties
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
return dispatcherServlet;
}
(1)对应配置文件的配置项为 spring.mvc
@ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties
3、DispatcherServlet 通过 ServletRegistrationBean<DispatcherServlet> 注入
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
public class DispatcherServletRegistrationBean extends ServletRegistrationBean<DispatcherServlet> implements DispatcherServletPath
(1)默认映射 / 路径
public String getPath() {
return this.path;
}
private String path = "/";
(2)application.yaml 修改默认映射路径
spring:
mvc:
servlet:
path:
(3)若多个 Servlet 都能处理同一层路径,遵循精确匹配原则
嵌入式 Servlet 容器
1、默认支持的 Web 服务器:Tomcat、Jetty、Undertow
2、ServletWebServerApplicationContext 容器启动寻找 ServletWebServerFactory,并引导创建服务器
3、ServletWebServerApplicationContext
(1)Spring Boot 使用不同类型 ApplicationContext 来支持嵌入式 Servlet 容器
(2)ServletWebServerApplicationContext 是 WebApplicationContext 的一种特殊类型,它通过搜索单个 ServletWebServerFactory bean 来引导自己
(3)通常自动配置 Spring Boot 底层默认的 Web 服务器工厂:TomcatServletWebServerFactory、JettyServletWebServerFactory 或 UndertowServletWebServerFactory
4、原理
(1)SpringBoot 应用启动,发现当前为 Web 应用
(2)Web 应用创建一个 Web 版的 IOC 容器:ServletWebServerApplicationContext
(3)ServletWebServerApplicationContext 启动时,查找 ServletWebServerFactory(Servlet 的 Web 服务器工厂),该工厂创建 Servlet 的 Web 服务器
(4)自动配置类:ServletWebServerFactoryAutoConfiguration,前置导入 ServletWebServerFactoryConfiguration 配置类
(5)ServletWebServerFactoryConfiguration 配置类,其中配置默认三种 Web 服务器工厂,动态判断系统导入 Web 服务器的包(默认 Web 场景启动器导入 tomcat 包),按条件装配 Web 服务器工厂
(6)TomcatServletWebServerFactory 创建出 Tomcat 服务器,并启动,TomcatWebServer 构造器的初始化方法:initialize,调用 start 方法
5、切换
(1)默认 Web 场景启动器导入 Tomcat 启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.7.0</version>
<scope>compile</scope>
</dependency>
(2)排除 Tomcat 依赖
<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>
(3)导入以下其中一个启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
定制 Servlet 容器
1、方式一:修改 application.yaml 配置文件(建议)
(1)ServletWebServerFactoryAutoConfiguration 绑定的配置文件
@EnableConfigurationProperties(ServerProperties.class)
(2)该配置属性以 server: 开头
2、方式二:自行注册 Servlet 的 Web 服务器工厂
(1)自行注册 TomcatServletWebServerFactory,JettyServletWebServerFactory 或 UndertowServletWebServerFactory
(2)方法返回 ConfigurableServletWebServerFactory 类型,该接口提供配置选项的 setter 方法
(3)示例
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.setPort(9000);
factory.setSessionTimeout(10, TimeUnit.MINUTES);
factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notfound.html"));
return factory;
}
3、方式三:自定义 Servlet 的 Web 服务器定制化器
(1)注册实现 WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> 接口的 Spring bean
(2)WebServerFactoryCustomizer 提供对 ConfigurableServletWebServerFactory 的访问,其中包括自定义 setter 方法
(3)示例
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;
@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory server) {
server.setPort(9000);
}
}
(4)ServletWebServerFactoryAutoConfiguration 中注册 ServletWebServerFactoryCustomizer
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,
ObjectProvider<WebListenerRegistrar> webListenerRegistrars,
ObjectProvider<CookieSameSiteSupplier> cookieSameSiteSuppliers) {
return new ServletWebServerFactoryCustomizer(serverProperties,
webListenerRegistrars.orderedStream().collect(Collectors.toList()),
cookieSameSiteSuppliers.orderedStream().collect(Collectors.toList()));
}
(5)ServletWebServerFactoryCustomizer 从 ServerProperties 获取信息,把配置文件的值和 ServletWebServerFactory 进行绑定
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(this.serverProperties::getPort).to(factory::setPort);
map.from(this.serverProperties::getAddress).to(factory::setAddress);
map.from(this.serverProperties.getServlet()::getContextPath).to(factory::setContextPath);
map.from(this.serverProperties.getServlet()::getApplicationDisplayName).to(factory::setDisplayName);
map.from(this.serverProperties.getServlet()::isRegisterDefaultServlet).to(factory::setRegisterDefaultServlet);
map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);
map.from(this.serverProperties::getSsl).to(factory::setSsl);
map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);
map.from(this.serverProperties::getCompression).to(factory::setCompression);
map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);
map.from(this.serverProperties.getServlet()::getContextParameters).to(factory::setInitParameters);
map.from(this.serverProperties.getShutdown()).to(factory::setShutdown);
for (WebListenerRegistrar registrar : this.webListenerRegistrars) {
registrar.register(factory);
}
if (!CollectionUtils.isEmpty(this.cookieSameSiteSuppliers)) {
factory.setCookieSameSiteSuppliers(this.cookieSameSiteSuppliers);
}
}
常见定制化方式
1、修改 application.yaml 配置文件
(1)流程:场景 starter -> xxxAutoConfiguration 自动配置类 -> 导入对应组件 -> 绑定xxxProperties -> 绑定配置文件项
(2)只需关心导入场景启动器,与修改配置文件项
2、自定义定制化器:xxxCustomizer
3、@Configuration 自定义配置类 + @Bean 替换 / 增加容器中的默认组件,如:视图解析器
4、Web 应用:不使用 @EnableWebMvc,@Configuration 配置类实现 WebMvcConfigurer 接口,保留 Spring Boot MVC 功能,且定制化 Web 功能 + @Bean 扩展容器中的组件(建议)
5、声明 WebMvcRegistrations:改变默认底层组件,希望提供 RequestMappingHandlerMapping,RequestMappingHandlerAdapter 或 ExceptionHandlerExceptionResolver 的自定义实例(最底层)
public interface WebMvcRegistrations {
default RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return null;
}
default RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
return null;
}
default ExceptionHandlerExceptionResolver getExceptionHandlerExceptionResolver() {
return null;
}
}
6、@EnableWebMvc + @Configuration 配置类实现 WebMvcConfigurer 接口—— @Bean:全面接管 SpringMVC,所有规则全部重新配置, 实现定制和扩展功能
(1)WebMvcAutoConfiguration 默认为 SpringMVC 自动配置功能类
(2)容器中没有 WebMvcConfigurationSupport,WebMvcAutoConfiguration 才生效
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
(3)使用 @EnableWebMvc,导入 DelegatingWebMvcConfiguration 替代 WebMvcAutoConfiguration
@Import(DelegatingWebMvcConfiguration.class)
(4)DelegatingWebMvcConfiguration:只自动配置一些最核心的组件,依赖的组件都从容器中获取,保证 SpringMVC 最基本的使用
(5)获取系统中的所有 WebMvcConfigurer,使每个 WebMvcConfigurer 定制功能生效
(6)继承 WebMvcConfigurationSupport,导致 WebMvcAutoConfiguration 没有生效
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战