Spring Boot 统一处理功能

目录

1.用户登陆权限验证

1.1 每个方法验证

1.2 Spring AOP 用户统一登陆验证

1.3 拦截器

1.3.1 自定义拦截器

1.3.2 将自定义拦截器配置到系统设置中,并且设置拦截规则

1.3.3 排除所有的静态资源

1.4 登录拦截器(练习)

1.5 拦截器原理

2.统一异常处理

3.统一数据返回格式

3.1 统一数据处理(强制执行)


统一处理的功能包括:

  • 统一用户登陆权限验证
  • 统一数据格式返回
  • 统一异常处理

1.用户登陆权限验证

之前学到的是在每个方法中验证用户登录权限,现在是统一的用户登录验证处理

1.1 每个方法验证

最初的验证方法

@RestController
@RequestMapping("/user")
public class UserController {
/*** 某⽅法 1*/@RequestMapping("/m1")public Object method(HttpServletRequest request) {//有 session 就获取,没有不会创建HttpSession session = request.getSession(false);if(session != null && session.getAttribute("userinfo") != null) {// 说明已经登录,业务处理return true;} else {// 未登录return false;}}
}

每个方法中都有相同的用户登录验证权限,它的缺点是:

  • 每个方法都要单独写一个用户登录验证的方法
  • 添加的控制器越多,调⽤⽤户登录验证的⽅法也越多,这样就增加了后期的修改成本和维护成本。
  • 这些⽤户登录验证的⽅法和接下来要实现的业务⼏何没有任何关联,但每个⽅法中都要写⼀遍

1.2 Spring AOP 用户统一登陆验证

通过前置通知或者环绕通知实现:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class UserAspect {// 定义切点⽅法 controller 包下、⼦孙包下所有类的所有⽅法@Pointcut("execution(* com.example.demo.controller..*.*(..))")public void pointcut(){ }// 前置⽅法@Before("pointcut()")public void doBefore(){}// 环绕⽅法@Around("pointcut()")public Object doAround(ProceedingJoinPoint joinPoint){Object obj = null;System.out.println("Around ⽅法开始执⾏");try {// 执⾏拦截⽅法obj = joinPoint.proceed();} catch (Throwable throwable) {throwable.printStackTrace();}System.out.println("Around ⽅法结束执⾏");return obj;}
}

缺点:

  • 无法获取 HttpSession 对象
  • 我们要对⼀部分方法机型拦截,⽽另⼀部分方法不拦截,如注册方法和登录⽅法是不拦截的,这样的话排除⽅法的规则很难定义,甚⾄没办法定义。 

1.3 拦截器

Spring 提供了具体的实现拦截器:HandlerInterceptor

  1. 创建自定义拦截器,实现 HandlerInterceptor 接口的 preHandle(执行具体方法之前的预处理)方法
  2. 将自定义拦截器加入 WebMvcConfigurer 的 addInterceptors 方法中

1.3.1 自定义拦截器

用户登陆拦截器

