[Spring Boot] The use of interceptors and common functions are uniformly packaged

The hesitant devil 2022-08-06 19:32:32 阅读数:555

springbootuseinterceptorscommon

1. 拦截器

1.1 拦截器的使用

Spring 中提供了拦截器 HandlerInteceptor,Its specific use is divided into the following two steps:

  1. 创建自定义拦截器,实现 HandlerInteceptor 接口的 preHandle(执行具体方法之前的预处理)方法.
  2. 将自定义拦截器加入 WebMvcConfigurer 的 addInterceptors 方法中.

1.2 拦截器的原理

通过使用拦截器,The business execution process is generally as follows:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jk93rtZE-1659533770813)(C:/Users/bbbbbge/Pictures/接单/1659418795924.png)]

其中所有 controller The execution will go through a scheduler DispatcherServlet 来实现,And all methods will be executed 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 = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {

long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {

return;
}
}
// 调用预处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {

return;
}
// 执行 controller 中的业务
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);
}
}
}

Preprocessing and execution are marked in the above source code controller the location of the business,And preprocessing is the location of the interceptor source code we want to explore,其中使用了 applyPreHandle 方法,applyPreHandle 方法的源码如下:

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {

HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {

for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {

HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {

this.triggerAfterCompletion(request, response, (Exception)null);
return false;
}
}
}
return true;
}

We can find that all interceptors are obtained first in the preprocessing,并进行遍历,If the return value of the current interceptor is true 则不进行拦截,如果返回值为 false 则提前结束,In preprocessing, it will be straightforward return.

The above is the analysis of the interceptor source code,在 applyRreHandle will get all interceptors HandlerInterceptor,并执行拦截器的 preHandle 方法,This will correspond to a personal custom interceptor.

而本质上 Spring Interceptors in are also implemented through the idea of ​​dynamic proxies and wraparound notifications,大体的调用流程如下:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5FHiBxLL-1659533770815)(C:/Users/bbbbbge/Pictures/接单/1659421359875.png)]

2. 用户登录权限校验

  1. Defines the interceptor for user login permission verification.

    public class LoginInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    HttpSession session = request.getSession(false);
    if (session != null && session.getAttribute("user") != null){
    
    return true;
    }
    response.setStatus(401);
    return false;
    }
    }
    
  2. 将自定义拦截器加入到系统配置.

    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
    
    /** * Add interceptors and specify interception rules * @param registry */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    registry.addInterceptor(new LoginInterceptor())
    .addPathPatterns("/**") // 拦截所有请求
    .excludePathPatterns("/user/**"); // 放行 user 的请求
    }
    }
    

3. 统一异常处理

统一异常处理使用的是 @ControllerAdvice 和 @ExceptionHandler 来实现的,@ControllerAdvice 表示控制器通知类,@ExceptionHandler 是异常处理器,两个结合表示当出现异常的时候执行某个通知,That is to execute a method,具体实现步骤如下:

  1. 创建一个类,并在此类上添加 @ControllerAdvice@ResponseBody 注解.

    @ControllerAdvice // 表示控制器通知类
    @ResponseBody
    public class ExceptionAdvice {
    
    }
    
  2. Write methods that return data by exception,在该方法上加上 @ExceptionHandler 注解.

    @ControllerAdvice // 表示控制器通知类
    @ResponseBody
    public class ExceptionAdvice {
    
    @ExceptionHandler(Exception.class)
    public Object Exeception(Exception e){
    
    HashMap<String, Object> map = new HashMap<>();
    map.put("code", 0); // 状态码(设置0为异常,1为正常)
    map.put("data", null); // 返回数据
    map.put("msg", e.getMessage()); // 异常信息
    return map;
    }
    }
    
  3. 当有多个异常通知时,The matching order is that the existing current class and its subclasses are matched upwards.For example, the unified exception code is as follows:

    @ControllerAdvice // 表示控制器通知类
    @ResponseBody
    public class ExceptionAdvice {
    
    @ExceptionHandler(Exception.class)
    public Object Exeception(Exception e){
    
    HashMap<String, Object> map = new HashMap<>();
    map.put("code", 0); // 状态码(设置0为异常,1为正常)
    map.put("data", null); // 返回数据
    map.put("msg", "总的异常信息:" + e.getMessage()); // 异常信息
    return map;
    }
    @ExceptionHandler(ArithmeticException.class)
    public Object ArithmeticException(ArithmeticException e){
    
    HashMap<String, Object> map = new HashMap<>();
    map.put("code", 0);
    map.put("data", null);
    map.put("msg", "Arithmetic exception information:" + e.getMessage());
    return map;
    }
    }
    

    When an arithmetic exception occurs,返回的结果应该是 ArithmeticException Exception notified by the method.

4. 统一数据返回格式

The format of unified data can be encapsulated manually,代码如下:

@Data
public class Response implements Serializable {

private int code; // 状态码(-1-失败 0-成功 1-空)
private Object data; // 数据
private String msg; // 异常信息
public static String success(Object data){

Response response = new Response();
response.code = 0;
response.data = data;
response.msg = null;
return JSON.toJSONString(response);
}
public static String success(){

Response response = new Response();
response.code = 0;
response.data = null;
response.msg = null;
return JSON.toJSONString(response);
}
public static String fail(String msg){

Response response = new Response();
response.code = -1;
response.data = null;
response.msg = msg;
return JSON.toJSONString(response);
}
public static String fail(){

Response response = new Response();
response.code = -1;
response.data = null;
response.msg = null;
return JSON.toJSONString(response);
}
public static String Empty(){

Response response = new Response();
response.code = 1;
response.data = null;
response.msg = "没有找到数据!";
return JSON.toJSONString(response);
}
}

也可以通过 @ControllerAdvice 注解和实现 ResponseBodyAdvice interface to achieve automatic encapsulation.实现方式如下:

  1. 创建一个类,add to the class @ControllerAdvice 注解,并实现 ResponseBodyAdvice 接口.

    @ControllerAdvice
    public class MyResponseBodyAdvice implements ResponseBodyAdvice {
    
    }
    
  2. 重写 ResponseBodyAdvice 接口的两个方法.

    • supports The method indicates whether to rewrite the data before returning it,返回值为 true 表示重写.
    • beforeBodyWrite The method is used to override the data returned by the method,The data is encapsulated and returned to the front end.
    @ControllerAdvice
    public class MyResponseBodyAdvice implements ResponseBodyAdvice {
    
    /** * 将 supports 的返回值设置为 true to indicate overwriting the data before returning it */
    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
    
    return true;
    }
    /** * Encapsulates the data to be rewritten */
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
    
    HashMap<String, Object> map = new HashMap<>();
    map.put("code", 1);
    map.put("data", o);
    map.put("msg", null);
    return map;
    }
    }
    
copyright:author[The hesitant devil],Please bring the original link to reprint, thank you. https://en.javamana.com/2022/218/202208061926209037.html