SpringBoot:切面AOP实现权限校验:实例演示与注解全解

2023-10-26

1 理解AOP

1.1 什么是AOP

AOP(Aspect Oriented Programming),面向切面思想,是Spring的三大核心思想之一(两外两个:IOC-控制反转、DI-依赖注入)。

那么AOP为何那么重要呢?在我们的程序中,经常存在一些系统性的需求,比如权限校验、日志记录、统计等,这些代码会散落穿插在各个业务逻辑中,非常冗余且不利于维护。例如下面这个示意图:
在这里插入图片描述

有多少业务操作,就要写多少重复的校验和日志记录代码,这显然是无法接受的。当然,用面向对象的思想,我们可以把这些重复的代码抽离出来,写成公共方法,就是下面这样:
在这里插入图片描述

这样,代码冗余和可维护性的问题得到了解决,但每个业务方法中依然要依次手动调用这些公共方法,也是略显繁琐。有没有更好的方式呢?有的,那就是AOP,AOP将权限校验、日志记录等非业务代码完全提取出来,与业务代码分离,并寻找节点切入业务代码中:
在这里插入图片描述

1.2 AOP体系与概念

简单地去理解,其实AOP要做三类事:

  • 在哪里切入,也就是权限校验等非业务操作在哪些业务代码中执行。
  • 在什么时候切入,是业务代码执行前还是执行后。
  • 切入后做什么事,比如做权限校验、日志记录等。

因此,AOP的体系可以梳理为下图:
在这里插入图片描述

一些概念详解:

  • Pointcut:切点,决定处理如权限校验、日志记录等在何处切入业务代码中(即织入切面)。切点分为execution方式和annotation方式。前者可以用路径表达式指定哪些类织入切面,后者可以指定被哪些注解修饰的代码织入切面。
  • Advice:处理,包括处理时机和处理内容。处理内容就是要做什么事,比如校验权限和记录日志。处理时机就是在什么时机执行处理内容,分为前置处理(即业务代码执行前)、后置处理(业务代码执行后)等。
  • Aspect:切面,即Pointcut和Advice。
  • Joint point:连接点,是程序执行的一个点。例如,一个方法的执行或者一个异常的处理。在 Spring AOP中,一个连接点总是代表一个方法执行。
  • Weaving:织入,就是通过动态代理,在目标对象方法中执行处理内容的过程。

网络上有张图,我觉得非常传神,贴在这里供大家观详:
在这里插入图片描述

2 AOP实例

实践出真知,接下来我们就撸代码来实现一下AOP。完成项目已上传至:
https://github.com/ThinkMugz/aopDemo
使用 AOP,首先需要引入 AOP 的依赖。参数校验:这么写参数校验(validator)就不会被劝退了~

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.1 第一个实例

接下来,我们先看一个极简的例子:所有的get请求被调用前在控制台输出一句"get请求的advice触发了"。
具体实现如下:

  1. 创建一个AOP切面类,只要在类上加个 @Aspect注解即可。@Aspect注解用来描述一个切面类,定义切面类的时候需要打上这个注解。@Component 注解将该类交给 Spring来管理。在这个类里实现advice:
package com.mu.demo.advice;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAdvice {
   
    // 定义一个切点:所有被GetMapping注解修饰的方法会织入advice
    @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
    private void logAdvicePointcut() {
   }

 // Before表示logAdvice将在目标方法执行前执行
    @Before("logAdvicePointcut()")
    public void logAdvice(){
   
     // 这里只是一个示例,你可以写任何处理逻辑
        System.out.println("get请求的advice触发了");
    }
}
  1. 创建一个接口类,内部创建一个get请求:
package com.mu.demo.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(value = "/aop")
public class AopController {
   
    @GetMapping(value = "/getTest")
    public JSONObject aopTest() {
   
        return JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200}");
    }
    
 @PostMapping(value = "/postTest")
    public JSONObject aopTest2(@RequestParam("id") String id) {
   
        return JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200}");
    }
}

项目启动后,请求http://localhost:8085/aop/getTest接口:
在这里插入图片描述

请求http://localhost:8085/aop/postTest接口,控制台无输出,证明切点确实是只针对被GetMapping修饰的方法。

