• SpringMVC全局异常处理+拦截器使用+参数校验


    SpringMVC全局异常处理+拦截器使用+参数校验

    1 全局异常的处理机制

    1.1 异常处理两种方式

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

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

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

    站在宏观角度来看待声明式事务处理:

    整个项目从架构这个层面设计的异常处理的统一机制和规范。

    一个项目中会包含很多个模块,各个模块需要分工完成。如果张三负责的模块按照 A 方案处理异常,李四负责的模块按照 B 方案处理异常……各个模块处理异常的思路、代码、命名细节都不一样,那么就会让整个项目非常混乱。

    使用声明式异常处理,可以统一项目处理异常思路,项目更加清晰明了!

    1.2 基于注解异常声明异常处理

    //@ControllerAdvice   //可以返回逻辑视图  转发和重定向的!
    @RestControllerAdvice   //@ResponseBody  直接返回json字符串
    public class GlobalExceptionHandler {
    
        /**
         * 算数异常的处理
         * @return
         */
        @ExceptionHandler(ArithmeticException.class)
        public Object ArithmeticException(ArithmeticException e){
            //自定义处理异常即可  handler
            String message = e.getMessage();
            System.out.println("message = " + message);
            return message;
        }
    
        /**
         * 全局异常 当精确查找不到对应的异常时  就查找父异常
         */
        @ExceptionHandler(Exception.class)
        public Object ExceptionHandler(Exception e){
            //自定义处理异常即可 handler
            String message = e.getMessage();
            System.out.println("message = " + message);
            return message;
        }
    }
    

    2 拦截器的使用

    2.1 拦截器的概念

    拦截器 Springmvc VS 过滤器 javaWeb:

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

    选择:

    功能需要如果用 SpringMVC 的拦截器能够实现,就不使用过滤器。

    在这里插入图片描述

    2.2 拦截器的使用

    package com.atguigu.interceptor;
    
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    /**
     * @author XXDTG
     * @version 1.0
     * @api 自定义拦截器
     * @since 2024年 月 日
     */
    public class MyInterceptor implements HandlerInterceptor {
    
        /**
         * 执行handler之前  调佣的拦截方法
         * 一般用于编码格式设置、登录保护、权限处理
         * @param request   请求对象
         * @param response  响应对象
         * @param handler   handler就是要调用的方法对象
         * @return  true  放行   false   拦截
         * @throws Exception
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("request = " + request + ", response = " + response + ", handler = " + handler);
            return true;
        }
    
        /**
         * 当handler执行完毕后  prehandler return true  触发的方法    没有拦截机制了
         * 对结果处理 敏感词汇检查
         * @param request   请求
         * @param response  响应
         * @param handler   handler方法
         * @param modelAndView  返回的视图和共享域对象
         * @throws Exception
         */
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
        }
    
        /**
         * 整体处理完毕调用的方法
         * @param request
         * @param response
         * @param handler
         * @param ex    handler报错了 异常对象
         * @throws Exception
         */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
        }
    }
    
    
    package com.atguigu.config;
    
    import com.atguigu.interceptor.MyInterceptor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
    import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
    
    /**
     * @author XXDTG
     * @version 1.0
     * @api 项目的配置类 controller handlerMapping handlerAdapter加入ioc容器
     * @since 2024年 月 日
     */
    
    @EnableWebMvc   //handlerAdapter配置json转化器
    @Configuration
    @ComponentScan("com.atguigu")
    public class MvcConfig implements WebMvcConfigurer {
    
        /**
         * 开启静态资源查找
         * @param configurer
         */
        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
            configurer.enable();
        }
    
        /**
         * 拦截器
         * 如果使用了多个拦截器 则先声明的优先级高  优先级高的在外层
         * 先执行优先级高的preHandler方法 最后执行优先级高的post/after方法
         * @param registry
         */
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            //配置方案一:拦截全部请求
            registry.addInterceptor(new MyInterceptor());
    
            //配置方案二:指定地址拦截
            /*
                * 任意一层字符串
                ** 任意多层字符串
             */
            registry.addInterceptor(new MyInterceptor())
                    //   /user/** 拦截user下所有请求
                    .addPathPatterns("/user/**");
    
            //配置方案三:排除拦截
            registry.addInterceptor(new MyInterceptor())
                    .addPathPatterns("/user/**")
                    .excludePathPatterns("/user/data1");
        }
    }
    

    3 参数校验

    在这里插入图片描述

    在 Web 应用三层架构体系中,表述层负责接收浏览器提交的数据,业务逻辑层负责数据的处理。为了能够让业务逻辑层基于正确的数据进行处理,我们需要在表述层对数据进行检查,将错误的数据隔绝在业务逻辑层之外。

    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 在完成数据绑定后执行数据校验的工作。

    导入依赖

    <!-- 校验注解 -->
    <dependency>
        <groupId>jakarta.platform</groupId>
        <artifactId>jakarta.jakartaee-web-api</artifactId>
        <version>9.1.0</version>
        <scope>provided</scope>
    </dependency>
            
    <!-- 校验注解实现-->        
    <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>8.0.0.Final</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator-annotation-processor -->
    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator-annotation-processor</artifactId>
        <version>8.0.0.Final</version>
    </dependency>
    
    package com.atguigu.pojo;
    
    import jakarta.validation.constraints.Email;
    import jakarta.validation.constraints.Min;
    import jakarta.validation.constraints.Past;
    import lombok.Data;
    import org.hibernate.validator.constraints.Length;
    import org.hibernate.validator.constraints.NotBlank;
    
    import java.util.Date;
    
    /**
     * @author XXDTG
     * @version 1.0
     * @api 参数校验
     * @since 2024年 月 日
     */
    @Data
    public class User {
    
        /**
         * name  不为null和空字符串
            字符串不为空 @NotBlank     集合不为空 @NotEmpty  包装不为空 @NotNull
         * password  长度大于6
         * age 必须 >= 1
         * email 邮箱格式的字符串
         * birthday 过去时间
         */
    
        @NotBlank
        private String name;
    
        @Length(min = 6)
        private String password;
    
        @Min(1)
        private int age;
    
        @Email
        private String email;
    
        @Past
        private Date birthday;
    }
    
    
    /**
     * @author XXDTG
     * @version 1.0
     * @api
     * @since 2024年 月 日
     */
    @Controller
    @RequestMapping("user")
    public class UserController {
    
        /**
         * 步骤一:  实体类属性添加检验注解
         * 步骤二:  handler(@validated实体类 对象)
         * 细节:  param | json 校验注解都有效果
         *          json参数  - @RequestBody
         * 如果 不符合校验规则 直接向前端抛出异常
         * 接收错误绑定信息!自定义返回结果!约定:参数错误 ——》{code:400} -> 前端
         * 捕捉错误绑定错误信息
         *      1. handler(校验对象,BindingResult result) 要求:BindingResult必须紧挨着校验对象
         *      2. bindingresult获取绑定错误
         */
        @PostMapping("register")
        public Object register(@Validated @RequestBody User user, BindingResult result){
            if(result.hasErrors()){
                //有绑定错误 就不直接返回了 自己写处理代码
                Map data = new HashMap();
                data.put("code",400);
                data.put("msg","参数校验异常了");
                return data;
            }
            System.out.println("user = " + user);
            return user;
        }
    
    
    }
    
  • 相关阅读:
    不可变集合、Lambda表达式、Stream流
    01.jvm介绍
    【MyBatis-Plus】DQL
    VMware 16开启虚拟机电脑就蓝屏W11解决方法
    CMD使用不完全指南
    认识测试---什么是测试?
    用户管理系统(五)——删除用户
    [题]P1150 Peter 的烟 #数学推导
    java lombok框架
    健康中国·营养先行|2022主动健康与临床营养高峰论坛圆满落幕
  • 原文地址:https://blog.csdn.net/weixin_49429082/article/details/139462654