当前位置: 首页 > news >正文

springAOP的实例

文章目录

  • 前言
  • 一.用户登录权限校验
    • 1.1 spring 拦截器
    • 1.2 传统的用户登录权限验证
    • 1.3 使用拦截器的方式
    • 1.4 案例
    • 1.5 拦截器实现原理
  • 三.统一异常处理
    • 3.1 什么是统一异常处理
    • 3.2 具体步骤
  • 四.统⼀数据返回格式
    • 4.1 为什么需要统一的数据返回
    • 4.2 统一返回数据的格式
    • 4.3 统一移除处理在遇到 String 返回返回时报错的问题

前言

前面一篇文章,我们具体学习了,springAOP相关概念,我们再具体的回忆一下,什么是springAOP其实就是统一功能的实现.下面我们将利用AOP的思想,去实现三个功能.

一.用户登录权限校验

⽤户登录权限的发展从之前每个⽅法中⾃⼰验证⽤户登录权限,到现在统⼀的⽤户登录验证处理,它是⼀个逐渐完善和逐渐优化的过程。
我们不使用AOP的思想,我们之前做登录验证校验的过程是:

1.1 spring 拦截器

在介绍用户登录的权限验证之前,我们需要介绍一下,spring的拦截器的实现步骤.
对于以上问题Spring 中提供了具体的实现拦截器:HandlerInterceptor,拦截器的实现分为以下两个步骤∶

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

具体实现如下。

我下面给出一个简单的demo
1.创建自定义拦截器,实现HandlerInterceptor接口

@Component
public class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle方法被调用");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle方法被调用");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion方法被调用");}
}

2.将自定义拦截器加入 WebMvcConfigurer的 addInterceptors方法中。

@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {@Autowiredprivate MyInterceptor myInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(myInterceptor); // 将拦截器注册到 Spring 容器中}
}

1.2 传统的用户登录权限验证

具体代码入下:

   @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;}}@RequestMapping("/m2")public Object method2(HttpServletRequest request) {// 有 session 就获取,没有不会创建HttpSession session = request.getSession(false);if (session != null && session.getAttribute("userinfo") != null) {// 说明已经登录,业务处理return true;} else {// 未登录return false;}}// 其他⽅法...}

从上述代码可以看出,每个⽅法中都有相同的⽤户登录验证权限,它的缺点是:

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

1.3 使用拦截器的方式

具体步骤如下:

  1. 创建自定义拦截器,实现 HandlerInterceptor 接口的preHandle (执行具体方法之前的预处理)方法。
/*
拦截器的自定义实现*/
@Component
public class LoginInterceptor implements HandlerInterceptor {//调用目标方法之前执行的方法//此方法返回boolean 类型的值,如果返回true 表示拦截成功,继续执行目标方法//如果返回false,表示拦截执行失败,检验未通过,目标方法不执行.@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//用户登录判断业务HttpSession session=request.getSession(false);if (session !=null && session.getAttribute("seesion_userinfo") !=null){return true;}response.setContentType("application/json;charset=utf8");response.getWriter().println("{\"code\":-1,\"msg\":\"登录失败\",\"data\":\"\"}");return false;}
}
  1. 将自定义拦截器加入 WebMvcConfigurer的 addInterceptors方法中。
@Configuration
public class MyConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/user/login")}
}

1.4 案例

