springMVC【java全端课33】

86 阅读17分钟

SpringMVC异常处理器

1 如何处理异常

  • 编程式异常处理:将异常处理代码与核心业务代码书写在同一代码块中,耦合度高(不推荐)
  • 声明式异常处理:将异常处理代码,先横向提取到切面类中,再动态织入到核心业务代码(推荐使用)

2 声明式异常处理

声明式异常处理是 Spring MVC 中一种强大且灵活的技术,它允许开发者通过注解和配置轻松地定义异常处理规则,而无需在业务逻辑中嵌入复杂的 try-catch 结构。SpringMVC提供很多注解实现异常处理器,其中常用注解如下:

@ControllerAdvice 注解用于定义一个类作为全局异常处理器,它可以应用于整个应用程序中的所有控制器。

@ExceptionHandler 注解,可以在该类中定义方法来处理特定类型的异常。

2.1 全局异常处理

这是最常用的一种声明式异常处理方法,它允许你定义一个或多个类来集中处理所有控制器抛出的异常。

package com.mytest.myexceptionhandler;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

//@ResponseBody
//@ControllerAdvice       //定义异常处理器
//@RestControllerAdvice   // = @ControllerAdvice+@ResponseBody
@ControllerAdvice
public class MyExceptionHandler {

    //处理空指针异常
    @ResponseBody
    @ExceptionHandler(NullPointerException.class)
    public String doNullPointerException(Exception ex) {
        System.out.println("ex = " + ex);
        return "{'status:'501'}";		//响应文本提示:错误信息
    }

    //处理算术异常
    @ExceptionHandler(ArithmeticException.class)
    public String doArithmeticException(Exception ex) {
        System.out.println("ex = " + ex);
        return "/error/error_502.html";	//响应页面提示:错误信息
    }

}

2.2 局部异常处理(了解)

除了全局异常处理器外,你还可以在每个控制器内部使用 @ExceptionHandler 来处理特定于该控制器的异常。这种方式提供了更细粒度的控制,但不如全局处理器灵活。

@Controller
public class MyController {

    @ExceptionHandler(ResourceNotFoundException.class)
    public String handleResourceNotFoundException(ResourceNotFoundException ex, Model model) {
        model.addAttribute("errorMessage", ex.getMessage());
        return "error/resource-not-found"; // 返回视图名称
    }

    // 控制器其他方法...
}

SpringMVC拦截器

过滤器与拦截器相同与不同

  • 相似点
    • 拦截:必须先把请求拦住,才能执行后续操作
    • 过滤:拦截器或过滤器存在的意义就是对请求进行统一处理
    • 放行:对请求执行了必要操作后,放请求过去,让它访问原本想要访问的资源
  • 不同点
    • 工作平台不同
      • 过滤器工作在 Servlet 容器中
      • 拦截器工作在 SpringMVC 的基础上
    • 拦截的范围
      • 过滤器:能够拦截到的最大范围是整个 Web 应用
      • 拦截器:能够拦截到的最大范围是整个 SpringMVC 负责的请求
    • IOC 容器支持
      • 过滤器:想得到 IOC 容器需要调用专门的工具方法,是间接的
      • 拦截器:它自己就在 IOC 容器中,所以可以直接从 IOC 容器中装配组件,也就是可以直接得到 IOC 容器的支持

1 拦截器(Interceptor)简介

在 Spring MVC 中,Interceptor(拦截器)是一种用于在请求处理的不同阶段插入自定义逻辑的机制。拦截器可以用来实现诸如日志记录、权限检查、性能监控、国际化设置等功能,而无需修改业务逻辑代码。下面是对 Interceptor 的详细解析:

拦截器允许开发者在以下三个关键点上执行额外的逻辑:

  1. 预处理请求(Pre-Handling)
    • 在请求被传递给控制器之前执行。
    • 可以用于验证用户身份、解析请求参数、设置线程局部变量等。
  2. 后处理响应(Post-Handling)
    • 在控制器方法执行完毕但视图渲染之前执行。
    • 可以用于添加额外的数据到模型中,或者修改返回的 ModelAndView 对象。
  3. 完成处理(After Completion)
    • 在整个请求处理完成后执行,无论是否发生异常。
    • 通常用于资源清理、记录日志等。

2 拦截器基本实现