2.2 第二个实例

下面我们将问题复杂化一些,该例的场景是:

  1. 自定义一个注解PermissionsAnnotation
  2. 创建一个切面类,切点设置为拦截所有标注PermissionsAnnotation的方法,截取到接口的参数,进行简单的权限校验
  3. 将PermissionsAnnotation标注在测试接口类的测试接口test上

具体的实现步骤:

  1. 使用@Target、@Retention、@Documented自定义一个注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PermissionAnnotation{
   
}
  1. 创建第一个AOP切面类,,只要在类上加个 @Aspect 注解即可。@Aspect注解用来描述一个切面类,定义切面类的时候需要打上这个注解。@Component 注解将该类交给 Spring来管理。在这个类里实现第一步权限校验逻辑:
package com.example.demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjso
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SpringBoot:切面AOP实现权限校验:实例演示与注解全解 的相关文章

  • “此 GPIO 引脚已存在:”第二次出现 GPIO 1 异常

    我正在 Raspberry pi 和 java 上工作 通过使用 pi4j 使 LED 闪烁 一切都已清除并且工作正常 LED 按照代码闪烁 但是当我第二次运行时 它会导致以下错误 我已经搜索了很多有很多相同的问题没有明确的答案如何解决 任
  • 行类型 Spark 数据集的编码器

    我想写一个编码器Row https spark apache org docs 2 0 0 api java index html org apache spark sql Row html输入 DataSet 用于我正在执行的地图操作 本
  • Chrome 崩溃:尝试在空对象引用上调用虚拟方法“long android.view.accessibility.AccessibilityNodeInfo.getSourceNodeId()”

    在处理网页的搜索表单 JavaScript CSS HTML 时 每次单击网络搜索图标并且输入字段获得焦点时 Chrome 浏览器 Android 10 都会崩溃 崩溃报告中的调试堆栈跟踪显示 Attempt to invoke virtu
  • 将数据传递到表单时的重定向后获取?

    我有几个场景 servlet 需要将数据从数据库检索到的记录传递到 JSP 中的表单 目前 我将此信息存储在请求中 使用 RequestDispatcher 转发到页面 一切都很好 然而 这不符合 PRG 模式 AFAIK 并且当然意味着刷
  • Java 7 中的 Beans Binding 将被什么取代?

    我在某处读到 我忘记了链接 Beans Binding 将不会成为 Java 7 的一部分 有人知道什么会取代它吗 另外 当前版本的 Java 中是否有 Bean 绑定的替代方案 我建议JGoodies 绑定 https binding d
  • 在循环中使用 if 语句? - 加工

    假设我必须在 for 循环中使用 if 语句 并且 for 循环在特定条件下触发 而 if 语句仅在 for 循环达到特定阶段时触发 例如 条件是一个计数器 当发生特定事件 例如球从屏幕上掉下来 时 该计数器会进行计数 每次球穿过屏幕时 都
  • Java 应用程序可以检测到调试器已连接吗?

    我知道 jvm 启动选项可以让 jvm 等待附加调试器 这不是我在这里的意思 是否有可能从 Java 代码中也检测调试器的附件 以便我可以例如编写一个正在执行某些操作的 脚本 然后在某个时刻让我的应用程序等待调试器 不会 这些选项是 JVM
  • 为什么 Java 类加载器找不到我的接口?

    在下面的代码中 我使用动态生成一个类sun tools javac Main 我将使用反射创建此类的新实例 问题是 我想避免使用 Reflection 来调用我为此类定义的方法 因此我创建了一个 ProxyInvoker 来引用我在项目中定
  • 如何在首次运行时填充大型 SQLite 数据库

    我正在开发一个基于 SQLite 数据库的字典应用程序 该数据库包含超过 300 000 行 问题在于 最终形式的数据库文件由全文索引表组成 并且重量远远超过150Mb 我通过创建无内容的 fts4 表设法将 db 文件大小降至最低 数据库
  • 返回 Consumer 表达式内的 Method 值

    我试图在方法中返回一个布尔值 并且我正在使用消费者函数 有什么方法可以直接在 Consumer 表达式中返回该值吗 这是代码 private static boolean uuidExists UUID uuid MySQL getResu
  • 在 Java/Android 中查找 UTF-8 字符串中的字符数

    我试图找出字符串以 UTF 8 存储时的长度 我尝试了以下方法 String str Charset UTF8 CHARSET Charset forName UTF 8 byte abc str getBytes UTF8 CHARSET
  • Preg_match PHP 到 java 的翻译

    我在将 php preg match 转换为 java 时遇到一些问题 我以为我的一切都是正确的 但它似乎不起作用 这是代码 原始PHP Pattern for 44 Character UUID pattern 0 9A F 44 if
  • 使用 TestRestTemplate 和 MockRestServiceServer 时,解析异常而不是实体列表不起作用

    我有一个简单的控制器 CODE https github com joergi tryouts blob main kotlin mockrestserver src main kotlin io joergi kotlinmockrest
  • 在java中的super调用之前创建一个对象

    考虑到简单的java代码是行不通的 public class Bar extends AbstractBar private final Foo foo new Foo bar public Bar super foo 我需要在之前创建一个
  • JavaFX:在 WebView img 标签中未加载本地图像

    以下是我的代码 一切安好 我可以加载远程页面 我可以放置 HTML 内容 但我的img标签显示一个X标志表示无法加载图像 Note 我的图像与类位于同一个包中JavaFX在 Smiley 文件夹中 我可以列出所有图像 这意味着路径没有问题
  • 在Android中创建自定义按钮类

    我正在尝试为我的 Android 应用程序创建自定义按钮类 public class TicTacButton extends Button 我已经在里面设置了所有构造函数TicTacButton并创建了自定义方法和属性 在我的主要活动中
  • 如何使用 Java 到 TestRail 的 API 将测试用例添加到现有测试运行中?

    我在执行期间创建了一个测试运行 我想在它们开始执行的同时添加测试用例 如果测试用例尚不存在 则已创建 并且该测试用例应该与其他测试用例一起添加到现有的测试运行中 我尝试过使用setCaseIds在运行期间和更新运行之后 但这会覆盖现有的运行
  • 数组所有可能的组合

    我有一个字符串数组 ted williams golden voice radio 我希望这些关键字的所有可能组合采用以下形式 ted williams golden voice radio ted williams ted golden
  • 无法使用 Struts 2 重定向 JSP 文件并显示值

    我创建了一个简单的程序 使用文本字段获取用户的名字和姓氏 但问题是 当我单击提交按钮时 我无法将其重定向到另一个显示用户名字和姓氏的 jsp 文件 这是我的HelloAction class package com novamsc trai
  • 为什么 JDOM 的 getChild() 方法返回 null?

    我正在做一个关于 html 文档操作的项目 我想要现有 html 文档中的正文内容将其修改为新的 html 现在我正在使用 JDOM 我想在我的编码中使用 body 元素 为此 我在编码中使用了 getChild body 但它向我的程序返