  • 实现 HandlerInterceptor 接口 的拦截器
  • 实现 preHandle(执行具体方法之前的预处理)方法:执行目标方法之前判断是否有登录
  • 重新 preHandle 方法:返回 true(拦截器验证成功,继续执行后续的方法);返回 false(拦截器验证失败,不会执行后续目标方法)
  • 写业务方法:得到 Session 对象,判断 Session 中是否有登陆用户,有——true;无——false
package com.example.demo.config;
import com.example.demo.common.AppVar;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/*** 自定义拦截器*/
public class UserInterceptor implements HandlerInterceptor {/*** 返回 true:表示拦截器验证成功,继续执行后续的方法*     false:拦截器验证失败,不会执行后续目标方法* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//业务方法//得到 Session 对象:判断 Session 中是否有登陆用户,有——true;无——falseHttpSession session = request.getSession(false);//默认 trueif (session != null && session.getAttribute(AppVar.SESSION_KEY) != null) {//用户已经登陆return true;}return false;}
}

定义 Session Key 值:

package com.example.demo.common;
//全局变量
public class AppVar {//Session Keypublic static final String SESSION_KEY = "SESSION_KEY";
}

1.3.2 将自定义拦截器配置到系统设置中,并且设置拦截规则

配置文件 AppConfig:

  • 实现 WebMvcConfigurer,表明是一个系统的配置文件
  • 重写 addInterceptors 方法(支持添加多个拦截器)

添加多个拦截器注入有两种方法:

1️⃣  

registry.addInterceptor(new UserInterceptor());

2️⃣ UserInterceptor 类中添加 @Component 注解,随着 spring 启动而启动;通过 @Autowired 注入进去

@Configuration 注解是随着 spring 启动而启动,将当前配置设置到系统中

package com.example.demo.config;
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.WebMvcConfigurer;
@Configuration
public class AppConfig implements WebMvcConfigurer {@Autowiredprivate UserInterceptor userInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//registry.addInterceptor(new UserInterceptor());registry.addInterceptor(userInterceptor).addPathPatterns("/**")//拦截规则:/**拦截所有的请求.excludePathPatterns("/user/reg")//不拦截.excludePathPatterns("/user/login");}
}

拦截规则:

  • addPathPatterns:表示需要拦截的 URL
  • /**:表示拦截任意方法(也就是所有方法)
  • excludePathPatterns:表示需要排除的 URL

新建 Controller 类(一个拦截、两个不拦截):

package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/getuser")public String getUser() {System.out.println("do getUser()");return "user";}@RequestMapping("/reg")public String reg() {System.out.println("do reg()");return "reg";}@RequestMapping("/login")public String login() {System.out.println("do login()");return "login";}
}

 在拦截器里边执行一个 do UserInterceptor:

@Component
public class UserInterceptor implements HandlerInterceptor {/*** 返回 true:表示拦截器验证成功,继续执行后续的方法*     false:拦截器验证失败,不会执行后续目标方法* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("do UserInterceptor");//业务方法//得到 Session 对象:判断 Session 中是否有登陆用户,有——true;无——falseHttpSession session = request.getSession(false);//默认 trueif (session != null && session.getAttribute(AppVar.SESSION_KEY) != null) {//用户已经登陆return true;}return false;}
}

运行启动类,首先我们先看不拦截的:访问 localhost:8080/user/reg

被拦截的:访问 localhost:8080/user/getuser

1.3.3 排除所有的静态资源

// 拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**") // 拦截所有接⼝.excludePathPatterns("/**/*.js").excludePathPatterns("/**/*.css").excludePathPatterns("/**/*.jpg").excludePathPatterns("/login.html").excludePathPatterns("/**/login"); // 排除接⼝
}
  • /*:一级目录
  • /**:所有目录

在这里有一个问题就是排除图片,图片的格式有很多,总不能一个一个排除,这样的情况我们可以在 resource 下的 static 创建一个 image 目录,把所有图片都放在这里边,排除 image 里边的所有东西

 .excludePathPatterns("/image/**");

1.4 登录拦截器(练习)

1.登录、注册页面不拦截,其他页面拦截

2.当登录成功写入 session 之后,拦截的页面也可以正常访问

1.拦截功能

package com.example.demo.config;
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.WebMvcConfigurer;@Configuration
public class AppConfig implements WebMvcConfigurer {@Autowiredprivate UserInterceptor userInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//registry.addInterceptor(new UserInterceptor());registry.addInterceptor(userInterceptor).addPathPatterns("/**")//拦截规则:/**拦截所有的请求.excludePathPatterns("/login.html").excludePathPatterns("/reg.html").excludePathPatterns("/css/**").excludePathPatterns("/editor/**").excludePathPatterns("/img/**").excludePathPatterns("/js/**").excludePathPatterns("/**/login"); // 排除接⼝;}
}

2.拦截器

