0


SpringBoot—统一功能处理

SpringBoot—统一功能处理

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

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

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

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

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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

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

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

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

Spring 拦截器的使用🍂

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

自定义拦截器

在这里插入图片描述

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

  • addPathPatterns, 表示需要拦截的 URL (/*表示一级路由, /**表示所有的请求)
  • excludePathPatterns, 表示不需要拦截的 URL在这里插入图片描述

拦截器的实现原理

调用方法时, 发现 DispatcherServlet

Dispatcher → 调度器

在这里插入图片描述

所有方法都会执行 DispatcherServlet 中的 doDispatch—调度方法

拦截器(doDispatch)的实现源码🌰

protectedvoiddoDispatch(HttpServletRequest request,HttpServletResponse response)throwsException{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((newServletWebRequest(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 =newNestedServletException("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,newNestedServletException("Handler processing failed", var23));}}finally{if(asyncManager.isConcurrentHandlingStarted()){if(mappedHandler !=null){
                 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}elseif(multipartRequestParsed){this.cleanupMultipart(processedRequest);}}}

在这里插入图片描述

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

在这里插入图片描述

applyPreHandle 的源码🌰

booleanapplyPreHandle(HttpServletRequest request,HttpServletResponse response)throwsException{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);returnfalse;}}returntrue;}

分析源码🍂

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

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

在这里插入图片描述

统⼀访问前缀添加

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

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

重写 configurePathMatch( ) 🍂

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

在这里插入图片描述

在配置文件中添加🍂

server:servlet:context-path: /bibubibu

在这里插入图片描述

🔎统一异常的处理

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

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

未设置异常处理🍂

在这里插入图片描述

NullPointerException

在这里插入图片描述

ArithmeticException

在这里插入图片描述

设置异常处理🍂

@ControllerAdvice@ResponseBodypublicclassMyExceptionHandler{/**
    * 拦截所有空指针异常, 进行统一数据格式的返回
    * @author bibubibu
    * @date 2023/7/9
    */@ExceptionHandler(NullPointerException.class)publicHashMap<String,Object>nullPointerException(NullPointerException e){HashMap<String,Object> map =newHashMap<>();
        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)publicHashMap<String,Object>arithmeticException(ArithmeticException e){HashMap<String,Object> map =newHashMap<>();
        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)publicHashMap<String,Object>exception(Exception e){HashMap<String,Object> map =newHashMap<>();
        map.put("status",-1);
        map.put("data",null);
        map.put("msg","Exception"+ e.getMessage());return map;}}

在这里插入图片描述

NullPointerException

在这里插入图片描述

ArithmeticException

在这里插入图片描述

🔎统一数据格式的返回

统一数据格式返回的优点

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

统一数据格式返回的实现

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

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

@RestController@RequestMapping("/user")publicclassUserController{@RequestMapping("get-num")publicIntegergetNumber(){return(int)(Math.random()*10+1);}}

在这里插入图片描述

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

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

supports() 类似于一个开关
当返回值为 true 时, 开启 beforeBodyWrite() 中编写的相关功能
当返回值为 false 时, 关闭 beforeBodyWrite() 中编写的相关功能

@ControllerAdvicepublicclassResponseAdviceimplementsResponseBodyAdvice{@Overridepublicbooleansupports(MethodParameter returnType,Class converterType){returntrue;}@OverridepublicObjectbeforeBodyWrite(Object body,MethodParameter returnType,MediaType selectedContentType,Class selectedConverterType,ServerHttpRequest request,ServerHttpResponse response){HashMap<String,Object> map =newHashMap<>();
        map.put("status",200);
        map.put("data", body);// 此处的 Body 是 String 类型会出错
        map.put("msg","");return map;}}

在这里插入图片描述

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

@RestController@RequestMapping("/user")publicclassUserController{@RequestMapping("/get-user")publicStringgetUser(){System.out.println("执行 getUser()");return"getUser~~";}}

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

在这里插入图片描述

类型转换异常

在这里插入图片描述

解决方法

当调用方法的返回值类型为 String 时, 利用 jackson 完成类型转换

@ControllerAdvicepublicclassResponseAdviceimplementsResponseBodyAdvice{// 利用 jackson 转换 String@AutowiredprivateObjectMapper objectMapper;@Overridepublicbooleansupports(MethodParameter returnType,Class converterType){returntrue;}@OverridepublicObjectbeforeBodyWrite(Object body,MethodParameter returnType,MediaType selectedContentType,Class selectedConverterType,ServerHttpRequest request,ServerHttpResponse response){HashMap<String,Object> map =newHashMap<>();
        map.put("status",200);
        map.put("data", body);// 此处的 Body 是 String 类型会出错
        map.put("msg","");// 判断 Body 是否为 String 类型if(body instanceofString){// 是 String 类型, 将 map 转换为 Json 格式try{return objectMapper.writeValueAsString(map);}catch(JsonProcessingException e){
                e.printStackTrace();}}return map;}}

在这里插入图片描述

在这里插入图片描述

🔎总结

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

🌸🌸🌸完结撒花🌸🌸🌸

在这里插入图片描述

标签: spring boot java 后端

本文转载自: https://blog.csdn.net/m0_74365243/article/details/131582741
版权归原作者 哔卟哔卟_: ) 所有, 如有侵权,请联系我们删除。

“SpringBoot—统一功能处理”的评论:

还没有评论