Spring Boot 中的 DispatcherServlet 和 web.xml

1、概览

DispatcherServlet 是 Spring Web 应用的前端控制器(Front Controller)。它用于在 Spring MVC 中创建 Web 应用和 REST 服务。在传统的 Spring Web 应用中,该 Servlet 是在 web.xml 文件中定义的。

本文将会带你了解如何在 Spring Boot 项目中配置 DispatcherServlet,以及如何配置 web.xml 中的 FilterServletListener

2、Maven 依赖

首先,在 pom.xml 文件中添加 spring-boot-starter-web Maven 依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

3、DispatcherServlet

DispatcherServlet 接收所有 HTTP 请求,并将其调度给 Controller 类。

在 Servlet 3.x 规范发布之前,DispatcherServlet 会在 Spring MVC 应用的 web.xml 文件中注册。自 Servlet 3.x 规范发布后,可以使用 ServletContainerInitializer 以编程方式注册 Servlet

web.xml 文件中的 DispatcherServlet 配置示例:

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

Spring Boot 为使用 Spring MVC 开发 Web 应用提供了开箱即用的 spring-boot-starter-web 组件。Spring Boot 的主要功能之一是自动配置。Spring Boot 自动配置会自动注册和配置 DispatcherServlet。因此,我们无需手动注册它。

默认情况下,spring-boot-starter-web Starter 会将 DispatcherServlet 的 URL Pattern 配置为 "/"。因此,无需在 web.xml 文件中为上述 DispatcherServlet 示例进行任何额外配置。不过,你可以在 application.properties 文件中使用 server.servlet.* 自定义 URL Pattern:

server.servlet.context-path=/demo
spring.mvc.servlet.path=/baeldung

如上,DispatcherServlet 的 URL Pattern 配置为 /baeldung,Root ContextPath 是 /demo。因此,DispatcherServlet 会处理 http://localhost:8080/demo/baeldung/ 下的请求。

4、应用配置

Spring MVC Web 应用除了将 web.xml 文件用作部署描述符文件外,它还定义了 FilterServletListener 以及他们对应的 URL 映射。

在 Spring Boot 中则有所更改。当我们想从传统的 Spring MVC 迁移到现代的 Spring Boot 应用时,如何将 web.xml 中配置的组件移植到新的 Spring Boot 应用中呢?在 Spring Boot 应用中,可以通过多种方式添加这些 Servlet 组件。

4.1、注册 Filter

通过实现 Filter 接口来创建一个 Filter

@Component
public class CustomFilter implements Filter {

    Logger logger = LoggerFactory.getLogger(CustomFilter.class);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
        logger.info("CustomFilter is invoked");
        chain.doFilter(request, response);
    }

    // 其他方法 
}

如果没有 Spring Boot,则需要在 web.xml 文件中配置 CustomFilter,如下:

<filter>
    <filter-name>customFilter</filter-name>
    <filter-class>CustomFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>customFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

要让 Spring Boot 加载 Filter,只需要在 Filter 类上注解 @Component,使之成为一个 Spring Bean 即可。

4.2、注册 Servlet

通过继承 HttpServlet 类来定义一个 servlet:

public class CustomServlet extends HttpServlet {

    Logger logger = LoggerFactory.getLogger(CustomServlet.class);

    @Override
    protected void doGet(
        HttpServletRequest req,
        HttpServletResponse resp) throws ServletException, IOException {
            logger.info("CustomServlet doGet() method is invoked");
            super.doGet(req, resp);
    }

    @Override
    protected void doPost(
        HttpServletRequest req,
        HttpServletResponse resp) throws ServletException, IOException {
            logger.info("CustomServlet doPost() method is invoked");
            super.doPost(req, resp);
    }
}

如果没有 Spring Boot,则需要在 web.xml 文件中配置 CustomServlet,如下:

<servlet>
    <servlet-name>customServlet</servlet-name>
    <servlet-class>CustomServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>customServlet</servlet-name>
    <url-pattern>/servlet</url-pattern>
</servlet-mapping>

在 Spring Boot 应用中,Servlet 可以通过以下两种方式进行注册:作为 Spring 的 @Bean 或通过扫描带有嵌入式容器 @WebServlet 注解的类来注册。

可以通过 ServletRegistrationBean 类以 Spring @Bean 的方式来注册 Servlet。

使用 ServletRegistrationBean 类把 CustomServlet 定义为一个 Bean:

@Bean
public ServletRegistrationBean customServletBean() {
    ServletRegistrationBean bean = new ServletRegistrationBean(new CustomServlet(), "/servlet");
    return bean;
}

4.3、注册 Listener

通过继承 ServletContextListener 类来定义一个监听器:

public class CustomListener implements ServletContextListener {

    Logger logger = LoggerFactory.getLogger(CustomListener.class);

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        logger.info("CustomListener is initialized");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        logger.info("CustomListener is destroyed");
    }
}

如果没有 Spring Boot,则需要在 web.xml 文件中配置 CustomListener

<listener>
    <listener-class>CustomListener</listener-class>
</listener>

可以使用 @Bean@WebListener 注解在 Spring Boot 应用中定义监听器。

可以通过 ServletListenerRegistrationBean 类以 Spring @Bean 的方式来注册 Listener

使用 ServletListenerRegistrationBean 类将 CustomListener 定义为一个 Bean:

@Bean
public ServletListenerRegistrationBean<ServletContextListener> customListenerBean() {
    ServletListenerRegistrationBean<ServletContextListener> bean = new ServletListenerRegistrationBean();
    bean.setListener(new CustomListener());
    return bean;
}

启动应用后,可以查看日志输出,确认 Listener 已成功初始化:

2020-09-28 08:50:30.872 INFO 19612 --- [main] c.baeldung.demo.listener.CustomListener: CustomListener is initialized

5、总结

本文介绍了如何在 Spring Boot 应用中定义 DispatcherServletweb.xml 中的元素(包括 FilterServletListener)。


Ref:https://www.baeldung.com/spring-boot-dispatcherservlet-web-xml