2.1 定义拦截器

package com.mytest.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

@Component("myInterceptor1")
public class MyInterceptor1 implements HandlerInterceptor {

    //在执行Controller之前执行
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("==>1.Myinterceprot111->preHandle()!!!");
        return true;   //true:放行   false:不放行
    }

    //在执行Controller之后执行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,  ModelAndView modelAndView) throws Exception {
        System.out.println("==>3.Myinterceprot111->postHandle()!!!");
    }

    //在最后执行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("==>4.Myinterceprot111->afterCompletion()!!!");
    }

}

2.2 注册拦截器

package com.mytest.config;

import com.mytest.interceptor.MyInterceptor1;
import com.mytest.interceptor.MyInterceptor2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class SpringMVCConfig implements WebMvcConfigurer {

    @Autowired
    private MyInterceptor1 myInterceptor1;

    //注册拦截器
    public void addInterceptors(InterceptorRegistry registry) {
        //注册拦截器(myInterceptor1)
  registry.addInterceptor(myInterceptor1).addPathPatterns("/interceptorController/testInterceptor");
    }
}

3 拦截器工作原理

3.1 单个拦截器工作原理

  • 客户端向服务器发送请求
  • 执行拦截器第一个方法preHandle(),放行请求(return true)
  • 执行Controller中相应方法
  • 执行拦截器第二个方法postHandle()
  • 执行拦截器第三个方法afterCompletion()

3.2 多个拦截器工作原理

假设我们有三个拦截器 A、B 和 C,并且它们按此顺序被添加到拦截器链中。那么,在处理请求的过程中,各个拦截器的方法将按照以下顺序执行:

  1. Pre-Handle 阶段

  • A.preHandle()
  • B.preHandle()
  • C.preHandle()

在这个阶段,拦截器的 preHandle() 方法会按照它们被注册的顺序依次调用。每个拦截器都有机会决定是否继续处理请求。如果任何一个拦截器的 preHandle() 返回 false,则整个请求处理过程将会终止,后续的拦截器和控制器方法都不会被执行。

  1. Controller 处理

如果所有拦截器的 preHandle() 方法都返回 true,请求将继续传递给控制器进行处理。控制器执行完毕后,会生成一个 ModelAndView 对象(或类似的响应结果)。

  1. Post-Handle 阶段

  • C.postHandle()
  • B.postHandle()
  • A.postHandle()

一旦控制器完成了业务逻辑处理,但视图还未渲染之前,拦截器链中的 postHandle() 方法将以逆序执行。也就是说,最后一个注册的拦截器 C 的 postHandle() 方法最先被调用,然后是 B 和 A。这个阶段可以用来修改 ModelAndView 或者添加额外的数据。

  1. After-Completion 阶段

  • C.afterCompletion()
  • B.afterCompletion()
  • A.afterCompletion()

最后,在整个请求处理完成后(包括视图渲染),无论是否发生异常,拦截器链中的 afterCompletion() 方法也会以逆序执行。这一步骤通常用于清理资源、记录日志等操作。

总结:假设这三个拦截器按 A -> B -> C 的顺序注册,当一个请求到达时,控制台输出如下:

A - Pre Handle
B - Pre Handle
C - Pre Handle
// Controller 执行...
C - Post Handle
B - Post Handle
A - Post Handle
// 视图渲染完成...
C - After Completion
B - After Completion
A - After Completion
注意事项
  • 短路行为:如果某个拦截器的 preHandle() 方法返回 false,则该请求将不会继续传递给下一个拦截器或控制器,而是直接进入 afterCompletion() 阶段。
  • 异常处理:如果在 preHandle() 或控制器执行期间抛出了异常,postHandle() 不会被调用,但是 afterCompletion() 仍然会被调用,并且异常信息会作为参数传递给它。
  • 线程安全性:确保拦截器中的共享资源是线程安全的,尤其是在多线程环境下使用时。

3.3 源码解析拦截器工作原理

  • SpringMVC断点入口

  • preHandle()正序执行

  • postHandle()倒序执行

  • afterCompletion()倒序执行

4 拦截器与过滤器异同