知道了具体的拦截器练习之后,我们接下来会进行一个案例的练习
登录拦截器
具体的效果如下
1.登录、注册页面不拦截,其他页面都拦截。
2.当登录成功写入 session 之后,拦截的页面可正常访问

  • 先准备一个前端登录页面
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Login</title>
</head>
<body>
<form action="/user/login" method="get"><label for="username">Username:</label><input type="text" id="username" name="username" required><br><label for="password">Password:</label><input type="password" id="password" name="password" required><br><button type="submit">Login</button>
</form>
</body>
</html>
  • 准备UserMapper.xml文件和接口类
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
<select id="login" resultType="com.example.demo.entity.UserInfo">select * from userinfo where username=#{username}
</select>
</mapper>
@Mapper
public interface UserMapper {UserInfo login(@Param("username") String username);
}
  • 准备Service层代码
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public UserInfo login(String username){return userMapper.login(username);}
}
  • 编写Controller层代码
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/index")public String index(){return "index";}@RequestMapping("/reg")public String reg(){return "login";}@RequestMapping("/login")public String login(String username, String password , HttpServletRequest request) {//1.进行非空校验if (  !StringUtils.hasLength(password) ||!StringUtils.hasLength(username)){return "参数有误";}UserInfo userInfo=userService.login(username);if (userInfo == null || userInfo.getId() <= 0)return  "用户名或密码输入错误!";if (!password.equals(userInfo.getPassword()))return  "用户名或密码输入错误!";HttpSession session=request.getSession();session.setAttribute("userinfo",userInfo);return "{\"code\":\"200\",\"message\":\"登录成功\"}";}}
  • 编写实体类

@Data
public class UserInfo {private int id;private String username;private String password;private String photo;private LocalDateTime createtime;private LocalDateTime updatetime;private int state;
}

最后启动springboot项目就可以了

1.5 拦截器实现原理

我们先来看程序正常的执行流程.
在这里插入图片描述

这边是有了拦截器之后的流程
在这里插入图片描述
看完了上面的具体流程以后,我们来具体的说明一下流程.
在SpringMVC中,拦截器可以通过实现HandlerInterceptor接口来实现。当请求到达时,SpringMVC会先通过HandlerMapping找到对应的Controller,然后再通过HandlerAdapter找到对应的处理方法。在这个过程中,如果有拦截器实现了HandlerInterceptor接口并重写了preHandle()方法,那么这个方法会在Controller和处理方法之前被调用。

三.统一异常处理

3.1 什么是统一异常处理

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

3.2 具体步骤

1.创建一个异常处理类

@ControllerAdvice 
public class MyExceptionAdvice  {}

2.创建异常检测的类和处理业务方法

 @ExceptionHandler(NullPointerException.class)public HashMap<String,Object> NullPointerException(NullPointerException e){HashMap<String, Object> result=new HashMap<>();result.put("code",-1);result.put("msg","空指针"+e.getMessage());result.put("data",null);return result;}

具体的发送请求

  @RequestMapping("/login2")public int login2(){int num=10 / 0;return 2;}

显示结果:
在这里插入图片描述

一旦项目出现了空指针异常之后,就会统一异常处理.

四.统⼀数据返回格式

4.1 为什么需要统一的数据返回

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

4.2 统一返回数据的格式

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

/*** 统一的格式处理*/
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {@Autowiredprivate ObjectMapper objectMapper;/*** 是否执行beforeBody* @param returnType* @param converterType* @return*/@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}/**** @param body 原始返回值* @param returnType* @param selectedContentType* @param selectedConverterType* @param request* @param response* @return*/@SneakyThrows@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {//返回的标准 HashMap<String,Object> ->code,msg,dataif (body instanceof HashMap) {return body;}//重写返回结果,让其中返回一个统一的数据格式HashMap<String, Object> result = new HashMap<>();result.put("code", 200);result.put("data", body);result.put("msg", "");return result;}
}

4.3 统一移除处理在遇到 String 返回返回时报错的问题

为什么会出现这样的错误呢?

现看具体的流程
1.方法返回的是 String
2.统一数据返回之前处理 -> String ConvertHashMap
3.将 HashMap 转换成 application/json宁符串给前端(接口)

具体的错误是出在第三步的,
在这里插入图片描述
因为走到第三步之后
就会判断原 Body 的类型 ->
1.是 String ->StringllttpMessageConverter 进行类型转换
2.非 StringHttpMessageConverter 进行类型转换.

所以我们就可以给出具体的解决方案
1.将 StringHttpMessageConverter 去掉
2.在统一数据重写时,单独处理 String 类型,让其返回一个 String 宁符串,而非 HashMap
在这里插入图片描述

相关文章:

springAOP的实例