随机推荐

  • studyreport

    Vi Java Ant Junit的自学报告 1 vi vim编辑器的使用 1 1 vi vim简介 vi编辑器是Linux和Unix上最基本的文本编辑器 工作在字符模式下 由于不需要图形界面 vi是效率很高的文本编辑器 它可以执行输出 删
  • HTML侧面导航栏

  • 函数的模拟实现

    目录 题一 qsort 练习使用库函数 qsort排序各种类型的数据 模拟实现qsort在不同情形的实现 题二 strlen 模拟实现strlen 优解一 优解二 优解三 题三 strcpy 模拟实现strcpy 优解一 题四 strcmp
  • 使用Qt开发绘制多个设备的流量曲线图(附带项目图)

    一 说明 在实际项目中 主要是使用Qt开发CS程序 当然主要是客户端 公司项目中有这个需求是实时显示多个设备的流量曲线图 设备将流量信息发给服务端 服务端再将信息通过Socket发给Qt客户端 Qt客户端通过Socket接收后实时显示在程序
  • pip安装pytorch

    问题简述 直接pip install pytorch 报错 解决办法 1 登陆https pytorch org get started locally 2 选择对应的条件 最后会生成command 3 将command中的代码copy到命
  • Netty实战(七)EventLoop和线程模型

    EventLoop和线程模型 一 什么是线程模型 二 EventLoop 接口 2 14 Netty 4 中的 I O 和事件处理 三 任务调度 3 1 JDK 的任务调度 API 3 2 使用 EventLoop 调度任务 四 实现细节
  • 【廖雪峰python入门笔记】Unicode编码_UnicodeDecodeError处理

    1 Unicode编码的由来 字符串还有一个编码问题 计算机只能处理数字 如果要处理文本 就必须先把文本转换为数字才能处理 最早的计算机在设计时采用8个比特 bit 作为一个字节 byte 所以 一个字节能表示的最大的整数就是255 二进制
  • JAVA String的问题

    String s1 字符串 String s2 字符串 System out println s1 s2 输出true String s3 new String 字符串 String s4 new String 字符串 System out
  • 以太坊 geth常用命令行

    命令行组成为geth lt 命令 gt 参数 geth datadir 指定数据存储位置 也是默认的私钥仓库位置 nodiscover 标志此节点私有 不被别人添加 maxpeer 0 设置网络中可以被接入的最大节点数目 0代表不被其它节点
  • 【线性代数01】矩阵的转置和逆

    这方面的总结一直都有想写 我们先从矩阵的转置和逆谈起 本篇内容整理自网页 矩阵的转置和逆 给出这部分叙事的主角 矩阵A和矩阵B A 1
  • 【深度学习】语义分割-源代码汇总

    目录 Transformer 1 vit 2 Swin Transformer 3 PVT 4 SETR 5 segformer Transformer 1 vit 1 官方 vision transformer 2 Swin Transf
  • Linux内核:内存管理——CMA预留内存

    平时看 proc meminfo中 总能看到CMA的身影 且好像总是被用光了 他是做什么的呢 为啥作为预留内存能用的干干净净呢 一起看下 CmaTotal 438272 kB CmaFree 0 kB Contiguous Memory A
  • 电路设计中LDO与DC-DC的选择问题(DC-DC篇)

    版权声明 本文为博主原创文章 转载请注明出处 https blog csdn net NeverImagine article details 93193105 接上文 上文讨论了LDO的原理和特性 本文再分析一下DC DC 二 DC DC
  • 查看mysql root账户密码

    cat root mysql secret 查看root账号密码
  • 虹膜识别系统 python实现

    先上传效果图 main py An highlighted block Demonstration of the GazeTracking library Check the README md for complete documenta
  • 【从嵌入式视角学习香山处理器】六、NutShell代码结构(乱画的框图)

    文章目录 一 前言 二 简单粗暴版 最终成品的框图 三 不要太凌乱版 去掉连线后的框图 一 前言 这是从上一篇文章 从嵌入式视角学习香山处理器 五 香山开发工作流实践1 主要子模块工程之间的关系 引出的对果壳核 NutShell 一个简单入
  • MySQL存储过程入门教程及实例详解

    1 存储过程简介 存储过程是可编程的函数 在数据库中创建并保存 可以由SQL语句和控制结构组成 当想要在不同的应用程序或平台上执行相同的函数 或者封装特定功能时 存储过程是非常有用的 数据库中的存储过程可以看做是对编程中面向对象方法的模拟
  • 不同集合中判断元素相同的方法

    判断集合中的元素是否相同 对于增删改查有重要意义 不同Collection的实现的判断依据不同 1 List类 线性表 统一标准是equals 2 HashSet和HashMap 哈希表 先hashcode 后equals 3 TreeSe
  • k8s selector_k8s(二)——kubectl的使用以及创建deployment

    kubectl的使用以及创建deployment kubectl的使用 常见概念 kubectl管理命令概要 管理和使用deployment 基于deployment创建nginx pod 有一个副本 查看k8s对象状态 发布应用 服务伸缩
  • SpringBoot:切面AOP实现权限校验:实例演示与注解全解

    1 理解AOP 1 1 什么是AOP AOP Aspect Oriented Programming 面向切面思想 是Spring的三大核心思想之一 两外两个 IOC 控制反转 DI 依赖注入 那么AOP为何那么重要呢 在我们的程序中 经常