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