SpringBoot统一处理功能实现的全过程

作者:我叫小八 时间:2022-12-24 09:43:26 

在处理网络请求时,有一部分功能是需要抽出来统一处理的,与业务隔开。

登录校验

可以利用spring mvc的 * Interceptor,实现HandlerInterceptor接口即可。实现该接口后,会在把请求发给Controller之前进行拦截处理。

* 的实现分为以下两个步骤:

  • 创建⾃定义 * ,实现 HandlerInterceptor 接⼝的 preHandle(执⾏具体⽅法之前的预处理)⽅法。

  • 将⾃定义 * 加⼊ WebMvcConfigurer 的 addInterceptors ⽅法中。

我们使用session来作为登录校验的例子,实现如下:

package com.demo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* 登录 *
*/
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
   /**
    * 为 false 则不能继续往下执行;为 true 则可以。
    */
   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
       // 判断session的信息是否合法
       HttpSession session = request.getSession(false);
       if (session != null && session.getAttribute("userinfo") != null) {
           return true;
       }
       log.error("当前用户没有访问权限");
       response.setStatus(401);
       return false;
   }
}

将写好的⾃定义 * 通过WebMvcConfigurer注册到容器中,使得 * 生效,具体实现代码如下:

package com.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyConfig implements WebMvcConfigurer {
   @Autowired
   private LoginInterceptor loginInterceptor;
   @Override
   public void addInterceptors(InterceptorRegistry registry) {
       registry.addInterceptor(loginInterceptor)
               .addPathPatterns("/**") // 拦截所有请求
               .excludePathPatterns("/user/login"); // 排除不拦截的 url
   }
}

如果不注入对象的话,addInterceptor() 的参数也可以直接 new 一个对象:

@Configuration // 一定不要忘记
public class MyConfig implements WebMvcConfigurer {
   @Override
   public void addInterceptors(InterceptorRegistry registry) {
       registry.addInterceptor(new LoginInterceptor())
               .addPathPatterns("/**") // 拦截所有请求
               .excludePathPatterns("/user/login"); // 排除不拦截的 url
   }
}

原理

所有的 Controller 执⾏都会通过spring mvc的调度器 DispatcherServlet 来实现,所有⽅法都会执⾏ 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 {
               // ...  忽略不重要的代码
               // 调⽤预处理
               if (!mappedHandler.applyPreHandle(processedRequest, respon
                       se)) {
                   return;
               }
               // 执⾏ Controller 中的业务
               mv = ha.handle(processedRequest, response, mappedHandler.g
                       etHandler());
              // ...  忽略不重要的代码
           } catch (Exception var20) {
               dispatchException = var20;
           } catch (Throwable var21) {
               dispatchException = new NestedServletException("Handler di
                       spatch failed", var21);
           }
           this.processDispatchResult(processedRequest, response, mappedH
                   andler, mv, (Exception)dispatchException);
       } catch (Exception var22) {
           this.triggerAfterCompletion(processedRequest, response, mapped
                   Handler, var22);
       } catch (Throwable var23) {
           this.triggerAfterCompletion(processedRequest, response, mapped
                   Handler, new NestedServletException("Handler processing failed", var23));
       }
   } finally {
       if (asyncManager.isConcurrentHandlingStarted()) {
           if (mappedHandler != null) {
               mappedHandler.applyAfterConcurrentHandlingStarted(processe
                       dRequest, response);
           }
       } else if (multipartRequestParsed) {
           this.cleanupMultipart(processedRequest);
       }
   }
}

从上述源码可以看出在开始执⾏ Controller 之前,会先调⽤ 预处理⽅法 applyPreHandle,⽽ applyPreHandle ⽅法的实现源码如下:

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
   for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex
           = i++) {
       // 获取项⽬中使⽤的 * HandlerInterceptor
       HandlerInterceptor interceptor = (HandlerInterceptor)this.intercep
       torList.get(i);
       if (!interceptor.preHandle(request, response, this.handler)) {
           this.triggerAfterCompletion(request, response, (Exception)null
           );
           return false;
       }
   }
   return true;
}

异常处理

请求时的异常处理也是比较常见的统一处理的对象。

