springboot的统一处理
创始人
2025-05-30 20:16:05
0

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

登录校验

可以利用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 则可以。*/ @Overridepublic 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 {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/**") // 拦截所有请求.excludePathPatterns("/user/login"); // 排除不拦截的 url}
}

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

@Configuration // 一定不要忘记
public class MyConfig implements WebMvcConfigurer {@Overridepublic 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, HttpServletResponseresponse) 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, response)) {return;}// 执⾏ Controller 中的业务mv = ha.handle(processedRequest, response, mappedHandler.getHandler());// ...  忽略不重要的代码} 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);}}
}

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

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex= i++) {// 获取项⽬中使⽤的拦截器 HandlerInterceptorHandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.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)@ResponseBodypublic HashMap exceptionAdvie(Exception e) {HashMap result = new HashMap<>();result.put("code", "-1");result.put("msg", e.getMessage());return result;}@ExceptionHandler(ArithmeticException.class)@ResponseBodypublic HashMap arithmeticAdvie(ArithmeticException e) {HashMap 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 表示重写*/@Overridepublic boolean supports(MethodParameter returnType, Class converterTyp
e) {return true;}/*** ⽅法返回之前调⽤此⽅法*/@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType,MediaType selectedContentType,Class selectedConverterType, ServerHttpRequest request,ServerHttpResponse response) {// 构造统⼀返回对象HashMap result = new HashMap<>();result.put("state", 1);result.put("msg", "");result.put("data", body);return result;}
}

相关内容

热门资讯

建筑合同范本-英文建筑合同样本... Party A:Party B:Contract NoDate:Signed at:Witnesse...
建筑合同范本-园林古建筑合同样... 发包方:(以下简称甲方)承包方:(以下简称乙方)经甲、乙双方协商一致,就甲方委托乙方建造兰埔花园园林...
建筑合同范本-土地使用权租赁协... 甲方:_________集团公司  住所:_________省_________市_________...
建筑合同范本-农村土地出租排水... 甲方:_________  法定代表人:_________  住址:_________  邮编:__...
合同范本-委托开发合同书样本 ... 甲方委托乙方研制_________装置,特商定如下合同:1.研制内容:_________。2.技术要...
Python爬虫——Pytho... Beautiful Soup 简称 BS4(其中 4 表示版本号)是一个...
【linux】基本指令详解 文章目录【Linux】1. 基本指令详解前言--Linux操作系统由来1.1Linux发展史1.2L...
合同范本-技术培训合同样本 技... 委托方:_________??法定代表人或负责人:________??服务方:_______??法定...
合同范本-科技查新合同样本 房... 合同编号:查新项目名称中文:英文:委托单位名称:通信地址:邮政编码:电子信箱:负责人电话:传真:联系...
合同范本-软件外包合同范本样本... 甲方:_________乙方:_________(个人)身份证号码:________________...
Git 和 GitHub 超入... 工作流程 工作的流程应该遵循以下步骤: (1) 在 issue 跟踪系统中创建一个新的...
技术服务合同(含技术培训、技术... 项目名称:____________________________________________委...
转让技术秘密和补偿贸易合作生产... 甲方:____________________________________地址:________...
【Git】git环境如何搭建与... 搭建 Git 环境: 安装 Git 客户端:根据操作系统选择对应的版本进...
day36_jdbc 今日内容 上课同步视频:CuteN饕餮的个人空间_哔哩哔哩_bilibili 同步笔记沐沐霸的博客...
与梦想有关的哲理说说短语 梦想...   01、 我就是我是颜色不一样的烟火天空海阔要做最坚强的泡沫。  02、 不论你在什么时候开始,重...
激励人生的励志签名致自己 激励...   01、 农村三驴子,进城。‰  02、 ≮问世间情为何物,一物降一物。≯  03、 上学的心情,...
青春励志个性签名正能量 励志个...   01、 还喜欢 还在意 但不渴望在一起  02、 努力吧一意孤行的公主  03、 让人耗尽心力的...
样本-合同范本-中外专有技术许... 中外专有技术许可合同签约时间:签字地点:合同号:中国,北京,×××公司(以下简称“受让方”)为一方,...
人生格言个性励志签名 微信励志...   01、 如果迩是棉花糖机,莪愿融化真心,旋转成手心旳云。  02、 人活着不是要用眼泪博得同情,...