文章目录 前言一.用户登录权限校验1.1 spring 拦截器1.2 传统的用户登录权限验证1.3 使用拦截器的方式1.4 案例1.5 拦截器实现原理 三.统一异常处理3.1 什么是统一异常处理3.2 具体步骤 四.统⼀数据返回格式4.1 为什么需要统一的数据返回4.2 统一返回数据的格式4.3 统一移除处理…...

【JavaEE】深入了解Spring中Bean的可见范围(作用域)以及前世今生(生命周期)

【JavaEE】Spring的开发要点总结&#xff08;4&#xff09; 文章目录 【JavaEE】Spring的开发要点总结&#xff08;4&#xff09;1. Bean的作用域1.1 一个例子感受作用域的存在1.2 通过例子说明作用域的定义1.3 六种不同的作用域1.3.1 singleton单例模式&#xff08;默认作用域…...

P1320 压缩技术(续集版)

题目描述 设某汉字由 N N N \times N NN 的 0 \texttt 0 0 和 1 \texttt 1 1 的点阵图案组成。 我们依照以下规则生成压缩码。连续一组数值&#xff1a;从汉字点阵图案的第一行第一个符号开始计算&#xff0c;按书写顺序从左到右&#xff0c;由上至下。第一个数表示连续有…...

k8s(七) 叩丁狼 service Ingress

负责东西流量&#xff08;同层级/内部服务网络通信&#xff09;的通信 service的定义 apiVersion: v1 kind: Service metadata:name: nginx-svclabels:app: nginx-svc spec:ports:- name: http # service 端口配置的名称protocol: TCP # 端口绑定的协议&#xff0c;支持 TCP、…...

Android Studio 关于BottomNavigationView 无法预览视图我的解决办法

一、前言&#xff1a;最近在尝试一步一步开发一个自己的软件&#xff0c;刚开始遇到的问题就是当我们引用 com.google.android.material.bottomnavigation.BottomNavigationView出现了无法预览视图的现象&#xff0c;我也在网上查了很多中解决方法&#xff0c;最后在执行了如下…...

【STM32】小电流FOC驱控一体板(开源)

FOC驱控一体板 主控芯片stm32f103c8t6 驱动芯片drv8313 三相电流采样 根据B站一个UP主的改的&#xff08;【【自制】年轻人的第一块FOC驱动器】&#xff09;&#xff0c;大多数元器件是0805&#xff0c;实验室具备且便于自己动手焊接 。 晶振用的是无源晶振&#xff0c;体…...

代码分析:循环创建N个子进程——为什么最后一个属于父进程?

黑马C/C 2018年32期代码分析 //循环创建n个子进程 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <unistd.h>int main() {int i 0;for(i0; i<3; i){//创建子进程pid_t pid fork();if(pid&…...

【SpringBoot面试题整理-超级有效】

文章目录 1.SpringBoot如何解决跨域问题&#xff1f;2.为什么要用Spring Boot&#xff1f;3. Spring Boot的约定优于配置&#xff0c;你的理解是什么&#xff1f;4. SpringBoot有哪些优点&#xff1f;5. Spring Boot中自动装配机制的原理&#xff1f;6.SpringBoot支持哪些日志框…...

岩土工程仪器多通道振弦传感器信号转换器应用于隧道安全监测

岩土工程仪器多通道振弦传感器信号转换器应用于隧道安全监测 多通道振弦传感器信号转换器VTI104_DIN 是轨道安装式振弦传感器信号转换器&#xff0c;可将振弦、温度传感器信号转换为 RS485 数字信号和模拟信号输出&#xff0c;方便的接入已有监测系统。 传感器状态 专用指示灯方…...

西瓜书读书笔记整理(五)—— 第四章 决策树

第四章 决策树 4.1 基本流程4.1.1 什么是决策树算法4.1.2 决策树学习的目的4.1.3 决策树学习基本过程4.1.4 决策树学习基本算法4.1.5 递归结束的三种情况 4.2 划分选择4.2.1 信息增益&#xff08;information gain&#xff09;—— ID3 决策树学习算法属性划分准则4.2.2 信息增…...

