springmvc 全局异常处理 and 拦截器

文章目录

配置类 SpringMvcInit

java 复制代码
package com.cool.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

"可以被 web项目加载,会初始化ioc容器,会设置 dispatcherServlet的地址"

public class SpringMvcInit extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[0];
    }

    "设置配置类"
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{MvcConfig.class};
    }

    "配置springmvc内部自带servlet的访问地址"
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

配置类 MvcConfig

java 复制代码
package com.cool.config;

import com.cool.interceptor.MyInterceptor;
import com.cool.interceptor.MyInterceptor1;
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;

@EnableWebMvc
@Configuration
@ComponentScan({"com.cool.controller", "com.cool.error"})
public class MvcConfig implements WebMvcConfigurer {

    /*
        通过configurer.enable()启用默认Servlet处理。
        当请求无法匹配到Spring的控制器时,会回退到Servlet容器的默认Servlet(如:Tomcat的DefaultServlet),
        主要用于,处理静态资源(如HTML、JS、CSS)。

        典型场景:
            配置后,对 /static/** 的请求会先由Spring处理,未匹配则交给容器默认Servlet
    */
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 配置方案-1:拦截全部请求
        registry.addInterceptor(new MyInterceptor());


        // 配置方案-2:拦截指定的请求
        // *:任意一层字符串;**:任意多层字符串
        registry.addInterceptor(new MyInterceptor())
                .addPathPatterns("/user/**"); // 拦截 user/ 下的所有请求


        // 配置方案-3:排除拦截
        // 拦截 user/ 下除了 /user/data1 之外的请求地址
        registry.addInterceptor(new MyInterceptor())
                .addPathPatterns("/user/**")
                .excludePathPatterns("/user/data1");


        // 如果有多个拦截器,执行流程是怎样的呢?
        /*
            先声明的优先级高,优先级高的在外层,
            MyInterceptor.preHandle
            MyInterceptor1.preHandle
            OrderController.data
            MyInterceptor1.postHandle
            MyInterceptor.postHandle
            MyInterceptor1.afterCompletion
            MyInterceptor.afterCompletion
        */
        registry.addInterceptor(new MyInterceptor());
        registry.addInterceptor(new MyInterceptor1());
    }
}

全局异常处理器

全局异常处理器 GlobalException 使用了 @RestControllerAdvice,

处理 ArithmeticException 和 Exception。

当 Controller 中的方法抛出这些异常时,会被对应的 @ExceptionHandler 方法捕获并处理。

java 复制代码
package com.cool.error;

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

/**
 * 全局异常处理器
 *
 * @ControllerAdvice
 *      作用:全局异常发生,就会走此类的方法,可以返回 逻辑视图、转发、重定向
 *
 * @RestControllerAdvice
 *      相当于 @ControllerAdvice 和 @ResponseBody 的组合,
 *      可以直接返回 json 字符串
 *
 */

@RestControllerAdvice
public class GlobalException {

    /*
        发生异常后 -> ControllerAdvice注解的类型 -> @ExceptionHandler(指定的异常) -> 执行handler

        如果,没有找到指定的异常,就会找父类Exception
    */

	"精准匹配算术异常"
    @ExceptionHandler(ArithmeticException.class)
    public Object ArithmeticExceptionHandler(ArithmeticException e){
        // 自定义异常处理即可
        String message = e.getMessage();
        System.out.println("ArithmeticExceptionHandler=" + message);
        return message;
    }

	"兜底所有异常"
    @ExceptionHandler(Exception.class)
    public Object ExceptionHandler(Exception e){
        String message = e.getMessage();
        System.out.println("ExceptionHandler=" + message);
        return message;
    }
}

User实体类 (参数校验)

java 复制代码
package com.cool.pojo;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Past;
import lombok.Data;
import org.hibernate.validator.constraints.Length;

import java.util.Date;

/**
 * name:不为null、不为空字符串
 *      字符串不为空:@NotBlank
 *      集合不为空:@NotEmpty
 *      包装类型不为空:@NotNull
 *
 * password:长度大于6
 */

@Data
public class User {
    @NotBlank
    private String name;

    @Length(min = 6, max = 20)
    private String password;

    @Min(1)
    private int age;

    @Email
    private String email;

    @Past
    private Date birthday;
}

UserController

java 复制代码
package com.cool.controller;

import com.cool.pojo.User;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("user")
public class UserController {

