当前位置:   article > 正文

Spring Web MVC知道这些就够了_webmvc

webmvc

Version 5.3.30-SNAPSHOThttps://docs.spring.io/spring-framework/docs/5.3.30-SNAPSHOT/reference/html/web.html#mvc-servlet-config

Spring Web MVC是基于Servlet API的原始Web框架,从一开始就包含在Spring框架中。其正式名称“Spring Web MVC”来自其源模块(Spring -webmvc)的名称,但更常见的名称是“Spring MVC”。

与Spring Web MVC并行,Spring Framework 5.0引入了一个响应式堆栈Web框架,其名称“Spring WebFlux”也是基于其源模块(Spring - WebFlux)。

在挖掘Spring Web MVC之前,我们需要先搞清两个概念:MVC模式和前端控制器模式。

-1MVC模式

MVC是一种软件设计模式。MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。

  • Model(模型) - 模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器。
  • View(视图) - 视图代表模型包含的数据的可视化。
  • Controller(控制器) - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。

在这里插入图片描述

-1.1实现

我们创建Car模型对象,CarView是将Car信息打印在控制台的视图类,CarController负责存储模型数据到该控制器类并把相应的模型信息更新到CarView视图。

  • Car

    package com.example.demo.mvc;
    
    /**
     * @Author yrz
     * @create 2023/7/27 14:10
     * @Description TODO
     */
    
    public class Car {
        public Car() {
        }
    
        public Car(String plateNo, String color) {
            this.plateNo = plateNo;
            this.color = color;
        }
    
        /**
         * 车牌
         */
        private String plateNo;
        /**
         * 车颜色
         */
        private String color;
    
        public String getPlateNo() {
            return plateNo;
        }
    
        public void setPlateNo(String plateNo) {
            this.plateNo = plateNo;
        }
    
        public String getColor() {
            return color;
        }
    
        public void setColor(String color) {
            this.color = color;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
  • CarView

    package com.example.demo.mvc;
    
    /**
     * @Author yrz
     * @create 2023/7/27 14:14
     * @Description TODO
     */
    
    public class CarView {
        public void printCarInfo(Car car){
            System.out.println(car.getPlateNo() + " " + car.getColor());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • CarController

    package com.example.demo.mvc;
    
    /**
     * @Author yrz
     * @create 2023/7/27 14:16
     * @Description TODO
     */
    
    public class CarController {
        private Car car;
        private CarView carView;
    
        public CarController(Car car, CarView carView) {
            this.car = car;
            this.carView = carView;
        }
    
        public void setCarPlateNo(String plateNo){
            car.setPlateNo(plateNo);
        }
    
        public void setCarColor(String color){
            car.setColor(color);
        }
    
        public void updateCarView(){
            carView.printCarInfo(car);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
  • MVCPatternDemo

    package com.example.demo.mvc;
    
    /**
     * @Author yrz
     * @create 2023/7/27 14:19
     * @Description TODO
     */
    
    public class MVCPatternDemo {
        public static void main(String[] args) {
            Car car = new Car("冀E6666", "红色");
            CarView carView = new CarView();
            CarController carController = new CarController(car, carView);
            carController.updateCarView();
            car.setColor("蓝色");
            carController.updateCarView();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

-1.2分工

常见的基于MVC框架的B/S架构应用软件中,前端(Client Side)和后端(Server Side)负责不同的任务和职责。

  • 前端(客户端)主要负责以下方面:
  1. 视图(View):前端负责创建和渲染用户界面,包括UI的布局、样式和交互。它负责将数据展示给用户,并接收用户的输入。

用户交互:前端处理用户的输入和交互,例如接收用户的点击、输入和操作,并相应地更新视图。

  • 后端(服务器端)主要负责以下方面:
  1. 模型(Model):后端负责处理业务逻辑和数据持久化,包括与数据库或其他持久化存储的交互。它定义了数据的结构和规则,并提供对数据的 CRUD(创建、读取、更新、删除)操作。
  2. 控制器(Controller):后端负责处理业务逻辑的控制和流程,根据用户的请求进行相应的处理。它接收来自前端的请求,处理请求并对模型进行操作,然后返回结果给前端。
  3. 数据存储和管理:后端负责与持久化存储(如数据库)进行交互,包括读取、写入、修改和删除数据。它还可以负责缓存数据以提高性能,并处理并发访问和安全性等方面的问题。
    总体而言,在MVC框架中,前端负责展示用户界面和处理用户的交互,后端负责处理业务逻辑和数据存储。前端与后端通过定义良好的接口和协议进行通信,前端将用户的请求发送给后端进行处理,并接收后端返回的数据和结果,从而实现一个完整的应用程序。

0前端控制器模式

前端控制器模式(Front Controller Pattern)是一种软件设计模式,用于构建 Web 应用程序的请求处理流程。它提供了一个中心化的组件,即前端控制器,用于集中处理所有的请求,并根据请求的类型和内容来调度相应的处理程序。

在前端控制器模式中,所有的请求都先经过前端控制器,然后根据请求的信息,分发给适当的处理程序来执行实际的处理逻辑。这种集中式的请求处理可以带来以下一些好处:

  1. 中心化的请求处理:前端控制器作为应用程序的入口点,负责接收和处理所有的请求。这种集中化的处理方式简化了请求分发和处理的逻辑,提高了代码的可维护性和重用性。

  2. 统一的请求处理:通过前端控制器,可以实现对请求的统一处理。例如,可以在前端控制器中进行身份验证、请求日志记录、异常处理等共同的处理步骤,避免在每个处理程序中重复编写相同的代码。

  3. 支持多样化的请求处理策略:前端控制器可以根据请求的类型、内容或其他标识来动态选择适当的处理程序。这种灵活性使得系统能够根据不同的请求需求进行定制化的处理,提升系统的可扩展性和适应性。

  4. 支持复杂的请求分发和路由:前端控制器可以根据请求的路由信息将请求分发给不同的处理程序。这种灵活的请求分发机制可以支持复杂的请求路由策略,如基于 URL、请求参数、请求头等进行路由和分发。

总的来说,前端控制器模式通过引入一个中心化的前端控制器组件,将请求的分发和处理集中在一个地方,提供了一种结构化和可扩展的方式来处理和管理 Web 应用程序的请求流程。它可以简化代码结构,提高代码复用性,并支持灵活的请求处理策略。

前端控制器设计模式(Front Controller Pattern)是用来提供一个集中的请求处理机制,所有的请求都将由一个单一的处理程序处理。该处理程序可以做认证/授权/记录日志,或者跟踪请求,然后把请求传给相应的处理程序。以下是这种设计模式的实体。

  • 前端控制器(Front Controller) - 处理应用程序所有类型请求的单个处理程序,应用程序可以是基于 web 的应用程序,也可以是基于桌面的应用程序。
  • 调度器(Dispatcher) - 前端控制器可能使用一个调度器对象来调度请求到相应的具体处理程序。
  • 视图(View) - 视图是为请求而创建的对象。

前端控制器+调度器=MVC中的Controller

0.1实现

我们将创建 FrontControllerDispatcher 分别当作前端控制器和调度器。HomeViewStudentView 表示各种为前端控制器接收到的请求而创建的视图。

FrontControllerPatternDemo,我们的演示类使用 FrontController 来演示前端控制器设计模式。

在这里插入图片描述

  • HomeView

    public class HomeView {
       public void show(){
          System.out.println("Displaying Home Page");
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • StudentView

    public class StudentView {
       public void show(){
          System.out.println("Displaying Student Page");
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • Dispatcher

    public class Dispatcher {
       private StudentView studentView;
       private HomeView homeView;
       public Dispatcher(){
          studentView = new StudentView();
          homeView = new HomeView();
       }
     
       public void dispatch(String request){
          if(request.equalsIgnoreCase("STUDENT")){
             studentView.show();
          }else{
             homeView.show();
          }  
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • FrontController

    public class FrontController {
       
       private Dispatcher dispatcher;
     
       public FrontController(){
          dispatcher = new Dispatcher();
       }
     
       private boolean isAuthenticUser(){
          System.out.println("User is authenticated successfully.");
          return true;
       }
     
       private void trackRequest(String request){
          System.out.println("Page requested: " + request);
       }
     
       public void dispatchRequest(String request){
          //记录每一个请求
          trackRequest(request);
          //对用户进行身份验证
          if(isAuthenticUser()){
             dispatcher.dispatch(request);
          }  
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
  • FrontControllerPatternDemo

    public class FrontControllerPatternDemo {
       public static void main(String[] args) {
          FrontController frontController = new FrontController();
          frontController.dispatchRequest("HOME");
          frontController.dispatchRequest("STUDENT");
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

1Servlet API

Servlet:(尤指 Java 语言中在服务器上运行的)小型应用程序;小服务程序

Servlet API 是用于开发基于 Java 的 Web 应用程序的标准 API(https://jakarta.ee/specifications/servlet/)。它提供了一组接口和类,用于处理 HTTP 请求和响应,并支持开发动态、可扩展的 Web 应用程序。下面是一些常用的 Servlet API 接口和类:

  1. javax.servlet.Servlet:所有 Servlet 类都必须实现的接口,定义了 servlet 的生命周期方法和服务方法。

  2. javax.servlet.http.HttpServlet:继承自 Servlet 接口的 HttpServlet 类,提供了用于处理 HTTP 请求的扩展方法,如 doGet()doPost() 等。

  3. javax.servlet.ServletConfig:表示 Servlet 的配置信息,包括初始化参数等。

  4. javax.servlet.ServletContext:表示 Servlet 的上下文,为 Servlet 提供共享的资源和信息。

  5. javax.servlet.http.HttpServletRequest:封装了客户端的 HTTP 请求信息,如请求 URI、请求参数、请求头等。

  6. javax.servlet.http.HttpServletResponse:封装了向客户端发送的 HTTP 响应信息,如响应状态码、响应头、输出流等。

  7. javax.servlet.http.HttpSession:用于在客户端和服务器之间跟踪会话状态,存储用户相关的数据。

  8. javax.servlet.RequestDispatcher:用于将请求转发给其他 Servlet 或 JSP 页面进行处理。

  9. javax.servlet.Filter:用于在请求到达 Servlet 之前或响应返回给客户端之前,对请求和响应进行预处理和后处理。

  10. javax.servlet.annotation.WebServlet:用于通过注解配置 Servlet。

以上仅是 Servlet API 中的一部分接口和类,Servlet API 还包含许多其他接口和类,用于处理会话管理、Cookie、文件上传下载、异步处理等。这些接口和类提供了构建 Web 应用程序所需的核心功能。

2DispatcherServlet

与许多其他web框架一样,Spring MVC是围绕前端控制器模式设计的,其中中心Servlet DispatcherServlet为请求处理提供共享算法,而实际工作由可配置的委托组件执行。这个模型是灵活的,并且支持不同的工作流。

DispatcherServlet和任何Servlet一样,需要通过使用Java配置或在web.xml中根据Servlet规范声明和映射。反过来,DispatcherServlet使用Spring配置来发现请求映射、视图解析、异常处理等所需的委托组件。

2.1自定义DispatcherServlet

下面的Java配置示例注册并初始化DispatcherServlet,它由Servlet容器自动检测:

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) {

        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(AppConfig.class);

        // Create and register the DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(context);
        ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

这段代码是一个典型的实现了 WebApplicationInitializer 接口的类。它用于配置和初始化一个基于 Spring Web 的应用程序。

onStartup 方法中,主要完成以下几个任务:

  1. 创建并注册 Spring Web 应用程序的配置类:通过创建一个 AnnotationConfigWebApplicationContext 对象,并注册应用程序的配置类 AppConfig.class,来告诉 Spring Web 如何配置和组装应用程序的相关组件。

  2. 创建并注册 DispatcherServlet:创建一个 DispatcherServlet 对象,将之前创建的 AnnotationConfigWebApplicationContext 对象作为参数传入。DispatcherServlet 是一个用于接收并处理所有请求的核心控制器。接着,利用 servletContext 对象的 addServlet 方法来注册 DispatcherServlet,指定了 Servlet 名称为 “app”,并设置其加载顺序为 1。

  3. 配置 DispatcherServlet 的 URL 映射规则:通过调用 registration 对象的 addMapping 方法,将 “/app/*” 这个 URL 模式映射到刚刚创建的 DispatcherServlet,这意味着所有以 “/app” 开头的请求都将由该 DispatcherServlet 来处理。

以上代码的作用是初始化和配置 Spring Web 应用程序。通过 WebApplicationInitializer 的实现类,我们可以自定义应用程序的启动行为,并按照需要进行各种配置操作,比如加载 Spring 配置、注册 Servlet、设置 URL 映射等。这种方式与传统的基于 web.xml 配置的方式相比,更加灵活且易于扩展。

在上面的代码中,AppConfig 是一个应用程序的配置类,用于配置和组装 Spring Web 应用程序的相关组件。具体来说,AppConfig 类通常会使用 @Configuration 注解进行标记,并且可能会使用其他注解来定义 Bean、拦截器、视图解析器等。

下面是一个示例的 AppConfig 类的实现,供参考:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example")
public class AppConfig implements WebMvcConfigurer {

    // 配置视图解析器
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }

    // 配置静态资源
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    // 配置拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor());
    }

    // 配置请求处理器
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/about").setViewName("about");
    }
    
    // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

在这个示例中,AppConfig 类使用了 @Configuration 注解来表明它是一个配置类。注解 @EnableWebMvc 启用了 Spring MVC 的自动配置,并同时允许我们进行一些自定义配置。

AppConfig 中,我们可以定义各种 Bean,比如视图解析器、拦截器等。示例中通过 viewResolver 方法定义了一个视图解析器,并设置了视图文件的前缀和后缀。configureDefaultServletHandling 方法用于配置处理静态资源的默认处理器。 addInterceptors 方法用于添加拦截器。addViewControllers 方法用于定义简单的请求处理器来处理特定的 URL 请求。

当然,实际开发中,根据项目需求,AppConfig 类的具体实现可能会有所不同。你可以根据项目的具体情况,定制 AppConfig 类的内容,包括定义其他的 Bean、配置数据源、事务等。

总之,AppConfig 类的作用是配置和组装 Spring Web 应用程序的相关组件,使得应用程序能够正确运行并提供所需的功能。

在 Spring Boot 项目中,通常不需要手动实现 WebApplicationInitializer 接口来配置 DispatcherServlet。这是因为 Spring Boot 已经自动处理了这些配置,并提供了默认的配置方式。

Spring Boot 通过自动配置的方式,以约定大于配置的原则来简化和加速 Spring 应用程序的开发。它会自动检测项目中的依赖和文件结构,并根据约定为应用程序提供默认的配置和行为。

在 Spring Boot 中,通过添加依赖 spring-boot-starter-web,框架会自动配置 DispatcherServlet,并根据约定将它映射到根路径 “/”。Spring Boot 会自动加载并初始化 DispatcherServlet,并自动配置一些默认的处理器、视图解析器、异常处理器等。

除了 DispatcherServlet 的自动配置,Spring Boot 还提供了其他许多自动配置的功能,如自动注册 Servlet、Filter、Listener,自动配置数据源、事务等。

如果需要自定义配置,Spring Boot 提供了各种方式来覆盖或扩展默认的配置。可以通过在应用程序的配置类中使用注解如 @EnableWebMvc@RequestMapping 来自定义 URL 映射规则和处理逻辑。也可以通过在 application.propertiesapplication.yml 文件中配置自定义的属性来覆盖默认的配置。

Spring MVC 支持定义多个 DispatcherServlet 实例。每个 DispatcherServlet 实例都有自己独立的上下文和请求处理流程。

通常情况下,我们可以在一个 Spring MVC 应用程序中定义多个 DispatcherServlet 实例的场景包括:

  1. 处理不同 URL 模式的请求:如果我们希望将不同 URL 模式的请求分发给不同的处理器,可以为每个 URL 模式配置一个独立的 DispatcherServlet 实例。这样可以根据请求的 URL 来选择不同的处理逻辑。

  2. 多个 Web 应用程序的聚合:在某些情况下,我们可能需要将多个独立的 Web 应用程序聚合在一个父项目中。为了实现这种场景,我们可以为每个子应用程序配置一个独立的 DispatcherServlet 实例,以便独立处理每个子应用程序的请求。

  3. 使用不同的视图技术:如果我们需要在同一个应用程序中同时使用不同的视图技术(如 JSP、Thymeleaf、FreeMarker 等),可以为每种视图技术配置一个独立的 DispatcherServlet 实例。这样可以根据请求的视图技术选择相应的视图解析器和模板引擎。

需要注意的是,每个 DispatcherServlet 实例都需要在应用程序中具有唯一的名称,并配置独立的映射路径。可以通过在 web.xml 中进行配置,或者在 Spring Boot 中,通过在应用程序配置类中使用 @ServletComponentScan 注解来自定义 DispatcherServlet 实例。

总之,Spring MVC 提供了灵活的配置选项,允许我们在一个应用程序中定义多个独立的 DispatcherServlet 实例,以满足不同的需求和场景。

在 Spring Boot 的 Web 项目中,默认情况下会有两个 DispatcherServlet 实例。

  1. DispatcherServlet 实例:默认映射路径为 “/*”,用于处理 Web 请求。这个实例是通过 @SpringBootApplication 注解自动创建的。它会加载应用程序的所有配置,并处理来自客户端的 Web 请求。

  2. 管理 DispatcherServlet 实例:默认映射路径为 “/actuator”,用于处理 Spring Boot 的管理端点请求。这个实例是由 Spring Boot 自动添加的,并提供了一系列的管理端点,用于监控和管理应用程序的运行状态。例如,可以通过 “/actuator/health” 端点来获取应用程序的健康状态。

这两个 DispatcherServlet 实例具有不同的映射路径,用于区分不同类型的请求。主 DispatcherServlet 处理一般的 Web 请求,而管理 DispatcherServlet 处理管理端点的请求。

需要注意的是,虽然默认情况下只有两个 DispatcherServlet 实例,但我们也可以自定义添加更多的 DispatcherServlet 实例,以适应特定的业务需求。

总结起来,在 Spring Boot 的 Web 项目中,默认情况下会有两个 DispatcherServlet 实例,一个用于处理一般的 Web 请求,另一个用于处理管理端点的请求。

2.2上下文层次结构

DispatcherServlet需要一个WebApplicationContext(一个普通ApplicationContext的扩展)用于它自己的配置。WebApplicationContext有一个链接到ServletContext和它所关联的Servlet。它还绑定到ServletContext,这样应用程序就可以在需要访问WebApplicationContext时使用RequestContextUtils上的静态方法来查找它。

对于许多应用程序来说,只有一个单例WebApplicationContext就足够了。也可能有一种上下文层次结构,根WebApplicationContext在多个DispatcherServlet(或其他Servlet)实例之间共享,每个实例都有自己的子WebApplicationContext配置。

根WebApplicationContext通常包含基础设施bean,例如需要跨多个Servlet实例共享的数据存储库和业务服务。这些bean被有效地继承,并且可以在特定于Servlet的子WebApplicationContext中被重写(即重新声明),该子WebApplicationContext通常包含给定Servlet的本地bean。下图显示了这种关系:

在这里插入图片描述

结合2.1中的DispatcherServlet配置代码再看上面这张图,DispatcherServlet需要包含一个WebApplicationContext(AnnotationConfigWebApplicationContext),并定义Controllers、ViewResolver、HandlerMapping(AppConfig.class),这些实例组成DispatcherServlet上下文层次结构。

姑且这么理解吧|ू•ૅω•́)ᵎᵎᵎ

2.3特殊Bean类型

DispatcherServlet委托特殊的bean来处理请求并呈现适当的响应。所谓“特殊bean”,我们指的是实现框架契约的spring管理对象实例。它们通常带有内置契约,但您可以自定义它们的属性并扩展或替换它们。

下表列出了DispatcherServlet检测到的特殊bean:

Bean typeExplanation
HandlerMapping将一个请求映射到一个处理程序以及一个[拦截器]列表(https://docs.spring.io/spring-framework/docs/5.3.30-SNAPSHOT/reference/html/web.html#mvc-handlermapping-interceptor),用于预处理和后处理。映射基于一些标准,其细节因’ HandlerMapping '实现而异。两个主要的“HandlerMapping”实现是“RequestMappingHandlerMapping”(它支持“@RequestMapping”注释方法)和“SimpleUrlHandlerMapping”(它维护对处理程序的URI路径模式的显式注册)。
HandlerAdapter帮助’ DispatcherServlet ‘调用映射到请求的处理程序,而不管处理程序实际上是如何调用的。例如,调用带注释的控制器需要解析注释。’ HandlerAdapter ‘的主要目的是保护’ DispatcherServlet '不受这些细节的影响。
HandlerExceptionResolver解决异常的策略,可能将它们映射到处理程序、HTML错误视图或其他目标。看到(异常)(https://docs.spring.io/spring-framework/docs/5.3.30-SNAPSHOT/reference/html/web.html mvc-exceptionhandlers)。
ViewResolver解析从处理程序返回的逻辑的基于’ String ‘的视图名称到一个实际的’ view ',用它来呈现给响应。参见View ResolutionView Technologies
LocaleResolver, LocaleContextResolver解析客户端正在使用的“区域设置”以及可能的时区,以便能够提供国际化视图。看到(地区)(https://docs.spring.io/spring-framework/docs/5.3.30-SNAPSHOT/reference/html/web.html mvc-localeresolver)。
ThemeResolver解决您的web应用程序可以使用的主题-例如,提供个性化的布局。看到(主题)(https://docs.spring.io/spring-framework/docs/5.3.30-SNAPSHOT/reference/html/web.html mvc-themeresolver)。
MultipartResolver抽象用于在一些多部分解析库的帮助下解析多部分请求(例如,浏览器表单文件上传)。参见多部分解析器
FlashMapManager存储和检索“输入”和“输出”“FlashMap”,可以用来将属性从一个请求传递到另一个请求,通常是通过重定向。参见Flash属性

表格中的这些类或者接口都是DispatcherServlet的成员属性,了解成员属性作用其实也就知道了DispatcherServlet的作用。

2.4自定义DispatcherServlet补充

两部分:

  1. Web MVC Config:定义2.3中的特殊Bean,如果没有匹配的bean类型,它会返回到DispatcherServlet.properties中列出的默认类型。
  2. Servlet Config:参考2.1。

2.5处理过程

在这里插入图片描述

2.6 路径匹配

在 Spring MVC 5.3 之前的版本中,对于解码 requestURI(请求的 URI)面临着以下问题:

  1. URL 编码问题:当浏览器发送带有特殊字符或非 ASCII 字符的请求时,这些字符会被浏览器自动进行 URL 编码,即将特殊字符转换成 % 后面加上字符的 ASCII 表示形式。例如,空格会被编码为 %20,中文字符会被编码为 %E4%BD%A0 等。而在旧版本的 Spring MVC 中,requestURI 默认是保持编码状态的,即不对 URL 进行解码处理。

  2. 中文乱码问题:由于 requestURI 是在浏览器编码之后传递给服务器的,服务器默认会将 % 后面的 ASCII 表示还原成原来的字符,但是对于中文字符,旧版本的 Spring MVC 会存在中文乱码的问题。比如 %E4%BD%A0 会被解码成乱码字符而不是正确的中文字符。

这些问题可能导致应用程序无法正确处理带有特殊字符或非 ASCII 字符的请求,导致出现乱码或参数解析错误等问题。

PathPattern和AntPathMatcher是两种用于匹配路径模式的工具。

  1. AntPathMatcher:
    AntPathMatcher是Spring框架中提供的路径匹配器之一,它基于Ant风格的路径模式匹配。Ant风格的路径模式使用通配符来表示路径中的某些部分,如?表示匹配任意单个字符,*表示匹配任意数量的字符(包括0个字符),**表示匹配任意数量的路径(包括0个路径段)。AntPathMatcher可以用于匹配文件路径、URL路径等。

例如,使用AntPathMatcher可以进行如下的路径匹配:

  • /user/? 可以匹配 /user/1,但不能匹配 /user/1/friends
  • /user/* 可以匹配 /user/1/user/1/friends
  • /user/** 可以匹配 /user/1/user/1/friends
  1. PathPattern:

PathPattern 是 Spring Framework 5.3 引入的一个类,用于解决 URL 路径模式匹配的问题。在 Web 应用开发中,对于输入的 URL 路径进行匹配和解析是常见的需求。PathPattern 提供了一种更灵活和强大的方式来处理 URL 路径模式。

PathPattern 解决了以下问题:

  1. URL 路径的灵活匹配:传统的 URL 路径匹配基于字符串匹配,无法处理复杂的路径模式匹配需求。PathPattern 支持使用路径变量、通配符、正则表达式等来构建灵活的 URL 路径匹配模式。例如,可以通过 /{category}/{id} 来匹配 /books/123/movies/456 等路径,并将 {category}{id} 作为变量进行提取。

  2. 路径参数的提取和解析:PathPattern 可以从匹配的 URL 路径中提取出路径参数,并将其转换为对应的数据类型。路径参数可以用于动态地传递数据,例如 /books/123 中的 123 可以作为路径参数进行提取和解析。

  3. RESTful 路由和资源映射:PathPattern 可以与 Spring Web MVC 或 Spring WebFlux 集成,用于进行 RESTful 路由和资源映射。通过定义和匹配路径模式,可以更方便地映射请求到相应的处理器方法,并且支持多种灵活的路径匹配模式。

  4. 安全路径匹配:PathPattern 通过对 URL 路径进行规范化和标准化处理,可以提供更安全的路径匹配。它可以处理 URL 编码、解码和规范化,避免在处理路径匹配时引入潜在的安全漏洞。

与AntPathMatcher相比,PathPattern提供了更丰富和灵活的路径匹配功能。PathPattern对路径进行了更严格的验证,并且支持更多的模式匹配规则,包括路径段的匹配、路径参数的匹配、查询参数的匹配等。

例如,使用PathPattern可以进行如下的路径匹配:

  • /user/{id} 可以匹配 /user/1,但不能匹配 /user/1/friends
  • /user/{id}/** 可以匹配 /user/1/user/1/friends
  • /user/{id:\\d+} 可以匹配 /user/1,但不能匹配 /user/abc

如果你在项目用采用了Spring Boot 2.6.x,那么它的路径匹配格式为PathPattern,如果你又要集成Swagger,那么注意了启动会报错,需要修改Spring MVC的路径匹配规则为Ant风格:

spring.mvc.pathmatch.matching-strategy=ant_path_matcher
  • 1

2.7视图解析

Spring MVC定义了ViewResolver和View接口,使您可以在浏览器中呈现模型,而无需将您绑定到特定的视图技术。ViewResolver提供了视图名称和实际视图之间的映射。视图处理数据在移交给特定视图技术之前的准备工作。

下表提供了ViewResolver层次结构的更多细节:

ViewResolverDescription
AbstractCachingViewResolver’ AbstractCachingViewResolver ‘的子类缓存它们解析的视图实例。缓存提高了某些视图技术的性能。你可以通过设置’ cache ‘属性为’ false ‘来关闭缓存。此外,如果您必须在运行时刷新某个视图(例如,当修改FreeMarker模板时),您可以使用’ removeFromCache(String viewName, Locale loc) '方法。
UrlBasedViewResolverViewResolver接口的简单实现,可以直接将逻辑视图名称解析为url,而不需要显式的映射定义。如果您的逻辑名称以一种直接的方式匹配视图资源的名称,而不需要任意映射,那么这是合适的。
InternalResourceViewResolver方便的子类’ UrlBasedViewResolver ‘,支持’ InternalResourceView ‘(实际上,servlet和jsp)和子类,如’ JstlView ‘和’ TilesView ‘。你可以使用’ setViewClass(…) '为这个解析器生成的所有视图指定视图类。详细信息请参见’ UrlBasedViewResolver ’ javadoc。
FreeMarkerViewResolver方便的UrlBasedViewResolver子类,支持FreeMarkerView和自定义子类。
ContentNegotiatingViewResolver’ ViewResolver ‘接口的实现,该接口基于请求文件名或’ Accept '头解析视图。参见【内容协商】(https://docs.spring.io/spring-framework/docs/5.3.30-SNAPSHOT/reference/html/web.html#mvc-multiple-representations)。
`BeanNameViewResolver’ ViewResolver '接口的实现,该接口在当前应用程序上下文中将视图名称解释为bean名称。这是一个非常灵活的变体,允许基于不同的视图名称混合和匹配不同的视图类型。每个这样的“视图”都可以定义为一个bean,例如在XML或配置类中。

2.8Multipart Resolver

MultipartResolver是一种用于解析multipart 请求(包括文件上传)的策略。有一种实现基于Commons FileUpload,另一种实现基于Servlet 3.0多部分请求解析。

Commons FileUpload传统上只适用于POST请求,但接受任何multipart/内容类型。有关详细信息和配置选项,请参阅commonsmmultipartresolver javadoc。

Servlet 3.0多部分解析需要通过Servlet容器配置来启用。默认情况下,它将尝试用任何HTTP方法解析任何multipart/内容类型,但这可能不支持所有Servlet容器。有关详细信息和配置选项,请参阅StandardServletMultipartResolver javadoc。

3带注释的控制器Handler

Spring MVC提供了一个基于注解的编程模型,其中@Controller和@RestController组件使用注解来表达请求映射、请求输入、异常处理等等。带注释的控制器具有灵活的方法签名,不必扩展基类,也不必实现特定的接口。下面的例子展示了一个由注解定义的控制器:

@Controller
public class HelloController {

    @GetMapping("/hello")
    public String handle(Model model) {
        model.addAttribute("message", "Hello World!");
        return "index";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在前面的例子中,这个方法接受一个Model并返回一个字符串形式的视图名,但是还有很多其他的选项,我们将在本章后面解释。

@RestController是一个复合注释,它本身带有@Controller和@ResponseBody的元注释,以表明控制器的每个方法都继承了类型级的@ResponseBody注释,因此,直接写入响应体,而不是使用HTML模板进行视图解析和呈现。所以我们在Spring Boot项目中,在定义页面路由时,WebController应该采用@Controller注解而不是@RestController。

3.1Request Mapping

你可以使用@RequestMapping注释将请求映射到控制器方法。它有各种属性,可以根据URL、HTTP方法、请求参数、标头和媒体类型进行匹配。您可以在类级别使用它来表示共享映射,或者在方法级别使用它来缩小到特定的端点映射。

也有HTTP方法特定的快捷方式变体@RequestMapping:

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping

快捷方式是提供自定义注释,因为大多数控制器方法应该映射到特定的HTTP方法,而不是使用@RequestMapping,默认情况下,它匹配所有HTTP方法。在类级别上仍然需要一个@RequestMapping来表达共享映射。

下面的例子有类型和方法级别的映射:

@RestController
@RequestMapping("/persons")
class PersonController {

    @GetMapping("/{id}")
    public Person getPerson(@PathVariable Long id) {
        // ...
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public void add(@RequestBody Person person) {
        // ...
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

3.2方法参数

Controller method argumentDescription
WebRequest, NativeWebRequestGeneric access to request parameters and request and session attributes, without direct use of the Servlet API.
javax.servlet.ServletRequest, javax.servlet.ServletResponseChoose any specific request or response type — for example, ServletRequest, HttpServletRequest, or Spring’s MultipartRequest, MultipartHttpServletRequest.
javax.servlet.http.HttpSessionEnforces the presence of a session. As a consequence, such an argument is never null. Note that session access is not thread-safe. Consider setting the RequestMappingHandlerAdapter instance’s synchronizeOnSession flag to true if multiple requests are allowed to concurrently access a session.
javax.servlet.http.PushBuilderServlet 4.0 push builder API for programmatic HTTP/2 resource pushes. Note that, per the Servlet specification, the injected PushBuilder instance can be null if the client does not support that HTTP/2 feature.
java.security.PrincipalCurrently authenticated user — possibly a specific Principal implementation class if known.Note that this argument is not resolved eagerly, if it is annotated in order to allow a custom resolver to resolve it before falling back on default resolution via HttpServletRequest#getUserPrincipal. For example, the Spring Security Authentication implements Principal and would be injected as such via HttpServletRequest#getUserPrincipal, unless it is also annotated with @AuthenticationPrincipal in which case it is resolved by a custom Spring Security resolver through Authentication#getPrincipal.
HttpMethodThe HTTP method of the request.
java.util.LocaleThe current request locale, determined by the most specific LocaleResolver available (in effect, the configured LocaleResolver or LocaleContextResolver).
java.util.TimeZone + java.time.ZoneIdThe time zone associated with the current request, as determined by a LocaleContextResolver.
java.io.InputStream, java.io.ReaderFor access to the raw request body as exposed by the Servlet API.
java.io.OutputStream, java.io.WriterFor access to the raw response body as exposed by the Servlet API.
@PathVariableFor access to URI template variables. See URI patterns.
@MatrixVariableFor access to name-value pairs in URI path segments. See Matrix Variables.
@RequestParamFor access to the Servlet request parameters, including multipart files. Parameter values are converted to the declared method argument type. See @RequestParam as well as Multipart.Note that use of @RequestParam is optional for simple parameter values. See “Any other argument”, at the end of this table.
@RequestHeaderFor access to request headers. Header values are converted to the declared method argument type. See @RequestHeader.
@CookieValueFor access to cookies. Cookies values are converted to the declared method argument type. See @CookieValue.
@RequestBodyFor access to the HTTP request body. Body content is converted to the declared method argument type by using HttpMessageConverter implementations. See @RequestBody.
HttpEntity<B>For access to request headers and body. The body is converted with an HttpMessageConverter. See HttpEntity.
@RequestPartFor access to a part in a multipart/form-data request, converting the part’s body with an HttpMessageConverter. See Multipart.
java.util.Map, org.springframework.ui.Model, org.springframework.ui.ModelMapFor access to the model that is used in HTML controllers and exposed to templates as part of view rendering.
RedirectAttributesSpecify attributes to use in case of a redirect (that is, to be appended to the query string) and flash attributes to be stored temporarily until the request after redirect. See Redirect Attributes and Flash Attributes.
@ModelAttributeFor access to an existing attribute in the model (instantiated if not present) with data binding and validation applied. See @ModelAttribute as well as Model and DataBinder.Note that use of @ModelAttribute is optional (for example, to set its attributes). See “Any other argument” at the end of this table.
Errors, BindingResultFor access to errors from validation and data binding for a command object (that is, a @ModelAttribute argument) or errors from the validation of a @RequestBody or @RequestPart arguments. You must declare an Errors, or BindingResult argument immediately after the validated method argument.
SessionStatus + class-level @SessionAttributesFor marking form processing complete, which triggers cleanup of session attributes declared through a class-level @SessionAttributes annotation. See @SessionAttributes for more details.
UriComponentsBuilderFor preparing a URL relative to the current request’s host, port, scheme, context path, and the literal part of the servlet mapping. See URI Links.
@SessionAttributeFor access to any session attribute, in contrast to model attributes stored in the session as a result of a class-level @SessionAttributes declaration. See @SessionAttribute for more details.
@RequestAttributeFor access to request attributes. See @RequestAttribute for more details.
Any other argumentIf a method argument is not matched to any of the earlier values in this table and it is a simple type (as determined by BeanUtils#isSimpleProperty), it is resolved as a @RequestParam. Otherwise, it is resolved as a @ModelAttribute.

上述表格中为@Controller中方法的参数和参数注解,具体含义不翻译了可自行百度。有几个常见的、重要的还是有必要去了解的:javax.servlet.ServletRequest, javax.servlet.ServletResponsejavax.servlet.http.HttpSession@RequestBody,@PathVariable,@RequestParam

3.3返回值

Controller method return valueDescription
@ResponseBodyThe return value is converted through HttpMessageConverter implementations and written to the response. See @ResponseBody.
HttpEntity<B>, ResponseEntity<B>The return value that specifies the full response (including HTTP headers and body) is to be converted through HttpMessageConverter implementations and written to the response. See ResponseEntity.
HttpHeadersFor returning a response with headers and no body.
StringA view name to be resolved with ViewResolver implementations and used together with the implicit model — determined through command objects and @ModelAttribute methods. The handler method can also programmatically enrich the model by declaring a Model argument (see Explicit Registrations).
ViewA View instance to use for rendering together with the implicit model — determined through command objects and @ModelAttribute methods. The handler method can also programmatically enrich the model by declaring a Model argument (see Explicit Registrations).
java.util.Map, org.springframework.ui.ModelAttributes to be added to the implicit model, with the view name implicitly determined through a RequestToViewNameTranslator.
@ModelAttributeAn attribute to be added to the model, with the view name implicitly determined through a RequestToViewNameTranslator.Note that @ModelAttribute is optional. See “Any other return value” at the end of this table.
ModelAndView objectThe view and model attributes to use and, optionally, a response status.
voidA method with a void return type (or null return value) is considered to have fully handled the response if it also has a ServletResponse, an OutputStream argument, or an @ResponseStatus annotation. The same is also true if the controller has made a positive ETag or lastModified timestamp check (see Controllers for details).If none of the above is true, a void return type can also indicate “no response body” for REST controllers or a default view name selection for HTML controllers.
DeferredResult<V>Produce any of the preceding return values asynchronously from any thread — for example, as a result of some event or callback. See Asynchronous Requests and DeferredResult.
Callable<V>Produce any of the above return values asynchronously in a Spring MVC-managed thread. See Asynchronous Requests and Callable.
ListenableFuture<V>, java.util.concurrent.CompletionStage<V>, java.util.concurrent.CompletableFuture<V>Alternative to DeferredResult, as a convenience (for example, when an underlying service returns one of those).
ResponseBodyEmitter, SseEmitterEmit a stream of objects asynchronously to be written to the response with HttpMessageConverter implementations. Also supported as the body of a ResponseEntity. See Asynchronous Requests and HTTP Streaming.
StreamingResponseBodyWrite to the response OutputStream asynchronously. Also supported as the body of a ResponseEntity. See Asynchronous Requests and HTTP Streaming.
Reactor and other reactive types registered via ReactiveAdapterRegistryA single value type, e.g. Mono, is comparable to returning DeferredResult. A multi-value type, e.g. Flux, may be treated as a stream depending on the requested media type, e.g. “text/event-stream”, “application/json+stream”, or otherwise is collected to a List and rendered as a single value. See Asynchronous Requests and Reactive Types.
Other return valuesIf a return value remains unresolved in any other way, it is treated as a model attribute, unless it is a simple type as determined by BeanUtils#isSimpleProperty, in which case it remains unresolved.

3.4Jackson

Jackson是一个Java库,用于处理JSON格式的数据。它提供了一组强大的功能,可以在Java对象和JSON之间进行转换,包括序列化(将Java对象转换为JSON字符串)和反序列化(将JSON字符串转换为Java对象)。

以下是Jackson的主要作用:

  1. 对象序列化和反序列化:Jackson可以将Java对象转换为JSON字符串,以及将JSON字符串转换为Java对象。这对于在Java应用程序和前端或其他服务之间传递数据时非常有用。

  2. 数据绑定:Jackson可以将JSON数据绑定到Java对象上,并将JSON数据中的属性值映射到Java对象的相应属性上。这使得在处理JSON数据时可以轻松地将其转换为Java对象,并进行操作。

  3. 数据格式化和解析:Jackson提供了灵活的方式来格式化(美化)JSON数据并解析复杂的JSON结构。它支持各种JSON格式,如数组、嵌套对象、键值对等。

  4. 支持注解:Jackson支持在Java对象中使用注解,以控制JSON序列化和反序列化的行为。通过注解,可以自定义属性的命名、忽略某些属性、处理日期格式等。

  5. 支持流式API:Jackson提供了流式的API,可以逐行读取JSON数据,并进行处理,而不需要将整个JSON字符串加载到内存中。这对于处理大型JSON数据或实时流式数据非常有用。

Spring框架提供了对Jackson JSON库的全面支持,包括以下几个方面的应用:

  1. JSON序列化和反序列化:Spring使用Jackson库作为默认的JSON序列化和反序列化工具。在处理HTTP请求和响应时,Spring MVC框架会自动将Java对象转换为JSON格式的响应数据,并将接收到的JSON数据转换为Java对象。这使得开发人员可以在控制器方法中直接使用Java对象,而不需要手动处理JSON数据的转换。

  2. HTTP消息转换器:Spring提供了一组HTTP消息转换器,其中包括使用Jackson库进行JSON格式的消息转换器。这些转换器允许开发人员在处理HTTP请求和响应时轻松地将Java对象与JSON数据之间进行转换。

  3. 注解支持:Spring提供了对Jackson库注解的支持,包括@JsonSerialize@JsonDeserialize@JsonProperty等。通过这些注解,开发人员可以自定义Java对象的JSON序列化和反序列化规则,包括属性的命名、类型转换、日期格式化等。

  4. 配置选项:Spring允许开发人员对Jackson库的配置进行细粒度的控制。例如,可以配置Jackson的特性,如是否缩进JSON输出、是否忽略空值属性等。通过配置选项,开发人员可以根据具体需求来调整JSON的序列化和反序列化行为。

  5. 支持其他JSON库:尽管Spring默认使用Jackson库进行JSON处理,但它也支持其他JSON库,如Gson、FastJson等。开发人员可以根据自己的喜好和项目需求选择合适的JSON库进行集成,并在Spring中进行配置。

总之,通过Spring对Jackson JSON库的支持,开发人员可以更加方便地处理JSON数据,实现Java对象和JSON数据之间的转换。这为开发RESTful API、处理前端与后端数据交互等场景提供了便利,并提升了开发效率和代码的可读性。

4异步请求

Spring MVC与Servlet 3.0异步请求处理有广泛的集成:

控制器方法中的DeferredResult和Callable返回值为单个异步返回值提供了基本支持。

控制器可以流式传输多个值,包括SSE和原始数据。

4.1DeferredResult

一旦在Servlet容器中启用了异步请求处理特性,控制器方法就可以用DeferredResult包装任何支持的控制器方法返回值,如下例所示:

@GetMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
    DeferredResult<String> deferredResult = new DeferredResult<String>();
    // Save the deferredResult somewhere..
    return deferredResult;
}

// From some other thread...
deferredResult.setResult(result);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

控制器可以从不同的线程异步地生成返回值——例如,响应外部事件(JMS消息)、计划任务或其他事件。

4.2Callable

控制器可以用java.util.concurrent包装任何支持的返回值。可调用,如下例所示:

@PostMapping
public Callable<String> processUpload(final MultipartFile file) {

    return new Callable<String>() {
        public String call() throws Exception {
            // ...
            return "someView";
        }
    };
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

然后可以通过配置的TaskExecutor运行给定的任务来获得返回值。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/586162
推荐阅读
相关标签
  

闽ICP备14008679号