STM32 4G学习

硬件连接 ATK-IDM750C模块可直接与正点原子 MiniSTM32F103开发板板载的ATK模块接口&#xff08;ATK-MODULE&#xff09;进行连接。 功能说明 ATK-IDM750C是正点原子&#xff08;ALIENTEK&#xff09;团队开发的一款高性能4G Cat1 DTU产品&#xff0c;支持移动4G、联通4G和…...

Golang 中实现实时聊天通讯

客户端代码 package mainimport ("fmt""log""net/url""os""os/signal""time""github.com/gorilla/websocket" )func main() {interrupt : make(chan os.Signal, 1)signal.Notify(interrupt, os.Interr…...

前端面试的性能优化部分(5)每天10个小知识点

目录 系列文章目录前端面试的性能优化部分&#xff08;1&#xff09;每天10个小知识点前端面试的性能优化部分&#xff08;2&#xff09;每天10个小知识点前端面试的性能优化部分&#xff08;3&#xff09;每天10个小知识点前端面试的性能优化部分&#xff08;4&#xff09;每天…...

【链表OJ 1】移除链表元素val

大家好&#xff0c;欢迎来到我的博客&#xff0c;此题是关于链表oj的第一题&#xff0c;此后还会陆续更新博客&#xff0c;如有错误&#xff0c;欢迎大家指正。 来源:https://leetcode.cn/problems/remove-linked-list-elements/description/ 题目: 方法一:定义prev和cur指针…...

复原 IP 地址——力扣93

文章目录 题目描述回溯题目描述 回溯 class Solution{public:static constexpr int seg_count=4<...

OSPF综合实验

实验题目如下&#xff1a; 实验拓扑如下&#xff1a; 实验要求如下&#xff1a; 【1】R4为ISP&#xff0c;其上只能配置IP地址: R4与其他所有直连设备间使用公有 【2】R3---R5/6/7为MGRE环境&#xff0c;R3为中心站点 【3】整个OSPF环境IP地址为172.16.0.0/16 【4】所有设备…...

安卓4G核心板开发板_MTK6785/MT6785(Helio G95)安卓手机主板方案

联发科MTK6785&#xff08;Helio G95&#xff09;安卓核心板采用八核 CPU 具有两个强大的 Arm Cortex-A76 处理器内核&#xff0c;主频高达 2.05GHz&#xff0c;外加六个 Cortex-A55 高效处理器。其强大的图形性能由 Arm Mali-G76 MC4 提供&#xff0c;速度可提升至 900MHz 。 …...

Linux 匿名页的生命周期

目录 匿名页的生成 匿名页生成时的状态 do_anonymous_page缺页中断源码 从匿名页加入Inactive lru引出 一个非常重要内核patch 匿名页何时回收 本文以Linux5.9源码讲述 匿名页的生成 用户空间malloc/mmap(非映射文件时&#xff09;来分配内存&#xff0c;在内核空间发生…...

设计模式概述与UML图

文章目录 一、设计模式概述1. 软件设计模式的产生背景2. 软件设计模式的概念3. 学习设计模式的必要性4. 设计模式分类&#xff08;1&#xff09;创建型模式&#xff08;2&#xff09;结构型模式&#xff08;3&#xff09;行为型模式 二、UML图1. 类图概述2. 类图作用3. 类图表示…...

使用Vscode编辑keil工程

一、需要安装的插件 1. Keil Assistant 2. C/C 3. 中文配置&#xff1a; 二、插件配置 1. Keil Assistant 添加Keil的安装路径 接下来就可以使用vscode编辑Keil的工程了&#xff0c;调试编译和下载程序需要返回到Keil中进行操作。 三、Vscode常用快捷键 可以自定义进行配置…...

高效医学知识图谱构建方案:CMeKG工具自动化处理中文医学文本技术深度解析

高效医学知识图谱构建方案&#xff1a;CMeKG工具自动化处理中文医学文本技术深度解析 【免费下载链接】CMeKG_tools 项目地址: https://gitcode.com/gh_mirrors/cm/CMeKG_tools 在医疗信息化与人工智能深度融合的今天&#xff0c;中文医学知识图谱构建面临严峻的技术挑…...

ChatBI怎么在BI试点中用?3个低门槛落地场景亲测有效

ChatBI试点的前置门槛&#xff1a;先搞定最小可行数据集&#xff0c;不用全量建设 ChatBI是观远数据推出的自然语言分析产品&#xff0c;用户可以通过口语化的提问直接获取数据结果、可视化图表甚至分析结论&#xff0c;无需掌握复杂的报表制作或SQL查询技能。在BI试点阶段引入…...

社媒爆款流水线:手把手教你用Runway Gen-4.5的A/B测试功能,批量生产TikTok热门视频

社媒爆款流水线&#xff1a;用Runway Gen-4.5打造数据驱动的短视频生产引擎 在短视频内容爆炸式增长的今天&#xff0c;一个残酷的现实是&#xff1a;99%的内容在发布后的24小时内就会沉入算法深渊。那些能突破重围的爆款视频&#xff0c;往往不是偶然灵感的产物&#xff0c;而…...

C++笔记 继承关系中构造和析构顺序(面向对象)

在C面向对象编程中&#xff0c;继承是实现代码复用和类层次设计的核心特性。当存在基类与派生类的继承关系时&#xff0c;构造函数和析构函数的调用顺序有严格的规则——这不仅是面试高频考点&#xff0c;更是避免内存泄漏、保证对象正确初始化/清理的关键。核心结论先明确&…...

Qwen3-14B日志分析教程:ELK栈收集推理请求、响应、错误全链路追踪

Qwen3-14B日志分析教程&#xff1a;ELK栈收集推理请求、响应、错误全链路追踪 1. 为什么需要日志分析 当你在私有化部署Qwen3-14B模型时&#xff0c;可能会遇到各种问题&#xff1a;为什么推理速度突然变慢了&#xff1f;为什么API返回了错误响应&#xff1f;哪些请求消耗了最…...

基于GOOSE - Transformer - LSTM的数据回归预测探索

基于GOOSE-Transformer-LSTM的数据回归预测 模型结合Transformer的全局注意力机制和LSTM的短期记忆及序列处理能力 首先&#xff0c;采用Transformer自注意力机制捕捉数据的全局依赖性&#xff0c;并输出一个经过全局上下文编码的表示&#xff1b;然后&#xff0c;采用2024年最…...

AI报告文档审核助力生态数据可信化:IACheck提升生物多样性调查报告物种识别准确性

在生态环境保护逐渐走向精细化管理的背景下&#xff0c;生物多样性调查数据的重要性不断提升。从自然保护区评估到生态修复项目&#xff0c;从环境影响评价到长期生态监测&#xff0c;物种数据已成为支撑决策的重要基础。而在这些数据中&#xff0c;“物种识别的准确性”&#…...

从芯片设计到产线测试:深入浅出聊聊DFT中的SCAN链设计与JTAG标准(含IEEE 1149.1)

从芯片设计到产线测试&#xff1a;深入浅出聊聊DFT中的SCAN链设计与JTAG标准&#xff08;含IEEE 1149.1&#xff09; 在芯片设计领域&#xff0c;可测试性设计&#xff08;DFT&#xff09;早已从"锦上添花"变成了"不可或缺"的核心环节。想象一下&#xff0…...

5分钟精通B站音频提取:从新手到高手的开源工具实战指南

5分钟精通B站音频提取&#xff1a;从新手到高手的开源工具实战指南 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader &#x1f633; 项目地址: https://gitcode.com/gh_mirrors/…...

ANR-WatchDog源码深度剖析:从线程监控到错误抛出的完整实现

ANR-WatchDog源码深度剖析&#xff1a;从线程监控到错误抛出的完整实现 【免费下载链接】ANR-WatchDog A simple watchdog that detects Android ANR (Application Not Responding) error and throws a meaningful exception 项目地址: https://gitcode.com/gh_mirrors/an/AN…...