springAOP的实例

news2024/12/25 9:41:28

文章目录

  • 前言
  • 一.用户登录权限校验
    • 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 {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle方法被调用");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle方法被调用");
    }

    @Override
    public 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 {

    @Autowired
    private MyInterceptor myInterceptor;

    @Override
    public 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,表示拦截执行失败,检验未通过,目标方法不执行.
    @Override
    public 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 {
    @Autowired
    private LoginInterceptor loginInterceptor;
    @Override
    public 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 {
    @Autowired
    private UserMapper userMapper;
    public UserInfo login(String username){
        return userMapper.login(username);
    }
}

  • 编写Controller层代码
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private 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 {
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 是否执行beforeBody
     * @param returnType
     * @param converterType
     * @return
     */
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    /**
     *
     * @param body 原始返回值
     * @param returnType
     * @param selectedContentType
     * @param selectedConverterType
     * @param request
     * @param response
     * @return
     */
    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        //返回的标准 HashMap<String,Object> ->code,msg,data
        if (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
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

瞅一眼nginx

目录 &#x1f9ac;什么是nginx? &#x1f9ac;nginx配置官方yum源&#xff1a; &#x1f9ac;nginx优点 &#x1f9ac;nginx 缺点 &#x1f9ac;查看nginx默认模块 &#x1f40c;nginx新版本的配置文件&#xff1a; &#x1f40c;nginx目录索引 &#x1f40c;nginx状态…

Attacks in NLP

一、 Introduction NLP对抗攻击是人工智能对抗攻击的一个重要的组成部分&#xff0c;但是最近几年才逐渐开始兴起&#xff0c;究其原因在于NLP对抗攻击与传统computer vision或者audio对抗攻击有很大的不同&#xff0c;主要在于值空间的连续性&#xff08;CV、audio&#xff0…

基于Selenium技术方案的爬虫入门实践

通过爬虫技术抓取网页&#xff0c;动态加载的数据或包含 JavaScript 的页面&#xff0c;需要使用一些特殊的技术和工具。以下是一些常用的技术方法&#xff1a; 使用浏览器模拟器&#xff1a;使用像 Selenium、PhantomJS 或其他类似工具可以模拟一个完整的浏览器环境&#xff0…

Mysql的instr()函数用法详解

最近接手了一个大型老项目&#xff0c;用到的jfinal技术&#xff0c;后端大部分都是拼写的sql&#xff0c;对一些sql函数不太理解的我算是一个挑战&#xff0c;也是一个进步的很大空间。 今天来说下instr这个函数 首先看下我们的表数据 我们先执行&#xff1a; SELECT * fro…

Axwing.878 线性同余方程

题目 给定n组数据ai, bi , mi&#xff0c;对于每组数求出一个xi&#xff0c;使其满足ai * xibi (mod mi)&#xff0c;如果无解则输出impossible。 输入格式 第一行包含整数n。 接下来n行&#xff0c;每行包含一组数据ai , bi , mi。 输出格式 输出共n行&#xff0c;每组数…

【C++】常用到的“using namespace std;”到底是什么?

一、引言 在初学C时&#xff0c;在包含完头文件之后&#xff0c;我们常常会看到这么一句话&#xff1a;using namespace std; 比如&#xff1a; #include<iostream> using namespace std; int main() {cout << "hello world" << endl;return 0…

Java实现neo4j数据库连接及增删改查

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

LeetCode96. 不同的二叉搜索树

96. 不同的二叉搜索树 文章目录 [96. 不同的二叉搜索树](https://leetcode.cn/problems/unique-binary-search-trees/)一、题目二、题解 一、题目 给你一个整数 n &#xff0c;求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种&#xff1f;返回满足题意的…

《剑指offer》(5)搜索算法、位运算、模拟

方法一&#xff1a; class Solution: def GetNumberOfK(self , nums: List[int], k: int) -> int: #从两边开始找&#xff0c;找到之后记录当前位置 left 0 right len(nums) - 1 if k not in nums: return 0 start len(nums) - 1 end 0 while left < right: if nums…

C++ 多态深入解析

文章目录 前言一、什么是多态二、如何实现多态三、代码讲解四、静态联编&#xff0c;动态联编总结 前言 在C编程中&#xff0c;多态性&#xff08;Polymorphism&#xff09;是一种重要的概念&#xff0c;它允许基于对象的实际类型来调用不同的函数。多态性提供了灵活性和可扩展…

高绩效项目管理助力企业数字化变革︱海克斯康数字智能大中华区PMO经理周游

海克斯康数字智能大中华区PMO经理周游先生受邀为由PMO评论主办的2023第十二届中国PMO大会演讲嘉宾&#xff0c;演讲议题&#xff1a;高绩效项目管理助力企业数字化变革。大会将于8月12-13日在北京举办&#xff0c;敬请关注&#xff01; 议题简要&#xff1a; 在当今项目驱动的…

vue2 封装 webSocket 开箱即用

第一步&#xff1a; 下载 webSocket npm install vue-native-websocket --save 第二步&#xff1a; 需要在 main.js 中 引入 import websocket from vue-native-websocket; Vue.use(websocket, , {connectManually: true, // 手动连接format: json, // json格式reconnection:…

口-肠-脑轴与精神健康的关系

谷禾健康 在个体中&#xff0c;每个微生物栖息地都表现出独特的微生物种群模式。迄今为止&#xff0c;关于微生物组相关疾病的研究主要集中在器官特异性微生物组上。然而&#xff0c;器官间的微生物网络正逐渐成为生理功能和病理过程中的重要调节因子和治疗机会。 在正常情况下…

c语言——三子棋

基本框架 三个文件: 其中.cpp文件用于游戏具体函数设计&#xff0c;.h文件为游戏的函数声明&#xff0c;test.cpp文件用于测试游戏运行。 需要用到的头文件&#xff1a; #include <stdio.h> #include <stdlib.h>//rand&srand #include <time.h>//时间相…

Linux命令200例:cd用于改变当前工作目录(常用)

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌。CSDN专家博主&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &…

Linux中singal信号的作用

void&#xff08;* signal&#xff08;int sig&#xff0c;void&#xff08;* func&#xff09;&#xff08;int&#xff09;&#xff09;&#xff09;&#xff08;int&#xff09;;设置处理信号的功能 头文件为&#xff1a;#include <signal.h> 指定使用sig指定的信号…

FreeRTOS通过消息队列实现串口命令解析(串口中断)

作者&#xff1a;Jack_G 时间&#xff1a;2023.08.08 版本&#xff1a;V1.0 上次修改时间&#xff1a; 环境&#xff1a; \quad \quad \quad \quad STM32Cube MX V6.8.1 \quad \quad \quad \quad STM32CubeH7 Firmware Package V1.11.0 / 04-Nov-2022 \quad \quad \quad \qu…

【Linux】—— 进程等待 waitwaitpid

序言&#xff1a; 之前讲过&#xff0c;子进程退出&#xff0c;父进程如果不管不顾&#xff0c;就可能造成‘僵尸进程’的问题&#xff0c;进而造成内存泄漏。因此&#xff0c;为了解决这个问题&#xff0c;就需要用到有关 “进程等待” 的基本知识&#xff01;&#xff01;&am…

form-create-designer整合element-plus使用方法

最近在使用form-create-designer生成表单的时候遇到了很多问题和各种报错&#xff0c;按照官方文档的方法一步步来做&#xff0c;发现行不通&#xff0c;后来经过不断尝试&#xff0c;终于找到了使用方法&#xff0c;这里做一下总结。 1、安装所需的依赖包 npm install eleme…

PE半透明屏,在建筑行业中,有哪些应用展示?

PE半透明屏是一种新型的屏幕材料&#xff0c;具有半透明的特点。 它由聚乙烯&#xff08;PE&#xff09;材料制成&#xff0c;具有良好的透明度和柔韧性。 PE半透明屏广泛应用于建筑、广告、展览等领域&#xff0c;具有很高的市场潜力。 PE半透明屏的特点之一是其半透明性。…