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


猜你喜欢
- 本文实例总结了Android开发之Button事件实现与监听方法。分享给大家供大家参考,具体如下:先来介绍Button事件实现的两种方法ma
- 本文实例讲述了C#实现将DataTable内容输出到Excel表格的方法。分享给大家供大家参考。具体如下:1.关于本文本文描述了一个函数(S
- 本文实例讲述了C#针对xml文件转化Dictionary的方法。分享给大家供大家参考。具体实现方法如下:下面是xml文件:<?xml
- 1. Limit实现分页1.1 为什么需要分页 减少数据的处理量1.2 使用Limit实现分页select * from user limi
- 一般我们在controller层调用service时,只需要使用@Autowired注解即可,例如如下代码我们经常看到:@RestContr
- 在开发过程中,我们需要统一返回前端json格式的数据,但有些接口的返回值存在 null或者""这种没有意义的字段。不仅影
- 相应的类库可在我的资源页面中找到,关于类成员的说明可通过对象浏览器查看函数说明Imports BitOperatorLibrary.Shif
- 在web页面上我们可以通过frameset,iframe嵌套框架很容易实现各种导航+内容的布局界面,而在winform、WPF中实现其实也很
- 前排提示,我在这个工具类加了@Component注解,如果在springboot的项目使用,记得通过@Autowired注入使用。impor
- 一、在 AndroidManifest.xml文件中配置Widgets:<manifest xmlns:android="h
- 上一篇文章讲解了Spring Cloud 整合 nacos 实现服务注册与发现,nacos除了有服务注册与发现的功能,还有提供动态配置服务的
- 上周,公司的项目改版要求加上一个右滑返回上一个界面,于是就在网上找了一些开源库打算实现.但是在使用的时候遇见了许多的问题.试了两天用过 ht
- ViewFlipper实现文字轮播(仿淘宝头条垂直滚动广告),供大家参考,具体内容如下广告条目可以单独写成布局文件,然后在布局文件或者代码中
- 问题介绍:用二维数组表示一个迷宫,设置迷宫起点和终点,输出迷宫中的一条通路实现思路:二维数组表示迷宫:0表示路且未走过、1表示墙、2表示通路
- 问题:Information:java: Errors occurred while compiling module &lsquo
- 本文介绍了Android 仿微信自定义数字键盘的实现代码,分享给大家,希望对大家有帮助最终效果:实现这个自定义键盘的思路很简单:要写出一个数
- 数组的用处是什么呢?——当你需要将30个数进行大小排列的时候,用数组这样的数据结构存储是个很好的选择,当你是一个班的班主任的时候,每次要记录
- 1、问题描述几种代码写法会有不同的ID返回值,下面我们一一分析。2、问题分析 首先一种插入写法,源码如下:SysUser .java/**
- 最近几天一直在看Hadoop相关的书籍,目前稍微有点感觉,自己就仿照着WordCount程序自己编写了一个统计关联商品。需求描述:根据超市的
- 编程是一门艺术,大批量的改动显然是非常丑陋的做法,用心的琢磨写的代码让它变的更美观。在软件开发系统中,**“方法的请求者