• SSM - Springboot - MyBatis-Plus 全栈体系(二十二)


    第四章 SpringMVC

    五、SpringMVC 其他扩展

    1. 全局异常处理机制

    1.1 异常处理两种方式
    • 开发过程中是不可避免地会出现各种异常情况的,例如网络连接异常、数据格式异常、空指针异常等等。异常的出现可能导致程序的运行出现问题,甚至直接导致程序崩溃。因此,在开发过程中,合理处理异常、避免异常产生、以及对异常进行有效的调试是非常重要的。

    • 对于异常的处理,一般分为两种方式:

      • 编程式异常处理:是指在代码中显式地编写处理异常的逻辑。它通常涉及到对异常类型的检测及其处理,例如使用 try-catch 块来捕获异常,然后在 catch 块中编写特定的处理代码,或者在 finally 块中执行一些清理操作。在编程式异常处理中,开发人员需要显式地进行异常处理,异常处理代码混杂在业务代码中,导致代码可读性较差。
      • 声明式异常处理:则是将异常处理的逻辑从具体的业务逻辑中分离出来,通过配置等方式进行统一的管理和处理。在声明式异常处理中,开发人员只需要为方法或类标注相应的注解(如 @Throws@ExceptionHandler),就可以处理特定类型的异常。相较于编程式异常处理,声明式异常处理可以使代码更加简洁、易于维护和扩展。
    • 站在宏观角度来看待声明式事务处理:

      • 整个项目从架构这个层面设计的异常处理的统一机制和规范。
      • 一个项目中会包含很多个模块,各个模块需要分工完成。如果张三负责的模块按照 A 方案处理异常,李四负责的模块按照 B 方案处理异常……各个模块处理异常的思路、代码、命名细节都不一样,那么就会让整个项目非常混乱。
      • 使用声明式异常处理,可以统一项目处理异常思路,项目更加清晰明了!
    1.2 基于注解异常声明异常处理
    1.2.1 声明异常处理控制器类
    • 异常处理控制类,统一定义异常处理 handler 方法!
    /**
     * projectName: com.alex.execptionhandler
     *
     * description: 全局异常处理器,内部可以定义异常处理Handler!
     */
    
    /**
     * @RestControllerAdvice = @ControllerAdvice + @ResponseBody
     * @ControllerAdvice 代表当前类的异常处理controller!
     */
    @RestControllerAdvice
    public class GlobalExceptionHandler {
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    1.2.2 声明异常处理 hander 方法
    • 异常处理 handler 方法和普通的 handler 方法参数接收和响应都一致!
    • 只不过异常处理 handler 方法要映射异常,发生对应的异常会调用!
    • 普通的 handler 方法要使用@RequestMapping 注解映射路径,发生对应的路径调用!
    /**
     * 异常处理handler
     * @ExceptionHandler(HttpMessageNotReadableException.class)
     * 该注解标记异常处理Handler,并且指定发生异常调用该方法!
     *
     *
     * @param e 获取异常对象!
     * @return 返回handler处理结果!
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Object handlerJsonDateException(HttpMessageNotReadableException e){
    
        return null;
    }
    
    /**
     * 当发生空指针异常会触发此方法!
     * @param e
     * @return
     */
    @ExceptionHandler(NullPointerException.class)
    public Object handlerNullException(NullPointerException e){
    
        return null;
    }
    
    /**
     * 所有异常都会触发此方法!但是如果有具体的异常处理Handler!
     * 具体异常处理Handler优先级更高!
     * 例如: 发生NullPointerException异常!
     *       会触发handlerNullException方法,不会触发handlerException方法!
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    public Object handlerException(Exception e){
    
        return null;
    }
    
    • 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
    1.2.3 配置文件扫描控制器类配置
    • 确保异常处理控制类被扫描
    /* 扫描controller对应的包,将handler加入到ioc */
     @ComponentScan(basePackages = {"com.alex.controller",
     "com.alex.exceptionhandler"})
    
    • 1
    • 2
    • 3

    2. 拦截器使用

    2.1 拦截器概念
    2.1.1 拦截器和过滤器解决问题
    • 生活中 - 为了提高乘车效率,在乘客进入站台前统一检票
      在这里插入图片描述
    • 程序中 - 在程序中,使用拦截器在请求到达具体 handler 方法前,统一执行检测
      在这里插入图片描述
    2.1.2 拦截器 Springmvc VS 过滤器 javaWeb
    • 相似点
      • 拦截:必须先把请求拦住,才能执行后续操作
      • 过滤:拦截器或过滤器存在的意义就是对请求进行统一处理
      • 放行:对请求执行了必要操作后,放请求过去,让它访问原本想要访问的资源
    • 不同点
      • 工作平台不同
        • 过滤器工作在 Servlet 容器中
        • 拦截器工作在 SpringMVC 的基础上
      • 拦截的范围
        • 过滤器:能够拦截到的最大范围是整个 Web 应用
        • 拦截器:能够拦截到的最大范围是整个 SpringMVC 负责的请求
      • IOC 容器支持
        • 过滤器:想得到 IOC 容器需要调用专门的工具方法,是间接的
        • 拦截器:它自己就在 IOC 容器中,所以可以直接从 IOC 容器中装配组件,也就是可以直接得到 IOC 容器的支持
    2.1.3 选择
    • 功能需要如果用 SpringMVC 的拦截器能够实现,就不使用过滤器。
      在这里插入图片描述
    2.2 拦截器使用
    2.2.1 创建拦截器类
    public class Process01Interceptor implements HandlerInterceptor {
    
        // if( ! preHandler()){return;}
        // 在处理请求的目标 handler 方法前执行
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("request = " + request + ", response = " + response + ", handler = " + handler);
            System.out.println("Process01Interceptor.preHandle");
    
            // 返回true:放行
            // 返回false:不放行
            return true;
        }
    
        // 在目标 handler 方法之后,handler报错不执行!
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("request = " + request + ", response = " + response + ", handler = " + handler + ", modelAndView = " + modelAndView);
            System.out.println("Process01Interceptor.postHandle");
        }
    
        // 渲染视图之后执行(最后),一定执行!
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("request = " + request + ", response = " + response + ", handler = " + handler + ", ex = " + ex);
            System.out.println("Process01Interceptor.afterCompletion");
        }
    }
    
    • 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
    • 拦截器方法拦截位置:
      在这里插入图片描述
    2.2.2 修改配置类添加拦截器
    @EnableWebMvc  //json数据处理,必须使用此注解,因为他会加入json处理器
    @Configuration
    @ComponentScan(basePackages = {"com.alex.controller","com.alex.exceptionhandler"}) //TODO: 进行controller扫描
    //WebMvcConfigurer springMvc进行组件配置的规范,配置组件,提供各种方法! 前期可以实现
    public class SpringMvcConfig implements WebMvcConfigurer {
    
        //配置jsp对应的视图解析器
        @Override
        public void configureViewResolvers(ViewResolverRegistry registry) {
            //快速配置jsp模板语言对应的
            registry.jsp("/WEB-INF/views/",".jsp");
        }
    
        //开启静态资源处理 
        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
            configurer.enable();
        }
    
        //添加拦截器
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            //将拦截器添加到Springmvc环境,默认拦截所有Springmvc分发的请求
            registry.addInterceptor(new Process01Interceptor());
        }
    }
    
    • 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
    2.2.3 配置详解
    2.2.3.1 默认拦截全部
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //将拦截器添加到Springmvc环境,默认拦截所有Springmvc分发的请求
        registry.addInterceptor(new Process01Interceptor());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    2.2.3.2 精准配置
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
        //将拦截器添加到Springmvc环境,默认拦截所有Springmvc分发的请求
        registry.addInterceptor(new Process01Interceptor());
    
        //精准匹配,设置拦截器处理指定请求 路径可以设置一个或者多个,为项目下路径即可
        //addPathPatterns("/common/request/one") 添加拦截路径
        //也支持 /* 和 /** 模糊路径。 * 任意一层字符串 ** 任意层 任意字符串
        registry.addInterceptor(new Process01Interceptor()).addPathPatterns("/common/request/one","/common/request/tow");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    2.2.3.3 排除配置
    //添加拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
        //将拦截器添加到Springmvc环境,默认拦截所有Springmvc分发的请求
        registry.addInterceptor(new Process01Interceptor());
    
        //精准匹配,设置拦截器处理指定请求 路径可以设置一个或者多个,为项目下路径即可
        //addPathPatterns("/common/request/one") 添加拦截路径
        registry.addInterceptor(new Process01Interceptor()).addPathPatterns("/common/request/one","/common/request/tow");
    
    
        //排除匹配,排除应该在匹配的范围内排除
        //addPathPatterns("/common/request/one") 添加拦截路径
        //excludePathPatterns("/common/request/tow"); 排除路径,排除应该在拦截的范围内
        registry.addInterceptor(new Process01Interceptor())
                .addPathPatterns("/common/request/one","/common/request/tow")
                .excludePathPatterns("/common/request/tow");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    2.2.4 多个拦截器执行顺序
    • preHandle() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置顺序调用各个 preHandle() 方法。
    • postHandle() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置相反的顺序调用各个 postHandle() 方法。
    • afterCompletion() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置相反的顺序调用各个 afterCompletion() 方法。

    3. 参数校验

    • 在 Web 应用三层架构体系中,表述层负责接收浏览器提交的数据,业务逻辑层负责数据的处理。为了能够让业务逻辑层基于正确的数据进行处理,我们需要在表述层对数据进行检查,将错误的数据隔绝在业务逻辑层之外。
    3.1 1 校验概述
    • JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 6.0 标准中。JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证。
    注解规则
    @Null标注值必须为 null
    @NotNull标注值不可为 null
    @AssertTrue标注值必须为 true
    @AssertFalse标注值必须为 false
    @Min(value)标注值必须大于或等于 value
    @Max(value)标注值必须小于或等于 value
    @DecimalMin(value)标注值必须大于或等于 value
    @DecimalMax(value)标注值必须小于或等于 value
    @Size(max,min)标注值大小必须在 max 和 min 限定的范围内
    @Digits(integer,fratction)标注值值必须是一个数字,且必须在可接受的范围内
    @Past标注值只能用于日期型,且必须是过去的日期
    @Future标注值只能用于日期型,且必须是将来的日期
    @Pattern(value)标注值必须符合指定的正则表达式
    • JSR 303 只是一套标准,需要提供其实现才可以使用。Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解:
    注解规则
    @Email标注值必须是格式正确的 Email 地址
    @Length标注值字符串大小必须在指定的范围内
    @NotEmpty标注值字符串不能是空字符串
    @Range标注值必须在指定的范围内
    • Spring 4.0 版本已经拥有自己独立的数据校验框架,同时支持 JSR 303 标准的校验框架。Spring 在进行数据绑定时,可同时调用校验框架完成数据校验工作。在 SpringMVC 中,可直接通过注解驱动 @EnableWebMvc 的方式进行数据校验。Spring 的 LocalValidatorFactoryBean 既实现了 Spring 的 Validator 接口,也实现了 JSR 303 的 Validator 接口。只要在 Spring 容器中定义了一个 LocalValidatorFactoryBean,即可将其注入到需要数据校验的 Bean 中。Spring 本身并没有提供 JSR 303 的实现,所以必须将 JSR 303 的实现者的 jar 包放到类路径下。

      配置 @EnableWebMvc 后,SpringMVC 会默认装配好一个 LocalValidatorFactoryBean,通过在处理方法的入参上标注 @Validated 注解即可让 SpringMVC 在完成数据绑定后执行数据校验的工作。

    3.1.2 操作演示
    3.1.2.1 导入依赖
    
    <dependency>
        <groupId>jakarta.platformgroupId>
        <artifactId>jakarta.jakartaee-web-apiartifactId>
        <version>9.1.0version>
        <scope>providedscope>
    dependency>
    
    
    
    <dependency>
        <groupId>org.hibernate.validatorgroupId>
        <artifactId>hibernate-validatorartifactId>
        <version>8.0.0.Finalversion>
    dependency>
    
    <dependency>
        <groupId>org.hibernate.validatorgroupId>
        <artifactId>hibernate-validator-annotation-processorartifactId>
        <version>8.0.0.Finalversion>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    3.1.2.2 应用校验注解
    import jakarta.validation.constraints.Email;
    import jakarta.validation.constraints.Min;
    import org.hibernate.validator.constraints.Length;
    
    /**
     * projectName: com.alex.pojo
     */
    public class User {
        //age   1 <=  age < = 150
        @Min(10)
        private int age;
    
        //name 3 <= name.length <= 6
        @Length(min = 3,max = 10)
        private String name;
    
        //email 邮箱格式
        @Email
        private String email;
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getEmail() {
            return email;
        }
    
        public void setEmail(String email) {
            this.email = email;
        }
    }
    
    • 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
    • 44
    3.1.2.3 handler 标记和绑定错误收集
    @RestController
    @RequestMapping("user")
    public class UserController {
    
        /**
         * @Validated 代表应用校验注解! 必须添加!
         */
        @PostMapping("save")
        public Object save(@Validated @RequestBody User user,
                           //在实体类参数和 BindingResult 之间不能有任何其他参数, BindingResult可以接受错误信息,避免信息抛出!
                           BindingResult result){
           //判断是否有信息绑定错误! 有可以自行处理!
            if (result.hasErrors()){
                System.out.println("错误");
                String errorMsg = result.getFieldError().toString();
                return errorMsg;
            }
            //没有,正常处理业务即可
            System.out.println("正常");
            return user;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    3.1.2.4 测试效果

    在这里插入图片描述

    3.1.3 易混总结
    • @NotNull、@NotEmpty、@NotBlank 都是用于在数据校验中检查字段值是否为空的注解,但是它们的用法和校验规则有所不同。
    1. @NotNull (包装类型不为 null)

      @NotNull 注解是 JSR 303 规范中定义的注解,当被标注的字段值为 null 时,会认为校验失败而抛出异常。该注解不能用于字符串类型的校验,若要对字符串进行校验,应该使用 @NotBlank 或 @NotEmpty 注解。

    2. @NotEmpty (集合类型长度大于 0)

      @NotEmpty 注解同样是 JSR 303 规范中定义的注解,对于 CharSequence、Collection、Map 或者数组对象类型的属性进行校验,校验时会检查该属性是否为 Null 或者 size()==0,如果是的话就会校验失败。但是对于其他类型的属性,该注解无效。需要注意的是只校验空格前后的字符串,如果该字符串中间只有空格,不会被认为是空字符串,校验不会失败。

    3. @NotBlank (字符串,不为 null,切不为" "字符串)

      @NotBlank 注解是 Hibernate Validator 附加的注解,对于字符串类型的属性进行校验,校验时会检查该属性是否为 Null 或 “” 或者只包含空格,如果是的话就会校验失败。需要注意的是,@NotBlank 注解只能用于字符串类型的校验。

    • 总之,这三种注解都是用于校验字段值是否为空的注解,但是其校验规则和用法有所不同。在进行数据校验时,需要根据具体情况选择合适的注解进行校验。

    六、SpringMVC 总结

    核心点掌握
    springmvc 框架主要作用、核心组件、调用流程
    简化参数接收路径设计、参数接收、请求头接收、cookie 接收
    简化数据响应模板页面、转发和重定向、JSON 数据、静态资源
    restful 风格设计主要作用、具体规范、请求方式和请求参数选择
    功能扩展全局异常处理、拦截器、参数校验注解
  • 相关阅读:
    ArcGIS Map Sdk for unity使用
    设计模式——策略模式
    2022年陕西省初级职称怎么报名?需要准备什么资料?
    Win11显示麦克风未插上怎么办?Win11显示麦克风未插上的解决方法
    该虚拟机的某个磁盘已经由虚拟机或者快照使用 vmware 损坏 .vmdk为空
    贪心算法(一) | leecode刷题笔记
    SpringBoot 项目实战 ~ 4.分类管理
    springboot web项目中 Set-Cookie 失败 办法
    安卓如何搜索到蓝牙5.0的扩展广播
    关于Zotero的主要插件功能说明
  • 原文地址:https://blog.csdn.net/sgsgkxkx/article/details/133617272