Spring 统一处理(JavaEE进阶系列6)

目录

前言:

1.用户登录权限的校验

2.Spring拦截器

2.1自定义拦截器

2.2将自定义拦截器加入到系统配置

2.3拦截器练习

2.4拦截器的实现原理

3.统一异常处理

4.统一数据的返回格式

结束语:


前言:

接下来就是Spring Boot中的统一功能处理模块了,也就是AOP的实战环节,这节中我们主要学习的有以下三点:1.统一用户登录权限验证。2.统一数据格式返回。3.统一异常处理。话不多说我们一起来了解吧!

1.用户登录权限的校验

用户登录权限的发展从之前每个方法中自己验证用户登录权限,到现在的统一的用户登录验证处理,它是一个逐渐完成和逐渐优化的过程。

最初我们实现用户登录功能的时候会有很多缺点,在每个方法中都有相同的用户登录验证权限:

  • 每个⽅法中都要单独写⽤户登录验证的⽅法,即使封装成公共⽅法,也⼀样要传参调⽤和在⽅法中进⾏判断。
  • 添加控制器越多,调⽤⽤户登录验证的⽅法也越多,这样就增加了后期的修改成本和维护成本。
  • 这些⽤户登录验证的⽅法和接下来要实现的业务⼏何没有任何关联,但每个⽅法中都要写⼀遍。所以提供⼀个公共的 AOP ⽅法来进⾏统⼀的⽤户登录权限验证迫在眉睫。

在我们之前学习过Spring AOP之后,如果再次提到要实现统一用户登录验证的时候,我们的第一想法应该就是Spring AOP的前置通知或通过环绕通知来实现。

但是使用我们之前的的方法来实现的话,会发现存在两个问题:

  • 没有办法获取到HttpSession对象。
  • 我们要对一部分方法进行拦截,而另一部分方法不拦截,如注册方法和登录方法是不拦截的,这样的话排除方法的规则就很难定义,甚至没有办法定义。

所以这针对于上述的问题我们就提出一个新的解决方案使用Spring中的拦截器。

2.Spring拦截器

对于上述的问题Spring中提供了具体的实现拦截器:HandlerInterceptor,拦截器的实现分为以下两步:

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

2.1自定义拦截器

具体的实现如下所示:

代码展示:

config层代码:

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");//业务方法HttpSession session = request.getSession(false);if (session != null && session.getAttribute(AppVar.SESSION_KEY) != null) {//用户已经登录return true;//继续执行后续的流程}response.sendRedirect("https://www.baidu.com");return false;}
}

common代码:

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

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("/reg")public String reg() {System.out.println("do reg()");Object obj = null;System.out.println(obj.hashCode());return "reg";}@RequestMapping("/login")public String login() {System.out.println("do login()");return "login";}
}

结果展示:

如果已经登录:

如果没有登录:

这里我们使用以下的方式来模拟没有登录的场景:

 

2.2将自定义拦截器加入到系统配置

上述自定好了一个拦截器之后接下来我们就需要将自定义的拦截器设置到当前项目的配置文件中,并设置拦截的规则。

package com.example.demo.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;public class AppConfig implements WebMvcConfigurer {@Autowiredprivate UserInterceptor userInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(userInterceptor).addPathPatterns("/**") //拦截所有的请求.excludePathPatterns("user/reg") //除去拦截reg注册请求.excludePathPatterns("user/login"); //除去拦截login登录请求}
}
其中:
  • addPathPatterns:表示需要拦截的 URL,“**”表示拦截任意⽅法(也就是所有⽅法)。
  • excludePathPatterns:表示需要排除的URL。

​​​​​​​2.3拦截器练习

接下来我们来实现一下面的这个登录拦截器的练习题:

  • 登录、注册⻚⾯不拦截,其他⻚⾯都拦截。
  • 当登录成功写⼊ session 之后,拦截的⻚⾯可正常访问。

首先我们先来导入一个博客系统的静态页面:

大家可以从我的Gitee中进行下载☞博客系统静态页面: 博客系统的静态页面

导入静态页面之后我们就可以开始编写拦截器的规则了。

package com.example.demo.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.annotation.Resource;@Configuration
public class AppConfig implements WebMvcConfigurer {@Resourceprivate UserInterceptor userInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(userInterceptor).addPathPatterns("/**") //拦截所有的请求.excludePathPatterns("/user/reg").excludePathPatterns("/reg.html").excludePathPatterns("/login.html").excludePathPatterns("/css/**").excludePathPatterns("/editor.md/**").excludePathPatterns("/img/**").excludePathPatterns("/js/**").excludePathPatterns("/user/login");}
}

接下来就可以正常访问页面了。

2.4拦截器的实现原理

正常情况下的调用顺序:

然而有了拦截器之后,会在调用Controller之前进行相应的业务处理,执行的流程如下所示:

 

首先所有的Controller执行都会通过一个调度器DispathcherServlet来实现,这一点可以在Spring Boot的控制台打印信息中看出来。

而所有的方法都会执行 DispathcherServlet 中的doDispatch调度方法,通过分析doDispatch源码我们可以看出在开始执行Controller之前,会先调用预处理方法applyPreHandle,在applyPreHandle中会获取所有的拦截器HandleInterceptor并执行拦截器中的preHandle方法,这样就和我们之前定义的拦截器可以对应上了。这就是拦截器的实现原理。

3.统一异常处理

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

代码如下所示:

package com.example.demo.common;import lombok.Data;/*** 统一对象*/
@Data
public class ResultAjax {private int code; // 状态码private String msg; // 状态码的描述信息private Object data; // 返回数据/*** 返回成功对象* @param data* @return*/public static ResultAjax succ(Object data) {ResultAjax resultAjax = new ResultAjax();resultAjax.setCode(200);resultAjax.setMsg("");resultAjax.setData(data);return resultAjax;}public static ResultAjax succ(String msg, Object data) {ResultAjax resultAjax = new ResultAjax();resultAjax.setCode(200);resultAjax.setMsg(msg);resultAjax.setData(data);return resultAjax;}/*** 返回失败对象* @param code* @param msg* @return*/public static ResultAjax fail(int code,String msg){ResultAjax resultAjax = new ResultAjax();resultAjax.setCode(code);resultAjax.setMsg(msg);resultAjax.setData(null);return resultAjax;}public static ResultAjax fail(int code,String msg,Object data){ResultAjax resultAjax = new ResultAjax();resultAjax.setCode(code);resultAjax.setMsg(msg);resultAjax.setData(data);return resultAjax;}}
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 //统一返回的对象public ResultAjax doNullPointerException(NullPointerException e) {ResultAjax resultAjax = new ResultAjax();resultAjax.setCode(-1);resultAjax.setMsg("空指针异常:" + e.getMessage());resultAjax.setData(null);return resultAjax;}
}

这里需要将Controller里面的返回值进行修改。

结果如下所示:

这样当出现异常的时候前端拿到的就不是之前报一大堆的错误了,就是可以直接在页面中显示出错误的信息了。

当然上述过程中我们只是处理了空指针异常,那么异常那么多难道要将没一种情况都写进去吗?

当然不是我们可以使用Exception来进行异常处理。如下代码所示:

4.统一数据的返回格式

为什么我们需要统一数据的返回格式呢?统一数据的返回格式优点有以下几点:

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

