利用 AOP 的思想对一些特定的功能进行统一的处理, 包括
通过一级路由调用多种方法, 需要保证这些方法的请求类型各不相同(GET, POST, PUT…)
使用 Spring AOP 可以实现统一拦截, 但 Spring AOP 的使用较为复杂, 包括
于是 Pivotal 公司针对上述情况开发出 Spring 拦截器
Spring 拦截器的使用🍂
(/*表示一级路由, /**表示所有的请求)
调用方法时, 发现 DispatcherServlet
Dispatcher → 调度器
所有方法都会执行 DispatcherServlet 中的 doDispatch—调度方法
拦截器(doDispatch)的实现源码🌰
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { try { ModelAndView mv = null; Object dispatchException = null; try { processedRequest = this.checkMultipart(request); multipartRequestParsed = processedRequest != request; mappedHandler = this.getHandler(processedRequest); if (mappedHandler == null) { this.noHandlerFound(processedRequest, response); return; } HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler()); String method = request.getMethod(); boolean isGet = HttpMethod.GET.matches(method); if (isGet || HttpMethod.HEAD.matches(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } this.applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception var20) { dispatchException = var20; } catch (Throwable var21) { dispatchException = new NestedServletException("Handler dispatch failed", var21); } this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException); } catch (Exception var22) { this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22); } catch (Throwable var23) { this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23)); } } finally { if (asyncManager.isConcurrentHandlingStarted()) { if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else if (multipartRequestParsed) { this.cleanupMultipart(processedRequest); } } }
当返回结果为 false 时, 拦截器将不会进行后续操作
applyPreHandle 的源码🌰
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) { HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i); if (!interceptor.preHandle(request, response, this.handler)) { this.triggerAfterCompletion(request, response, (Exception)null); return false; } } return true; }
分析源码🍂
在 applyPreHandle 中会获取所有的拦截器 HandlerInterceptor, 并执行 HandlerInterceptor 中的 preHandle 方法
即自定义拦截器中重写的 preHandle 方法
统⼀访问前缀的添加有 2 种方式
重写 configurePathMatch( ) 🍂
@Override public void configurePathMatch(PathMatchConfigurer configurer) { configurer.addPathPrefix("/bibubibu", c -> true); }
在配置文件中添加🍂
server: servlet: context-path: /bibubibu
统一异常的处理, 利用 2 个注解
未设置异常处理🍂
NullPointerException
ArithmeticException
设置异常处理🍂
@ControllerAdvice @ResponseBody public class MyExceptionHandler { /** * 拦截所有空指针异常, 进行统一数据格式的返回 * @author bibubibu * @date 2023/7/9 */ @ExceptionHandler(NullPointerException.class) public HashMapnullPointerException(NullPointerException e) { HashMap map = new HashMap<>(); map.put("status", -1); map.put("data", null); map.put("msg", "NullPointerException" + e.getMessage()); // 错误码的描述信息 return map; } /** * 拦截所有算数异常, 进行统一数据格式的返回 * @author bibubibu * @date 2023/7/9 */ @ExceptionHandler(ArithmeticException.class) public HashMap arithmeticException(ArithmeticException e) { HashMap map = new HashMap<>(); map.put("status", -1); map.put("data", null); map.put("msg", "ArithmeticException" + e.getMessage()); // 错误码的描述信息 return map; } /** * 拦截所有异常, 进行统一数据格式的返回 * @author bibubibu * @date 2023/7/9 */ @ExceptionHandler(Exception.class) public HashMap exception(Exception e) { HashMap map = new HashMap<>(); map.put("status", -1); map.put("data", null); map.put("msg", "Exception" + e.getMessage()); return map; } }
NullPointerException
ArithmeticException
统一数据格式的返回, 利用注解 @ControllerAdvice + ResponseBodyAdvice(接口) 实现
未设置统一数据格式的返回🍂
@RestController @RequestMapping("/user") public class UserController { @RequestMapping("get-num") public Integer getNumber() { return (int) (Math.random() * 10 + 1); } }
设置统一数据格式的返回🍂
supports() 类似于一个开关
当返回值为 true 时, 开启 beforeBodyWrite() 中编写的相关功能
当返回值为 false 时, 关闭 beforeBodyWrite() 中编写的相关功能
@ControllerAdvice public class ResponseAdvice implements ResponseBodyAdvice { @Override public boolean supports(MethodParameter returnType, Class converterType) { return true; } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { HashMapmap = new HashMap<>(); map.put("status", 200); map.put("data", body); // 此处的 Body 是 String 类型会出错 map.put("msg", ""); return map; } }
当方法的返回值类型为 String 时
@RestController @RequestMapping("/user") public class UserController { @RequestMapping("/get-user") public String getUser() { System.out.println("执行 getUser()"); return "getUser~~"; } }
当调用方法的返回值类型为 String 时, 设置统一数据格式的返回🍂
类型转换异常
解决方法
当调用方法的返回值类型为 String 时, 利用 jackson 完成类型转换
@ControllerAdvice public class ResponseAdvice implements ResponseBodyAdvice { // 利用 jackson 转换 String @Autowired private ObjectMapper objectMapper; @Override public boolean supports(MethodParameter returnType, Class converterType) { return true; } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { HashMapmap = new HashMap<>(); map.put("status", 200); map.put("data", body); // 此处的 Body 是 String 类型会出错 map.put("msg", ""); // 判断 Body 是否为 String 类型 if(body instanceof String) { // 是 String 类型, 将 map 转换为 Json 格式 try { return objectMapper.writeValueAsString(map); } catch (JsonProcessingException e) { e.printStackTrace(); } } return map; } }
🌸🌸🌸完结撒花🌸🌸🌸