统⼀异常处理使⽤的是 @ControllerAdvice + @ExceptionHandler 来实现的,@ControllerAdvice 表示控制器通知类,@ExceptionHandler 是异常处理器,两个结合表示当出现异常的时候执⾏某个通知,也就是执⾏某个⽅法事件,具体实现代码如下:

package com.demo;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
/**
* 统一处理异常
* 一般都需要自定义一个异常对象,这里为了简单说明只用一个map对象来说明
*/
@ControllerAdvice
public class ErrorAdive {
   @ExceptionHandler(Exception.class)
   @ResponseBody
   public HashMap<String, Object> exceptionAdvie(Exception e) {
       HashMap<String, Object> result = new HashMap<>();
       result.put("code", "-1");
       result.put("msg", e.getMessage());
       return result;
   }
   @ExceptionHandler(ArithmeticException.class)
   @ResponseBody
   public HashMap<String, Object> arithmeticAdvie(ArithmeticException e) {
       HashMap<String, Object> result = new HashMap<>();
       result.put("code", "-2");
       result.put("msg", e.getMessage());
       return result;
   }
}

此时若出现异常就不会报错了,代码会继续执行,但是会把自定义的异常信息返回给前端!

原理

@ControllerAdvice是spring的aop对于Controller进行切面所有属性的,包括切入点和需要织入的切面逻辑,配合@ExceptionHandler来捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的。

返回数据结构

统⼀的返回数据结构可以使用 @ControllerAdvice + ResponseBodyAdvice接口 的方式实现,具体实现代码如下:

package com.demo;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyA
dvice;
import java.util.HashMap;
/**
* 统一返回数据的处理
*/
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
   /**
    * 内容是否需要重写(通过此⽅法可以选择性部分控制器和⽅法进⾏重写)
    * 返回 true 表示重写
    */
   @Override
   public boolean supports(MethodParameter returnType, Class converterTyp
e) {
       return true;
   }
   /**
    * ⽅法返回之前调⽤此⽅法
    */
   @Override
   public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                 MediaType selectedContentType,
                                 Class selectedConverterType, ServerHttpR
                                         equest request,
                                 ServerHttpResponse response) {
       // 构造统⼀返回对象
       HashMap<String, Object> result = new HashMap<>();
       result.put("state", 1);
       result.put("msg", "");
       result.put("data", body);
       return result;
   }
}

来源:https://blog.csdn.net/h295928126/article/details/129655114

标签:SpringBoot,统一处理
0
投稿

猜你喜欢

  • Android之使用Android-query框架开发实战(二)

    2022-06-30 16:17:59
  • .NET单点登陆的实现方法及思路

    2023-06-10 10:13:53
  • Spring钩子接口汇总分析使用示例

    2022-09-24 23:29:56
  • C#子线程执行完后通知主线程的方法

    2022-02-26 20:15:40
  • js 交互在Flutter 中使用 webview_flutter

    2023-07-20 22:40:14
  • Android开发笔记之如何正确获取WebView的网页Title

    2022-06-04 07:02:41
  • Android架构组件Room的使用详解

    2022-04-30 14:59:24
  • 详解利用SpringCloud搭建一个最简单的微服务框架

    2023-08-21 04:24:32
  • 解决springboot生成bean名称冲突(AnnotationBeanNameGenerator)

    2023-01-09 22:27:11
  • 基于FeignException$InternalServerError的解决方案

    2023-04-25 15:50:45
  • flutter 屏幕尺寸适配和字体大小适配的实现

    2022-06-10 06:54:49
  • 浅谈JAVA中输入输出流实例详解

    2022-10-21 05:20:57
  • 必须了解的高阶JAVA枚举特性!

    2021-11-04 11:26:00
  • 深入学习Java单元测试(Junit+Mock+代码覆盖率)

    2021-06-17 00:39:13
  • Android三方依赖冲突Gradle中exclude的使用

    2023-05-05 05:09:56
  • Java 导出Excel增加下拉框选项

    2021-10-13 07:58:50
  • Java 字符终端上获取输入三种的方式分享

    2021-12-31 04:52:45
  • Java 关于时间复杂度和空间复杂度的深度刨析

    2023-11-10 16:07:39
  • Spring MVC过滤器-登录过滤的代码实现

    2021-06-05 15:01:43
  • C#实现的文件压缩和解压缩类

    2022-11-30 16:08:08
  • asp之家 软件编程 m.aspxhome.com