SpringBoot—统一功能处理

03-13 阅读 0评论

SpringBoot—统一功能处理

  • 🔎小插曲(通过一级路由调用多种方法)
  • 🔎使用拦截器实现用户登录权限的统一校验
    • 自定义拦截器
    • 将自定义拦截器添加至配置文件中
    • 拦截器的实现原理
    • 统⼀访问前缀添加
    • 🔎统一异常的处理
    • 🔎统一数据格式的返回
      • 统一数据格式返回的优点
      • 统一数据格式返回的实现
      • 🔎总结

        利用 AOP 的思想对一些特定的功能进行统一的处理, 包括

        • 使用拦截器实现用户登录权限的统一校验
        • 统一异常的处理
        • 统一数据格式的返回

          🔎小插曲(通过一级路由调用多种方法)

          通过一级路由调用多种方法, 需要保证这些方法的请求类型各不相同(GET, POST, PUT…)

          SpringBoot—统一功能处理

          SpringBoot—统一功能处理

          SpringBoot—统一功能处理

          🔎使用拦截器实现用户登录权限的统一校验

          使用 Spring AOP 可以实现统一拦截, 但 Spring AOP 的使用较为复杂, 包括

          1. 定义拦截的规则(切点表达式)较为复杂
          2. 在切面类中拿到 HttpSession 较为复杂

          于是 Pivotal 公司针对上述情况开发出 Spring 拦截器

          Spring 拦截器的使用🍂

          1. 自定义拦截器
            • 实现 HandlerInterceptor 接口
            • 重写 preHandler 方法, 在方法中编写业务代码
            • 将自定义拦截器添加至配置文件中, 并设置拦截的规则

          自定义拦截器

          SpringBoot—统一功能处理

          将自定义拦截器添加至配置文件中

          • addPathPatterns, 表示需要拦截的 URL

            (/*表示一级路由, /**表示所有的请求)

          • excludePathPatterns, 表示不需要拦截的 URL

            SpringBoot—统一功能处理

            拦截器的实现原理

            调用方法时, 发现 DispatcherServlet

            Dispatcher → 调度器

            SpringBoot—统一功能处理

            所有方法都会执行 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);
                     }
                 }
            }
            

            SpringBoot—统一功能处理

            当返回结果为 false 时, 拦截器将不会进行后续操作

            SpringBoot—统一功能处理

            applyPreHandle 的源码🌰

            boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
                for(int i = 0; i  
            

            分析源码🍂

            在 applyPreHandle 中会获取所有的拦截器 HandlerInterceptor, 并执行 HandlerInterceptor 中的 preHandle 方法

            即自定义拦截器中重写的 preHandle 方法

            SpringBoot—统一功能处理

            统⼀访问前缀添加

            统⼀访问前缀的添加有 2 种方式

            1. 重写 configurePathMatch( )
            2. 在配置文件中添加

            重写 configurePathMatch( ) 🍂

            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                configurer.addPathPrefix("/bibubibu", c -> true);
            }
            
            • /bibubibu, 要添加的统一前缀
            • c -> true, 所有请求均添加统一前缀

              SpringBoot—统一功能处理

              在配置文件中添加🍂

              server:
                servlet:
                  context-path: /bibubibu
              

              SpringBoot—统一功能处理

              🔎统一异常的处理

              统一异常的处理, 利用 2 个注解

              1. @ControllerAdvice → 感知异常
              2. @ExceptionHandler → 处理异常

              未设置异常处理🍂

              SpringBoot—统一功能处理

              NullPointerException

              SpringBoot—统一功能处理

              ArithmeticException

              SpringBoot—统一功能处理

              设置异常处理🍂

              @ControllerAdvice
              @ResponseBody
              public class MyExceptionHandler {
                  /**
                  * 拦截所有空指针异常, 进行统一数据格式的返回
                  * @author bibubibu
                  * @date 2023/7/9
                  */
                  @ExceptionHandler(NullPointerException.class)
                  public HashMap nullPointerException(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;
                  }
              }
              

              SpringBoot—统一功能处理

              NullPointerException

              SpringBoot—统一功能处理

              ArithmeticException

              SpringBoot—统一功能处理

              🔎统一数据格式的返回

              统一数据格式返回的优点

              1. 方便前端程序员更好的接收和解析后端数据接口返回的数据
              2. 降低前端程序员和后端程序员的沟通成本, 按照某个格式实现即可, 因为所有接口都是这样返回的
              3. 有利于项目统一数据的维护和修改
              4. 有利于后端技术部门统一规范的标准制定, 不会出现奇怪的返回内容

              统一数据格式返回的实现

              统一数据格式的返回, 利用注解 @ControllerAdvice + ResponseBodyAdvice(接口) 实现

              未设置统一数据格式的返回🍂

              @RestController
              @RequestMapping("/user")
              public class UserController {
                  @RequestMapping("get-num")
                  public Integer getNumber() {
                      return (int) (Math.random() * 10 + 1);
                  }
              }
              

              SpringBoot—统一功能处理

              设置统一数据格式的返回🍂

              1. 自定义类(ResponseAdvice), 添加 @ControllerAdvice 注解
              2. 实现 ResponseBodyAdvice 接口, 重写 supports() 与 beforeBodyWrite()

              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) {
                      HashMap map = new HashMap();
                      map.put("status", 200);
                      map.put("data", body); // 此处的 Body 是 String 类型会出错
                      map.put("msg", "");
                      return map;
                  }
              }
              

              SpringBoot—统一功能处理

              当方法的返回值类型为 String 时

              @RestController
              @RequestMapping("/user")
              public class UserController {
                  @RequestMapping("/get-user")
                  public String getUser() {
                      System.out.println("执行 getUser()");
                      return "getUser~~";
                  }
              }
              

              当调用方法的返回值类型为 String 时, 设置统一数据格式的返回🍂

              SpringBoot—统一功能处理

              类型转换异常

              SpringBoot—统一功能处理

              解决方法

              当调用方法的返回值类型为 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) {
                      HashMap map = 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;
                  }
              }
              

              SpringBoot—统一功能处理

              SpringBoot—统一功能处理

              🔎总结

              1. 用户登录权限的统一校验 → 实现 HandlerInterceptor 接口 + 重写 preHandler 方法 + 将自定义拦截器添加至配置文件中(实现 WebMvcConfigurer 接口)
              2. 统一访问前缀的添加 → 重写 configurePathMatch( ) / 在配置文件中添加
              3. 统一异常的处理 → 利用注解 @ControllerAdvice + @ExceptionHandler
              4. 统一数据格式的返回 → 利用注解 @ControllerAdvice + 实现接口 ResponseBodyAdvice

              🌸🌸🌸完结撒花🌸🌸🌸

              SpringBoot—统一功能处理


免责声明
本网站所收集的部分公开资料来源于AI生成和互联网,转载的目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。
文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

发表评论

快捷回复: 表情:
评论列表 (暂无评论,人围观)

还没有评论,来说两句吧...

目录[+]