    /**
     * 步骤1:实体类属性添加校验注解
     * 步骤2:handler(@Validated 实体类对象){}
     *       细节:
     *          param、json 校验注解都有效果
     *          只不过,json参数的话,需要用 @RequestBody 修饰形参哦!
     *
     * 这里有个天坑:如果不符合校验规则,会直接向前端抛出异常
     * 解决办法:
     *      handler(@Validated 实体类对象, BindingResult request){}
     *      多加一个 BindingResult request 参数,
     *      此参数,必须紧挨着 @Validated 实体类对象,否则此参数不管用
     */
    @PostMapping("register")
    public Object register(@Validated @RequestBody User user, BindingResult result){
        System.out.println("user= " +  user);

        if(result.hasErrors()){
            // 有错误的话,就不直接返回给前端了,可以在这里自定义内容,返回给前端
            Map data = new HashMap();
            data.put("code", 400);
            data.put("msg", "参数校验异常");
            return data;
        }

        return user;
    }

    @GetMapping("data")
    public String data(){
        // 这个方法,做成,空指针异常
        String name = null;
        name.toString();  // NullPointerException,报错后,就不会执行下面的代码了,就会取异常处理那里
        return "data - ok";
    }

    @GetMapping("data1")
    public String data1(){
        // 这个方法,做成,算数异常
        int i = 1/0;  // ArithmeticException: / by zero
        return "data1 - ok";
    }
}

拦截器 MyInterceptor

java 复制代码
package com.cool.interceptor;

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


/**
 * 声明好这个类之后,
 * 需要在 MvcConfig 类中注册上
 */
public class MyInterceptor implements HandlerInterceptor {

    /**
     * handler执行之前触发
     * @param request 请求对象
     * @param response 响应对象
     * @param 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);
        System.out.println("MyInterceptor.preHandle");
        return true;
    }

    /**
     * 当 handler 执行完毕后,触发此方法,没有拦截机制了
     * 此方法只有在 preHandle方法 return true 的时候才会被执行
     * @param request
     * @param response
     * @param handler
     * @param modelAndView 返回的视图和共享域的数据对象,如果没有的话,返回null
     * @throws Exception
     *
     * 对 响应结果 的处理
     *
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor.postHandle");
    }

    /**
     * 整体处理完毕后,会触发这个方法
     * @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 {
        System.out.println("MyInterceptor.afterCompletion");
    }
}

拦截器 MyInterceptor1

java 复制代码
package com.cool.interceptor;

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


/**
 * 声明好这个类之后,
 * 需要在 MvcConfig 类中注册上
 */
public class MyInterceptor1 implements HandlerInterceptor {

    /**
     * handler执行之前触发
     * @param request 请求对象
     * @param response 响应对象
     * @param handler 就是我们要调用的方法对象
     * @return true:放行,false:拦截
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptor1.preHandle");
        return true;
    }


    /**
     * 当 handler 执行完毕后,触发此方法,没有拦截机制了
     * 此方法只有在 preHandle方法 return true 的时候才会被执行
     * @param request
     * @param response
     * @param handler
     * @param modelAndView 返回的视图和共享域的数据对象,如果没有的话,返回null
     * @throws Exception
     *
     * 对 响应结果 的处理
     *
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor1.postHandle");
    }


    /**
     * 整体处理完毕后,会触发这个方法
     * @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 {
        System.out.println("MyInterceptor1.afterCompletion");
    }
}
相关推荐
FQNmxDG4S7 小时前
Java多线程编程:Thread与Runnable的并发控制
java·开发语言
虹科网络安全8 小时前
艾体宝干货|数据复制详解:类型、原理与适用场景
java·开发语言·数据库
axng pmje8 小时前
Java语法进阶
java·开发语言·jvm
rKWP8gKv79 小时前
Java微服务性能监控:Prometheus与Grafana集成方案
java·微服务·prometheus
老前端的功夫9 小时前
【Java从入门到入土】28:Stream API:告别for循环的新时代
java·开发语言·python
qq_435287929 小时前
第9章 夸父逐日与后羿射日:死循环与进程终止?十个太阳同时值班的并行冲突
java·开发语言·git·死循环·进程终止·并行冲突·夸父逐日
小江的记录本9 小时前
【Kafka核心】架构模型:Producer、Broker、Consumer、Consumer Group、Topic、Partition、Replica
java·数据库·分布式·后端·搜索引擎·架构·kafka
yaoxin5211239 小时前
397. Java 文件操作基础 - 创建常规文件与临时文件
java·开发语言·python
极客先躯11 小时前
高级java每日一道面试题-2025年11月24日-容器与虚拟化题[Dockerj]-runc 的作用是什么?
java·oci 的命令行工具·最小可用·无守护进程·完全标准·创建容器的核心流程·runc 核心职责思维导图
用户606487671889611 小时前
AI 抢不走的技能:用 Claude API 构建自动化工作流实战
java