特性拦截器(Interceptor)过滤器(Filter)
作用范围仅限于 Spring MVC 控制器请求,不处理静态资源或其他非 Spring MVC 请求。可应用于整个 Web 应用程序中的所有请求/响应,包括静态资源。
生命周期生命周期依赖于 Spring 容器,在应用启动时被创建并注册到 Spring MVC 配置中。在整个应用程序的生命周期中始终存在,随应用启动而初始化。
配置方式通过 Java 配置类(实现 WebMvcConfigurer 接口)或 XML 配置文件来定义。通过 web.xml 文件或者使用注解(如 @WebFilterServletContainerInitializer)来定义。
执行顺序在请求已经被 DispatcherServlet 接收后但在传递给具体处理器之前执行;preHandle() 按注册顺序,postHandle()afterCompletion() 逆序。在请求到达 Spring MVC 的 DispatcherServlet 之前执行;过滤器链按配置顺序依次执行。
功能特性提供了更细粒度的控制,可以在请求处理的不同阶段插入自定义逻辑,如认证授权、性能监控等。支持字符编码设置、日志记录、权限检查等功能,可以直接操作 HttpServletRequestHttpServletResponse 对象。
性能考虑由于只针对 Spring MVC 请求,可能具有更好的性能表现,因为它减少了不必要的处理步骤。因为会拦截所有请求,处理大量请求时可能会引入额外开销,特别是对静态资源的处理。
适用场景适合处理由 Spring MVC 控制器管理的请求,并利用框架提供的丰富特性。适合需要对所有 HTTP 请求(包括静态资源)进行统一处理的情况。

SpringMVC数据校验

1 SpringMVC数据校验概念

Spring MVC 提供了强大的数据校验功能,以确保从客户端接收到的数据符合预期的格式和规则。通过使用 Bean Validation API(如 Hibernate Validator),你可以轻松地为输入数据添加校验逻辑,并在控制器层中处理验证结果。

  • 常用校验规则如下:
校验注解作用
@AssertFalse验证Boolean类型字段是否为false
@AssertTrue验证Boolean类型字段是否为true
@DecimalMax验证字符串表示的数字是否小于等于指定的最大值
@DecimalMin验证字符串表示的数字是否大于等于指定的最小值
@Digits(integer, fraction)验证数值是否符合指定的格式,integer指定整数精度,fraction指定小数精度
@Email验证字符串是否为邮箱地址格式
@Future验证日期是否在当前时间之后
@Past验证日期是否在当前时间之前
@Min(value)验证数字是否大于等于指定的最小值
@Max(value)验证数字是否小于等于指定的最大值
@Null验证对象是否为null
@NotNull验证对象是否不为null, 与@Null相反**(a!=null)**
@NotEmpty验证字符串是否非空**(a!=null && a!=“”)**
@NotBlank验证字符串是否非空白字符**(a!=null && a.trim().length > 0)**
@Size(max=, min=)验证字符串、集合、Map、数组的大小是否在指定范围内
@Pattern(regexp=, flag=)验证字符串是否符合指定的正则表达式

2 SpringMVC数据校验基本实现

2.1 导入依赖

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

2.2 定义校验规则

在 DTO(Data Transfer Object)或实体类上使用注解来定义校验规则。这些注解可以应用于字段、getter 方法或类级别。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentDTO {
    @NotNull(message = "id不能为空!!!")
    private Integer stuId;
    @Length(min = 3, max = 6, message = "长度在3-6之间!!!")
    private String stuName;
    @Min(value = 18, message = "年龄最小18岁!!!")
    @Max(value = 120,message = "年龄最大120岁!!!")
    private Integer stuAge;
    @Email(message = "邮箱格式不正确!!!")
    private String stuEmail;
    
}

7.2.3 控制器中验证

package com.mytest.controller;

import com.mytest.pojo.Student;
import com.mytest.pojo.vo.StudentVO;
import jakarta.validation.Valid;
import org.springframework.beans.BeanUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
public class TestValidateController {
    @GetMapping("/testValidate")
    public String doValidate(@RequestBody @Valid StudentDTO studentDTO,
                             BindingResult br) {

        if(br.hasErrors()){
            Map<String,Object> errorMap = new HashMap<>();
            List<FieldError> fieldErrors = br.getFieldErrors();
            for (FieldError fieldError : fieldErrors) {
                errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
            }
            return errorMap.toString();
        }

        Student student = new Student();
        BeanUtils.copyProperties(studentDTO, student);

        System.out.println("student = " + student);
        return "doValidate";
    }


}