统一的数据返回格式可以使用@ControllerAdvice + ResponseBodyAdvice的方式来实现,具体实现代码如下所示:

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.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;import javax.annotation.Resource;@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {@Resourceprivate ObjectMapper objectMapper;/*** 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) {//已经包装好的对象//instanceof - 判断类型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);}
}

结束语:

好了这节小编就给大分享到这里啦,希望这节对大家有关于Spring中的统一处理的基础知识的了解有一定帮助,想要学习的同学记得关注小编和小编一起学习吧!如果文章中有任何错误也欢迎各位大佬及时为小编指点迷津(在此小编先谢过各位大佬啦!)

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

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

相关文章

对于使用win32 API获取性能计数器的理解

微软提供了获取性能计数器的接口,如下 LSTATUS RegQueryValueExA([in] HKEY hKey,[in, optional] LPCSTR lpValueName,LPDWORD lpReserved,[out, optional] LPDWORD lpType,[out, optional] LPBYTE lpData,[in, out, optional] L…

mysql面试题30:什么是数据库连接池、应用程序和数据库建立连接的过程、为什么需要数据库连接池、你知道哪些数据库连接池

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:什么是数据库连接池? 数据库连接池是一种用于管理和复用数据库连接的技术。它是在应用程序和数据库之间建立一组数据库连接,并以池的形式存储起…

Kubernetes革命:云原生时代的应用编排和自动化

文章目录 什么是Kubernetes以及为何它备受欢迎?云原生应用和K8s的关系Kubernetes的核心概念:Pods、Services、ReplicaSets等部署、扩展和管理应用程序的自动化容器编排的演进:Docker到Kubernetes实际用例:企业如何受益于K8s的应用…

winform窗体控件太多显示不过来,怎么实现滚动条

winform窗体控件太多显示不过来,怎么实现滚动条 Winform Panel实现滚动条 一、创建panel 在界面上拖拽一个父级Panel1,然后在Panel1里面拖拽一个子级Panel2 设置父级Panel1的AutoScroll属性为True 属性设置好后,当子级高度或者宽度大于父…

LED灯实验--汇编

asm-led.S .text .global _start _start: /* 1. led灯的初始化 *//* 1.1 使能GPIOE、DPIOF外设控制器的时钟 */ldr r0, 0x50000A28ldr r1, [r0]orr r1, r1, #(0x3 << 4)str r1, [r0]/* 1.2 设置PE10、PE8、PF10引脚为输出模式 */ldr r0, 0x50006000ldr r1, [r0]bic r1,…

jenkins工具系列 —— 插件 使用Changelog获取commit记录

文章目录 安装changelog插件重启jenkins配置 ChangelogExecute shell 使用 changelog邮件中html格式也可以使用构建测试&#xff08;查看构建项 -> 控制台输出&#xff09; 安装changelog插件 插件文件可通过 V 获取 点击 左侧的 Manage Jenkins —> Plugins ——> …

虹科方案 | 汽车CAN/LIN总线数据采集解决方案

全文导读&#xff1a;现代汽车配备了复杂的电子系统&#xff0c;CAN和LIN总线已成为这些系统之间实现通信的标准协议&#xff0c;为了开发和优化汽车的电子功能&#xff0c;汽车制造商和工程师需要可靠的数据采集解决方案。基于PCAN和PLIN设备&#xff0c;虹科提供了一种高效、…

基于SSM的个人博客系统

实现内容 本系统为用户提供实现了以下功能&#xff1a; 1.登录功能&#xff1a; 系统为单用户系统&#xff0c;为用户分配了用户名和密码。用户必须先登录&#xff0c;进入操作界面。用户输入ID和密码&#xff0c;通过服务器验证方可运行&#xff0c;否则显示消息提示。 2.…

鸿蒙手表开发之使用adb命令安装线上包

#国庆发生的那些事儿# 鸿蒙手表开发之使用adb命令安装线上包 前言&#xff1a; 由于之前的哥们匆忙离职了&#xff0c;所以鸿蒙手表项目的新版本我临时接过来打包发布&#xff0c;基本上之前没有啥鸿蒙经验&#xff0c;但是一直是做Android开发的&#xff0c;在工作人员的指…

6款流程图制作软件:一站式指南

流程图是一种常用的图示工具&#xff0c;可以帮助我们更清晰地表达和展示流程、流程图等内容。在今天已经变得非常普及和便捷&#xff0c;接下来本文将于大家分享6款好用的流程图软件&#xff0c;一起来看看吧&#xff01; 博思白板boardmix 博思白板boardmix是一款基于浏览器…

使用gpio子系统实现按键驱动(二)

一&#xff0c;gpio_keys.c介绍 Linux内核下的drivers/input/keyboard/gpio_keys.c实现了一个体系无关的GPIO按键驱动&#xff0c;使用此按键驱动&#xff0c;只需要在设备树gpio-key节点添加需要的按键子节点即可&#xff0c;适合于实现独立式按键驱动。 gpio-keys是基于inp…

关于ABB速度,加速度,轴监控指令

关于ABB速度&#xff0c;加速度&#xff0c;轴监控 关于轴监控指令要选择启用和关闭&#xff0c;这个指令是为了防止机器人在抓件放件过程中6轴来回旋转&#xff0c;已最佳的姿态运动 收录于合集 #ABB机器人 9个 上一篇关于ABB机器人的IO创建和设置

嵌入式养成计划-38----C++--匿名对象--友元--常成员函数和常对象--运算符重载

八十七、匿名对象 概念&#xff1a;没有名字对象格式 &#xff1a;类名&#xff08;&#xff09;;作用 用匿名对象给有名对象初始化的用匿名对象给对象数组初始化的匿名对象作为函数实参使用 示例 : #include <iostream> using namespace std; class Dog { private:s…

微电网单台并网逆变器PQ控制matlab仿真模型

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 微电网运行在并网模式下且公共电网供应正常时&#xff0c;因为公共电网给定了电 压和频率的参考值&#xff0c;所有的逆变器可以使用PQ控制方式。 当系统频率为额定频率f0时&#xff0c;系统稳定在A点&#x…

awvs 中低危漏洞

低危 X-Frame-Options Header未配置 查看请求头中是否存在X-Frame-Options Header字段 会话Cookie中缺少secure属性(未设置安全标志的Cookie) 当cookie设置为Secure标志时&#xff0c;它指示浏览器只能通过安全SSL/TLS通道访问cookie。 未设置HttpOnly标志的Cookie 当cookie设置…

终于找到了!多种类型的电子期刊模板在这里!

经过我不懈的努力和搜寻&#xff0c;终于找到了一个提供多种类型电子期刊模板的网站。这个网站拥有丰富多样的模板&#xff0c;可以满足各种不同的需求&#xff0c;无论是学术研究、商业报告还是个人兴趣爱好&#xff0c;都能在这里找到心仪的模板。 一、网站介绍 这个网站叫做…

软件行业与就业(导师主讲)

在企业软件应用的整体架构体系中&#xff0c;有一部分被称为中间件&#xff0c;那么什么叫中间件&#xff1f; 中间件&#xff08;Middleware&#xff09;是指位于操作系统和应用程序之间的一层软件层&#xff0c;它提供了一组工具和服务&#xff0c;用于简化和增强企业软件应用…

关于hive的时间戳

unix_timestamp&#xff08;&#xff09;和 from_unixtime&#xff08;&#xff09;的2个都是格林威治时间 北京时间 格林威治时间8 from_unixtme 是可以进行自动时区转换的 (4.0新特性) 4.0之前可以通过from_utc_timestamp进行查询 如果时间戳为小数&#xff0c;是秒&#…

Java基本数据类型

Java基本数据类型 1 数值型 整型数据类型 数据类型内存空间&#xff08;8位1字节&#xff09;取值范围byte(字节型&#xff09;8位&#xff08;1字节&#xff09;-128~127 &#xff08;2的8次方&#xff09;short(短整型&#xff09;16位&#xff08;2字节&#xff09;-32768~3…

利用MobaXterm连接服务器的全程配置

一、服务器上的操作 1.1 保证openssh的安装 openssh安装命令如下 apt-get update apt install openssh-server1.2 保证SSH服务没有在相应端口上侦听连接 1确保本地 SSH 服务正在运行 可以尝试使用以下命令检查 SSH 服务的状态&#xff08;在大多数 Linux 系统上&#xff0…