SpringBoot统一处理功能实现的全过程
作者:我叫小八
在处理网络请求时,有一部分功能是需要抽出来统一处理的,与业务隔开。
登录校验
可以利用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; } }
到此这篇关于SpringBoot统一处理功能实现的全过程的文章就介绍到这了,更多相关SpringBoot统一处理功能内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!