package com.example.demo.config;
import com.example.demo.common.AppVar;
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
public class UserInterceptor implements HandlerInterceptor {/*** 返回 true:表示拦截器验证成功,继续执行后续的方法*     false:拦截器验证失败,不会执行后续目标方法* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("do UserInterceptor");//业务方法//得到 Session 对象:判断 Session 中是否有登陆用户,有——true;无——falseHttpSession session = request.getSession(false);//默认 trueif (session != null && session.getAttribute(AppVar.SESSION_KEY) != null) {//用户已经登陆return true;}return false;}
}

1.5 拦截器原理

所有的 Controller 执行都会通过⼀个调度器 DispatcherServlet 来实现,这⼀点可以从 Spring Boot 空制台的打印信息看出

而所有方法都会执行 DispatcherServlet 中的 doDispatch 调度方法,源码一部分:

//调用预处理【重点】
if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;
}

执行所有的拦截器:所有拦截器不等于 false,才会进行之后的代码

// 执⾏ Controller 中的业务
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {return;
}

所有的 Controller 执行都会通过⼀个调度器 DispatcherServlet 来实现,而所有方法都会执行 DispatcherServlet 中的 doDispatch 调度方法(其中会执行 applyPreHandle 方法,所有拦截器不等于 false,才会执行 Controller 方法)

applyPreHandle 源码:

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {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);return false;}}return true;}    

 执行一个目标方法的时候,循环所有的拦截器,拿到容器中的所有拦截器,再去执行拦截器的预执行方法(preHandle 方法),如果有一个 preHandle 方法为 false,就返回 false,就不执行后续流程,直接返回;返回true,执行 controller 方法

2.统一异常处理

统⼀异常处理使用的是 @ControllerAdvice + @ExceptionHandler 来实现的,@ControllerAdvice 表示控制器通知类,@ExceptionHandler 是异常处理器,两个结合表示当出现异常的时候执行某个通知,也就是执行某个方法事件

默认返回的是一个页面(需要的是一个数据不是页面),这个时候需要加 @ResponseBody ,返回一个数据,而 @ResponseBody 既可以加到方法上也可以加在类上,所以可以直接使用注解 @RestControllerAdvice

 假设构建一个空指针异常(UserController 类):

package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/reg")public String reg() {System.out.println("do reg()");Object obj = null;System.out.println(obj.hashCode());return "reg";}
}

定义统一的返回对象:

package com.example.demo.common;
import lombok.Data;
//统一的对象
@Data
public class ResultAjax {private int code;//状态码private String msg;//状态码的描述信息private Object data;//返回数据
}

空指针异常处理:

方法名和返回值可以自定义,其中最重要的是 @ExceptionHandler(Exception.class) 注解

package com.example.demo.config;
import com.example.demo.common.ResultAjax;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class ExceptionAdvice {@ExceptionHandler(NullPointerException.class)public ResultAjax doNullPointerException(NullPointerException e) {ResultAjax resultAjax = new ResultAjax();resultAjax.setCode(-1);resultAjax.setMsg("空指针异常:" + e.getMessage());resultAjax.setData(null);return resultAjax;}
}

访问 localhost:8080/user/reg

上述为空指针异常,那么异常有很多,我们难道全要写这些异常?

这个时候我们可以设置一个默认异常,所有异常都是基于 Exception

@RestControllerAdvice
public class ExceptionAdvice {@ExceptionHandler(Exception.class)public ResultAjax doException(Exception e) {ResultAjax resultAjax = new ResultAjax();resultAjax.setCode(-1);resultAjax.setMsg("异常:" + e.getMessage());resultAjax.setData(null);return resultAjax;}
}

3.统一数据返回格式

  • 方便前端程序员更好的接收和解析后端数据接口返回的数据
  • 降低前端程序员和后端程序员的沟通成本
  • 有利于项⽬统⼀数据的维护和修改
  • 有利于后端技术部⻔的统⼀规范的标准制定,不会出现稀奇古怪的返回内容

3.1 统一数据处理(强制执行)

  • 使用 @ControllerAdvice
  • 实现 ResponseBodyAdvice 接口,并重写它的两个方法,supports 必须返回 true,beforeBodyWrite 方法中进行重新判断和重写操作
package com.example.demo.config;
import com.example.demo.common.ResultAjax;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
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.ResponseBodyAdvice;
/*** 听译返回值的保底实现类*/
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {@Autowiredprivate ObjectMapper objectMapper;//spring 自带的/*** true -> 才会调用 beforeBodyWrite 方法* 反之则永远不会调用 beforeBodyWrite 方法* @param returnType* @param converterType* @return*/@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType,MediaType selectedContentType, Class selectedConverterType,ServerHttpRequest request, ServerHttpResponse response) {// 已经包装好的对象if (body instanceof ResultAjax) {return body;}//对字符串进行判断和处理if (body instanceof String) {ResultAjax resultAjax = ResultAjax.succ(body);try {return objectMapper.writeValueAsString(resultAjax);} catch (JsonProcessingException e) {e.printStackTrace();}}return ResultAjax.succ(body);}
}
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/getnum")public int getNum() {return 1;}@RequestMapping("/getstr")public String getStr() {return "hello";}
}

String 比较特殊,既不属于对象,也不属于基本数据类型,在返回值的时候也是需要单独判断

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/163580.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在Windows或Mac上安装并运行LLAMA2

LLAMA2在不同系统上运行的结果 LLAMA2 在windows 上运行的结果 LLAMA2 在Mac上运行的结果 安装Llama2的不同方法 方法一&#xff1a; 编译 llama.cpp 克隆 llama.cpp git clone https://github.com/ggerganov/llama.cpp.git 通过conda 创建或者venv. 下面是通过conda 创建…

