• SpringMvc 源码分析 (如何自定义视图 + 如何自定义异常) (十四)


    0.前记

    在前面的文章中, 表示了视图解析的原理和异常解析器的解析原理。

    这篇通过如何自定义视图和自定义异常处理和自定义异常处理的原理进行说明。

    这里说明一下, 自定义的视图和自定义的异常都是会代替容器默认的组件的, 异常还好说, 就是不符合就抛, 视图的话需要注意一下优先级, 可以在自定义的视图解析器上加上@Order注解。

    1.自定义视图

    在这里插入图片描述

    MeiNvView.java

    public class MeiNvView implements View {
        @Override
        public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
            StringBuilder sb = new StringBuilder();
            sb.append(model.get("info")).append(request.getParameter("name"));
    
            response.getWriter().write(sb.toString());
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    MeiNvViewResolver.java

    @Order
    @Component
    public class MeiNvViewResolver implements ViewResolver {
        @Override
        public View resolveViewName(String viewName, Locale locale) throws Exception {
            if (viewName.startsWith("meinv:")){
                return new MeiNvView();
            }
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    HelloController.java

        @GetMapping("/meinv")
        public String meinv(String name, Model model){
            model.addAttribute("info", "001");
            return "meinv:" + name;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    这里原理就是添加一个视图和视图解析器, 然后放入容器中, 最后访问相应的路径就可以。
    原理解释请看之前的文章

    2.自定义异常

    在这里插入图片描述

    GgktException : 自定义异常类

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    // 自定义异常
    public class GgktException extends RuntimeException {
        private Integer code;
        private String msg;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    GlobalExceptionHandler : 全局异常处理

    @RestControllerAdvice
    public class GlobalExceptionHandler {
        // 全局异常
        @ExceptionHandler(Exception.class)
    //    @ResponseBody
        public Result error(Exception e){
            e.printStackTrace();
            return Result.fail().message("执行了全局异常处理");
        }
    
        // 特定异常
        @ExceptionHandler(ArithmeticException.class)
    //    @ResponseBody
        public Result error(ArithmeticException e){
            e.printStackTrace();
            return Result.fail().message("执行了特定异常处理");
        }
    
        // 自定义异常
        @ExceptionHandler(GgktException.class)
    //    @ResponseBody
        public Result error(GgktException e){
            e.printStackTrace();
            return Result.fail().message(e.getMsg()).code(e.getCode());
        }
    }
    
    • 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

    Result: 对于上述的异常处理的返回处理

    @Data
    public class Result<T> {
    
        private Integer code;
    
        private String message;
    
        private T data;
    
        public Result(){}
    
        public static <T> Result<T> build(T body, Integer code, String message) {
            Result<T> result = new Result<T>();
            if (body != null) {
                result.setData(body);
            }
            result.setCode(code);
            result.setMessage(message);
            return result;
        }
    
        public static<T> Result<T> ok(){
            return Result.ok(null);
        }
    
        /**
         * 操作成功
         * @param data  baseCategory1List
         * @param 
         * @return
         */
        public static<T> Result<T> ok(T data){
            return build(data,20000,"成功");
        }
    
        public static<T> Result<T> fail(){
            return Result.fail(null);
        }
    
        /**
         * 操作失败
         * @param data
         * @param 
         * @return
         */
        public static<T> Result<T> fail(T data){
            return build(data, 20001,"失败");
        }
    
        public Result<T> message(String msg){
            this.setMessage(msg);
            return this;
        }
    
        public Result<T> code(Integer code){
            this.setCode(code);
            return this;
        }
    }
    
    • 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
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    这里通过一个自定义的异常加入到全局的异常处理中, 上面加了@ExceptionHandler的注解

    在这里插入图片描述

    3.自定义异常的原理

    由上一篇可知, 异常解析器解析的过程中, 有3步, 第一步如果注解有@ExceptionHandler的话优先考虑这个方案。

    在此做一个解释。

    在这里插入图片描述

    因为ExceptionHandlerExceptionResolver实现了 InitializingBean这个接口, 所以实现了afterPropertiesSet方法

    在这里插入图片描述

    1. 首先会初始化ExceptionHandler的一些信息
    2. 加入参数解析器
    3. 加入返回值解析器

    在这里插入图片描述

    在这里插入图片描述

    1. 拿到所有含有@ControllerAdviceControllerAdviceBean
    2. 拿到这个类上标注了@ExceptionHandler的注解的方法
    3. 挨个解析

    .

    在这里插入图片描述
    找到所以含有@ControllerAdvice的类

    	public ExceptionHandlerMethodResolver(Class<?> handlerType) {
    		for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
    			for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
    				addExceptionMapping(exceptionType, method);
    			}
    		}
    	}
    	public static final MethodFilter EXCEPTION_HANDLER_METHODS = method ->
    			AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这里是找到@ExceptionHandler的类, 并缓存起来

    AbstractHandlerMethodExceptionResolver.doResolveException() -> ExceptionHandlerExceptionResolver.doResolveHandlerMethodException()

    在这里插入图片描述
    ExceptionHandlerExceptionResolver.doResolveHandlerMethodException()

    在这里插入图片描述
    这个方法遍历了所以的@ControllerAdvice, 看哪个类可以解析异常

  • 相关阅读:
    面试题 03.04. 化栈为队
    工艺防错指导、可视化工具管理——SunTorque智能扭矩系统
    [Unity 3d] 使用 Unity 开发无边框、可拖拽、缩放、置顶、最小化的应用
    【接口幂等性】使用token,Redis保证接口幂等性
    Axure常用技巧及问题
    .stream().map与.stream().flatMap的使用
    SpringBoot实用开发篇复习3
    Java基础32 this关键字
    多线程(二)多线程的锁机制(java)
    Linux内存管理(四):内存架构和内存模型简述
  • 原文地址:https://blog.csdn.net/qq_43141726/article/details/125890387