3 SpringMVC自定义数据校验器(了解)

3.1 定义校验注解

package com.mytest.annotation;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;

@Documented
@Constraint(
    validatedBy = {GenderValidate.class}
)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Gender {

    String message() default "性别只能是男或女!";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

7.3.2 定义校验注解规则

package com.mytest.annotation;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class GenderValidate implements ConstraintValidator<Gender,String> {
    @Override
    public void initialize(Gender constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
        return value.equals("男") || value.equals("女");
    }
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentDTO {

    @NotNull(message = "id不能为空!!!")
    private Integer stuId;
    @Length(min = 3, max = 6, message = "长度在3-6之间!!!")
    private String stuName;
    @Min(value = 18, message = "年龄最小18岁!!!")
    @Max(value = 120,message = "年龄最大120岁!!!")
    private Integer stuAge;
    @Email(message = "邮箱格式不正确!!!")
    private String stuEmail;
    //自定义验证器
    @Gender(message = "请求输入正确的性别!")
    private String stuGender;
}

4 VO与DTO

4.1 各种O的概念

在 Spring MVC 中,VO(Value Object)、DTO(Data Transfer Object)等对象类型用于不同场景下的数据处理和传输。理解这些概念及其用途对于设计良好的分层架构非常重要。以下是关于 各种O 介绍:

  • Value Object (VO):强调不可变性和基于内容的相等性,适用于表示具体值或属性组合。
  • Data Transfer Object (DTO):用于简化不同层次间的数据传输,避免直接暴露实体类。
  • Entity:表示持久化的业务实体,通常与数据库表相对应。
  • Form Object:专为表单提交设计,便于收集和验证用户输入。
  • Command Object:封装了执行某项操作所需的所有信息,常用于命令模式。
  • Transfer Object (TO):专注于远程调用或跨进程通信中的高效数据传输。

4.2 DTO介绍

DTO全称:Data Transfer Object (DTO)

  • 定义

  • DTO 主要用于在不同的应用程序层之间传递数据,特别是从服务层到表现层(如控制器)。它通常是一个简单的 POJO(Plain Old Java Object),包含一组 getter 和 setter 方法。

  • 使用场景

    • 简化复杂业务逻辑与视图之间的数据交换。
    • 避免直接暴露实体类给前端,从而保护内部数据结构并减少耦合。
    • 支持多样的展示需求,例如将多个实体的信息整合到一个 DTO 中进行返回。
  • 案例代码

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class StudentDTO {
        @NotNull(message = "id不能为空!!!")
        private Integer stuId;
        @Length(min = 3, max = 6, message = "长度在3-6之间!!!")
        private String stuName;
        @Min(value = 18, message = "年龄最小18岁!!!")
        @Max(value = 120,message = "年龄最大120岁!!!")
        private Integer stuAge;
        @Email(message = "邮箱格式不正确!!!")
        private String stuEmail;
        //自定义验证器
        @Gender(message = "请求输入正确的性别!")
        private String stuGender;
    }
    

4.3 VO介绍(了解)

VO全称:Value Object

  • 不可变性:Value Object 是一个表示值的对象,它的状态是不可改变的。一旦创建,其属性就不能被修改。

  • 相等性基于内容:两个 Value Object 如果它们的内容相同,则认为它们相等,而不是根据对象的引用地址。

  • 使用场景

    • 当你需要确保某个对象的状态不会发生变化时,比如货币、日期时间等。
    • 在领域驱动设计(DDD)中,Value Object 用来封装具有特定含义的值或属性组合。
  • 案例代码

    public final class Money implements ValueObject {
        private final BigDecimal amount;
        private final Currency currency;
      
        public Money(BigDecimal amount, Currency currency) {
            this.amount = amount;
            this.currency = currency;
        }
      
        // Getters but no setters...
    }
    

Swagger接口文档

1 Swagger介绍

Swagger 是一个用于设计、构建、记录和使用 RESTful Web 服务的开源框架。它不仅简化了 API 的开发过程,还提供了强大的工具来生成交互式的 API 文档,使得开发者和消费者能够更好地理解和使用这些 API。

Swagger 可以快速生成实时接口文档,方便前后开发人员进行协调沟通。遵循 OpenAPI 规范。 Knife4j 是基于 Swagger之上的增强套件

什么是 Swagger?

Swagger 实际上是 OpenAPI 规范的一部分,OpenAPI 规范定义了一套规则来描述 RESTful API。而 Swagger 则提供了一系列工具和服务来支持这一规范,包括但不限于:

  • Swagger Editor:一个基于浏览器的编辑器,允许你以 YAML 或 JSON 格式编写 OpenAPI 规范文件,并实时预览文档。
  • Swagger UI:一个动态生成的 HTML 页面,根据 OpenAPI 规范文件展示 API 的详细信息,并允许用户直接在浏览器中测试 API 调用。
  • Swagger Codegen:可以从 OpenAPI 规范自动生成客户端 SDK 和服务器存根代码。

2 Swagger基本应用

2.1 常用API

Knife4j 使用,参考:doc.xiaominfo.com/docs/quick-…
swagger标准常用注解;
访问 http://ip:port/doc.html 即可查看接口文档

注解标注位置作用
@Tagcontroller 类描述 controller 作用
@Parameter参数标识参数作用
@Parameters参数参数多重说明
@Schemamodel 层的 JavaBean描述模型作用及每个属性
@Operation方法描述方法作用
@ApiResponse方法描述响应状态码等

2.2 实现步骤

  • 导入依赖

    <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
        <version>4.4.0</version>
    </dependency>
    
  • 编写配置文件:application.properties或application.yml

    # springdoc-openapi项目配置
    springdoc:
      swagger-ui:
        path: /swagger-ui.html
        tags-sorter: alpha
        operations-sorter: alpha
      api-docs:
        path: /v3/api-docs
      group-configs:
        - group: 'default'
          paths-to-match: '/**'
          packages-to-scan: com.mytest.controller
    # knife4j的增强配置,不需要增强可以不配
    knife4j:
      enable: true
      setting:
        language: zh_cn
    
  • 使用注解

    package com.mytest.pojo;
    
    import com.mytest.springmvc05restful.validation.annotations.Gender;
    import io.swagger.v3.oas.annotations.media.Schema;
    import jakarta.validation.constraints.*;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import org.springframework.web.bind.annotation.PathVariable;
    import java.io.Serializable;
    import java.math.BigDecimal;
    
    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    // 单一职责原则
    public class Employee implements Serializable {
        @Schema(description = "修改员工信息时,该属性必须有值.增加员工信息时则不需要赋值")
        private Integer id;
        @Schema(description = "员工姓名")
        private String name;
        private Integer age;
        private String email;
        private String address;
        private BigDecimal salary;
        private String gender;
    }
    
    import io.swagger.v3.oas.annotations.Operation;
    import io.swagger.v3.oas.annotations.media.Content;
    import io.swagger.v3.oas.annotations.media.Schema;
    import io.swagger.v3.oas.annotations.responses.ApiResponse;
    import io.swagger.v3.oas.annotations.tags.Tag;
    
    @RestController
    @RequestMapping("/api/v1")
    @Tag(name = "员工管理", description = "员工信息管理的控制器")
    public class UserController {
        @Operation(summary = "查询全部员工信息的处理器")
        @GetMapping("/employees")
        public String getEmployees() {
            List<Employee> employees =employeeService.findAllEmployee();
            return employees.toString();
        }
        
        @Operation(summary = "根据id修改员工信息的业务接口实现")
        @PutMapping("/employee")
        public String updateEmployee(@RequestBody
                                          Employee employee){
            employeeService.updateEmnployee(employee);
            return  "success";
        }
    
        @Operation(summary = "添加员工信息的处理器")
        @Parameters({
                @Parameter(name = "eid", description = "员工ID",in = ParameterIn.PATH),
                @Parameter(name = "salary", description = "员工薪资",in = ParameterIn.PATH),
                @Parameter(name = "gender", description = "员工性别",in = ParameterIn.PATH),
                @Parameter(name = "address", description = "员工住址")
        })
        @PostMapping("/emp")
        public String insertEmployee(@RequestBody Employee employee) {
            employeeService.insertEmployee(employee);
            return "{'status':'ok'}";
        }
    }
    
  • 访问Swagger UI

    • 最后,访问Knife4j的文档地址:http://ip:port/doc.html即可查看文档

SpringMVC工作原理

1 SpringMVC九大组件

1.1 DispatcherServlet

  • 作用
    • 前端控制器,是整个 Spring MVC 的控制中心。用户请求到达前端控制器后,由前端控制器分发请求至后端控制器。
    • DispatchServlet中核心调度方法:doDispatch()
  • 配置:通常在 web.xml 中配置 DispatcherServlet。

1.2 HandlerMapping

  • 作用
    • 处理器映射器,负责根据用户请求找到 Handler(处理器),即 Controller。
    • 通过HandlerMapping获取HandlerAdapter对象
  • 类型:包括 BeanNameUrlHandlerMapping、DefaultAnnotationHandlerMapping、RequestMappingHandlerMapping 等。

1.3 HandlerAdapter

  • 作用
    • 处理器适配器,按照特定规则(HandlerAdapter 要求的规则)去执行 Handler。
    • 通过HandlerAdapter调用Controller中相应方法
  • 类型:包括 SimpleControllerHandlerAdapter、HttpRequestHandlerAdapter、AnnotationMethodHandlerAdapter 等。

1.4 Handler(Controller)

  • 作用:处理器,是后端控制器,在 MVC 模型中负责处理具体的业务逻辑。
  • 注解:通常使用 @Controller 注解标记一个类为 Controller。

1.5 ModelAndView

  • 作用:封装了 Model 和 View 的信息。Controller 处理完用户请求后返回一个 ModelAndView 对象,其中包含了模型数据和视图信息。
  • 结构:包含一个 Map 对象(用于存放模型数据)和一个 View 或视图名称(用于指定视图)。

1.6 ViewResolver

  • 作用:视图解析器,根据逻辑视图名解析成真正的视图 View(如 JSP、Thymeleaf 等)。
  • 类型:包括 InternalResourceViewResolver、FreeMarkerViewResolver、ThymeleafViewResolver 等。

1.7 View

  • 作用:视图,负责将结果显示给用户。视图可以是 JSP、HTML、PDF 等任何形式的页面。
  • 实现:通常由 ViewResolver 解析得到。

1.8 ExceptionHandler

  • 作用:异常处理器,用于处理 Controller 中抛出的异常。
  • 注解:使用 @ExceptionHandler 注解标记一个方法为异常处理器。

1.9 Interceptor

  • 作用:拦截器,类似于 Servlet 中的 Filter,用于在请求到达 Controller 之前或之后执行一些预处理或后处理操作。
  • 配置:在 Spring MVC 配置文件中配置拦截器链。

2 SpringMVC工作原理

2.1 图解SpringMVC工作原理

2.2 SpringMVC工作原理源码简述

  1. 客户端发送请求:用户通过浏览器或其他客户端工具向服务器发起 HTTP 请求。

  2. DispatcherServlet 接收请求:所有进入应用程序的请求首先由 DispatcherServlet 接收。

  3. 查找 HandlerMapping:根据请求的 URL,DispatcherServlet 使用 HandlerMapping 来确定哪个控制器应该处理该请求。

  4. 选择 HandlerAdapter:找到合适的 HandlerAdapter 来执行选定的控制器。

  5. 执行控制器方法:HandlerAdapter 调用控制器中的相应方法,并传递必要的参数(如路径变量、请求参数等)。

  6. 处理业务逻辑:控制器方法内部可能涉及到调用服务层、访问数据库等操作,完成相应的业务逻辑。

    • 如控制器中未使用@ResponseBody
      • 返回 ModelAndView:控制器方法完成后,通常会返回一个包含模型数据和视图名称的 ModelAndView 对象。
      • 解析视图:DispatcherServlet 使用 ViewResolver 将视图名称转换为实际的视图对象。
      • 渲染视图:使用解析后的视图对象,结合模型数据生成最终的响应内容(如 HTML 页面)。
      • 发送响应:将生成的内容发送回客户端展示给用户。
    • 如控制器中使用@ResponseBody:返回数据即可
  7. 配置拦截器

    • 控制器中存在异常
      • 执行异常处理器
      • 拦截器中preHandle()及afterCompletion()会执行,postHandle()不会执行
    • 控制器中不存在异常
      • 不执行异常处理器
      • 拦截器中所有方法正常执行