大语言模型研究进展综述

1、历史 自20世纪50年代图灵测试被提出以来&#xff0c;研究人员一直在探索和开发能够理解并掌握语言的人工智能技术。 作为重要的研究方向之一&#xff0c;语言模型得到了学术界的广泛研究&#xff0c;从早期的统计语言模型和神经语言模型开始&#xff0c;发展到基于Transform…

线上 kafka rebalance 解决

上周末我们服务上线完毕之后发生了一个kafka相关的异常&#xff0c;线上的kafka频繁的rebalance&#xff0c;详细的报错我已经贴到下面&#xff0c;根据字面意思&#xff1a;消费者异常 org.apache.kafka.clients.consumer.CommitFailedException: 无法完成提交&#xff0c;因为…

信创背景下,IAM统一身份管理联合国产身份域管,助力央国企构建数字身份新底座

数字身份底座是企业数字化的基石。数字时代&#xff0c;IAM 统一身份管理系统结合微软 AD 是央国企、金融机构数字身份建设的通用架构。国产化改造浪潮中&#xff0c;国外身份管理系统&#xff08;如微软 AD&#xff09;无法适配国产化异构 IT 环境。从业务和安全角度考虑&…

系列十一、拦截器(二)#案例演示

一、案例演示 说明&#xff1a;如下案例通过springboot的方式演示拦截器是如何使用的&#xff0c;以获取Controller中的请求参数为切入点进行演示 1.1、前置准备工作 1.1.1、pom <dependencies><!-- spring-boot --><dependency><groupId>org.spring…

DockerFile常用保留字指令及知识点合集

目录 DockerFile加深理解&#xff1a; DockerFile常用保留字指令 保留字&#xff1a; RUN&#xff1a;容器构建时需要运行的命令 COPY&#xff1a;类似ADD&#xff0c;拷贝文件和目录到镜像中。 将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 …

初识面向对象(类和对象)

目录 1. 面向对象的初步认知 2.面向对象与面向过程 3.类定义和使用 4.类的定义格式 练习 5.类的实例化 什么是实例化 6.this引用 为什么要有this引用 什么是this引用 this引用的特性 7.对象的初始化 默认初始化 就地初始化 使用构造方法初始化 1. 面向对象的初步…

JavaSE 再续篇-面试题:this 与 super 关键字的区别

&#x1f525;博客主页&#xff1a; 小扳_-CSDN博客 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 this 与 super 关键字的区别 1.1 在Java中&#xff0c;this 关键字有以下作用 1.2 在Java中&#xff0c;super 关键字有以下作用 2.0 经典习题 1.0 this 与 su…

Mac版eclipse如何安装,运行bpmn文件

一、下载程序包 网址&#xff1a;https://www.eclipse.org/downloads M2芯片安装包名称&#xff1a;eclipse-jee-2022-12-R-macosx-cocoa-aarch64.dmg 具体安装包版本根据自己电脑型号选择 二、eclipse安装步骤 1&#xff09;双击下载的文件 2&#xff09;将eclipse拖入到…

spring报错 @EnableAsync annotation metadata was not injected

报错 报错 internalAsyncAnnotationProcessor 这个spring内部的后处理器 创建失败&#xff0c;进而导致 EnableAsync 注解元数据没有注入容器 分析问题 查了 博客 是配置类放到原始项目路径下导致的问题。 博主的路径虽然正确&#xff0c;但发现是相似的问题&#xff0c;最…

项目流程管理工具 OmniPlan Pro 4 mac中文版软件介绍

OmniPlan Pro mac是 The Omni Group 为 macOS 和 iOS 操作系统开发的项目管理软件。它允许用户创建和管理复杂的项目、定义任务、分配资源、跟踪进度和生成报告。 OmniPlan Pro mac包括甘特图、网络图、关键路径分析、资源均衡和成本跟踪等功能。 借助 OmniPlan Pro&#xff0…

maven 上传本地jar包到nexus

maven上传命令 mvn deploy:deploy-file -DgroupIdcom.microsoft.sqlserver -DartifactIdsqljdbc4 -Dversion4.0 -Dpackagingjar -DfileC:\java\top-sdk-java-1.0.1-lib\lib\bcprov-jdk16-1.46.jar -Durlhttp://ip:port/repository/maven-releases/ -DrepositoryIdsnapshot…