- 0.前记
- 1.自定义视图
- 2.自定义异常
- 3.自定义异常的原理
在前面的文章中, 表示了视图解析的原理和异常解析器的解析原理。
这篇通过如何自定义视图和自定义异常处理和自定义异常处理的原理进行说明。
这里说明一下, 自定义的视图和自定义的异常都是会代替容器默认的组件的, 异常还好说, 就是不符合就抛, 视图的话需要注意一下优先级, 可以在自定义的视图解析器上加上@Order注解。
1.自定义视图MeiNvView.java
public class MeiNvView implements View {
@Override
public void render(Map 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());
}
}
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;
}
}
HelloController.java
@GetMapping("/meinv")
public String meinv(String name, Model model){
model.addAttribute("info", "001");
return "meinv:" + name;
}
这里原理就是添加一个视图和视图解析器, 然后放入容器中, 最后访问相应的路径就可以。
原理解释请看之前的文章
GgktException : 自定义异常类
@Data
@AllArgsConstructor
@NoArgsConstructor
// 自定义异常
public class GgktException extends RuntimeException {
private Integer code;
private String msg;
}
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());
}
}
Result: 对于上述的异常处理的返回处理
@Data public class Result{ private Integer code; private String message; private T data; public Result(){} public static Result build(T body, Integer code, String message) { Result result = new Result (); if (body != null) { result.setData(body); } result.setCode(code); result.setMessage(message); return result; } public static Result ok(){ return Result.ok(null); } public static Result ok(T data){ return build(data,20000,"成功"); } public static Result fail(){ return Result.fail(null); } public static Result fail(T data){ return build(data, 20001,"失败"); } public Result message(String msg){ this.setMessage(msg); return this; } public Result code(Integer code){ this.setCode(code); return this; } }
这里通过一个自定义的异常加入到全局的异常处理中, 上面加了@ExceptionHandler的注解
3.自定义异常的原理由上一篇可知, 异常解析器解析的过程中, 有3步, 第一步如果注解有@ExceptionHandler的话优先考虑这个方案。
在此做一个解释。
因为ExceptionHandlerExceptionResolver实现了 InitializingBean这个接口, 所以实现了afterPropertiesSet方法
- 首先会初始化ExceptionHandler的一些信息
- 加入参数解析器
- 加入返回值解析器
- 拿到所有含有@ControllerAdvice的 ControllerAdviceBean
- 拿到这个类上标注了@ExceptionHandler的注解的方法
- 挨个解析
.
找到所以含有@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);
这里是找到@ExceptionHandler的类, 并缓存起来
AbstractHandlerMethodExceptionResolver.doResolveException() -> ExceptionHandlerExceptionResolver.doResolveHandlerMethodException()
ExceptionHandlerExceptionResolver.doResolveHandlerMethodException()
这个方法遍历了所以的@ControllerAdvice, 看哪个类可以解析异常



