Spring之AOP的详细讲解

      

目录

一.SpringAOP是什么?

1.1理论知识点

1.2简单的AOP例子

二.SpringAOP的核心概念 

2.1切点(Pointcut)

2.2通知(Advice)

2.3切⾯(Aspect)

2.4通知类型

2.5切⾯优先级 @Order

2.6切点表达式

2.6.1 @execution表达式

2.6.2@annotation表达式

总结


一.SpringAOP是什么?

1.1理论知识点

        在学习SpringAOP前,我们需要了解一下什么是AOP?

         AOP(Aspect Oriented Programming):⾯向切⾯编程,通过预编译和运行期间动态代理来实现程序功能的统一维护的一种技术。 它是⼀种思想,它是对某⼀类事情的集中处理。
        ⽐如⽤户登录权限的效验,没学 AOP 之前,我们所有需要判断⽤户登录的⻚⾯(中
的⽅法),都要各⾃实现或调⽤⽤户验证的⽅法,然⽽有了 AOP 之后,我们只需要在某⼀处配置⼀下,所有需要判断⽤户登录⻚⾯(中的⽅法)就全部可以实现⽤户登录验证了,不再需要每个⽅法中都写相同的⽤户登录验证了。

        AOP中的基本单元是 Aspect(切面)

1.2简单的AOP例子

        理论永远没有代码直观!

引入依赖:

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

定义切面: 

@Aspect // 定义切面
@Component
public class UserAspect {// 切点@Pointcut("execution(* com.example.interview.Controller.UserController.*(..))")public void pointcut() {}// 前置通知通知@Before("pointcut()")public void doBefore() {System.out.println("执行了前置通知");}// 后置通知@After("pointcut()")public void doAfter() {System.out.println("执行了后置通知");}// 环绕通知@Around("pointcut()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("环绕通知执行之前");// 执行目标方法Object result = joinPoint.proceed();System.out.println("环绕通知执行之后");return result;}}

设计的Controller类:

package com.example.interview.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 "get user";}@RequestMapping("/deluser")public String delUser(){System.out.println("do delUser");return "del user";}}

执行结果:

二.SpringAOP的核心概念 

我们接下来分析一下切面代码:

从上面的代码中,我们可以得到哪些要素呢?

  • @Aspect:切面类,告诉Spring我这个类是个切面,里面有特殊处理方法
  • @Pointcut:切点,告诉Spring我要针对什么
  • @Before、@Around、@AfterReturning、@After、@AfterThrowing:通知,告诉Spring针对后要做什么处理

2.1切点(Pointcut)

切点(Pointcut), 也称之为"切⼊点"
Pointcut 的作⽤就是提供⼀组规则 (使⽤ AspectJ pointcut expression language 来描述), 告诉程序对 哪些⽅法来进⾏功能增强.也称:公共切点表达式!

如果我们不使用@Pointcut注释,将会让代码冗余大量的切点表达式!

不使用情况下:

@Aspect // 定义切面
@Component
public class UserAspect {// 前置通知通知@Before("execution(* com.example.interview.Controller.UserController.*(..))")public void doBefore() {System.out.println("执行了前置通知");}// 后置通知@After("execution(* com.example.interview.Controller.UserController.*(..))")public void doAfter() {System.out.println("执行了后置通知");}// 环绕通知@Around("execution(* com.example.interview.Controller.UserController.*(..))")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("环绕通知执行之前");// 执行目标方法Object result = joinPoint.proceed();System.out.println("环绕通知执行之后");return result;}}

我们会发现存在⼤量重复的切点表达 execution(*com.example.interview.Controller.UserController.*(..))")

execution,也可以说是连接点,就是告诉Spring,该路径下需要控制的方法,*代表的是所有方法,(..)代表任意参数。

注: 当切点定义使⽤private修饰时, 仅能在当前切⾯类中使⽤, 当其他切⾯类也要使⽤当前切点定义时, 就需要把private改为public. 引⽤⽅式为: 全限定类名.⽅法名()

例如:

@Slf4j
@Aspect
@Component
public class AspectDemo2 {//前置通知@Before("com.example.demo.aspect.AspectDemo.pt()")public void doBefore() {log.info("执⾏ AspectDemo2 -> Before ⽅法");}
}

2.2通知(Advice)

  • 通知包括前置通知、后置通知和环绕通知。
    • 前置通知在 doBefore() 方法中定义,使用了 @Before 注解,在切点方法执行之前被调用。
    • 后置通知在 doAfter() 方法中定义,使用了 @After 注解,在切点方法执行之后被调用。
    • 环绕通知在 doAround() 方法中定义,使用了 @Around 注解,在切点方法执行前后都可以进行一些额外的处理。环绕通知方法的参数类型为 ProceedingJoinPoint,可以通过调用 proceed() 方法执行目标方法,并在执行前后进行其他操作。

例如: 

2.3切⾯(Aspect)

       注: 切⾯(Aspect) = 切点(Pointcut) + 通知(Advice)
就是整个代码全是切面的知识点

2.4通知类型

Spring中AOP的通知类型有以下⼏种:
  • @Around: 环绕通知, 此注解标注的通知⽅法在⽬标⽅法前, 后都被执⾏
  • @Before: 前置通知, 此注解标注的通知⽅法在⽬标⽅法前被执⾏
  • @After: 后置通知, 此注解标注的通知⽅法在⽬标⽅法后被执⾏, ⽆论是否有异常都会执⾏
  • @AfterReturning: 返回后通知, 此注解标注的通知⽅法在⽬标⽅法后被执⾏, 有异常不会执⾏
  • @AfterThrowing: 异常后通知, 此注解标注的通知⽅法发⽣异常后执⾏

        前面五种,我们都可以通过之前代码看出,但是第五种是异常通知,程序正常运⾏的情况下, @AfterThrowing 标识的通知⽅法不会执⾏。但是如果发生异常了呢?什么会执行,上面不会执行呢?

  • @AfterReturning 标识的通知⽅法不会执⾏, @AfterThrowing 标识的通知⽅法执⾏了
  •   @Around 环绕通知中原始⽅法调⽤时有异常,通知中的环绕后的代码逻辑也不会在执⾏了(因为原始⽅法调⽤出异常了)

2.5切⾯优先级 @Order

        当我们在⼀个项⽬中, 定义了多个切⾯类时, 并且这些切⾯类的多个切⼊点都匹配到了同⼀个⽬标⽅法. 当⽬标⽅法运⾏的时候, 这些切⾯类中的通知⽅法都会执⾏, 那么这⼏个通知⽅法的执⾏顺序是什么样的呢?

切面定义三个,分别为AspectDemo2、AspectDemo3、AspectDemo4,为了简易化,只写@Before和@After,而这里只展示一个代码,其他在修改一下类名即可:

@Aspect // 定义切面
@Component
public class AspectDemo2 {@Pointcut("execution(* com.example.interview.Controller.UserController.*(..))")private void pt(){}// 前置通知通知@Before("pt()")public void doBefore() {System.out.println("执行了前置通知2");}// 后置通知@After("pt()")public void doAfter() {System.out.println("执行了后置通知2");}
}

UserControer代码:

@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/getuser")public String getUser(){System.out.println("do getUser");return "get user";}@RequestMapping("/deluser")public String delUser(){System.out.println("do delUser");return "del user";}}

访问对应的接口程序:http://localhost:8080/user/getuser

运行结果如下图:

通过对比我们可以发现:存在多个切⾯类时, 默认按照切⾯类的类名字⺟排序:

  •  @Before 通知:字⺟排名靠前的先执⾏
  •  @After 通知:字⺟排名靠前的后执⾏

问:如果我们需要指定某个切面先执行呢?

答: Spring 给我们提供了⼀个新的注解, 来控制这些切⾯通知的执⾏顺序: @Order

使用方式如下:

我们在切面类AspectDemo2、AspectDemo3、AspectDemo4上分别加上注解:@Order(3)、@Order(2)、@Order(1).

例如:

@Aspect // 定义切面
@Component
@Order(1)
public class AspectDemo4 {//代码照旧
}

 访问对应的接口程序:​​​​​​http://localhost:8080/user/getuser

运行结果: 

通过上述程序的运⾏结果, 得出结论:
@Order 注解标识的切⾯类, 执⾏顺序如下:
  • @Before 通知:数字越⼩先执⾏
  • @After 通知:数字越⼤先执⾏
@Order 控制切⾯的优先级, 先执⾏优先级较⾼的切⾯, 再执⾏优先级较低的切⾯, 最终执⾏⽬标⽅法.

2.6切点表达式

切点表达式常⻅有两种表达⽅式
  1.  execution(……):根据⽅法的签名来匹配
  2.  @annotation(……) :根据注解匹配

2.6.1 @execution表达式

execution() 是最常⽤的切点表达式, ⽤来匹配⽅法, 语法为:
 execution(<访问修饰符> <返回类型> <包名.类名.⽅法(⽅法参数)> <异常>)

切点表达式⽀持通配符表达:
  1. * :匹配任意字符,只匹配⼀个元素(返回类型, 包, 类名, ⽅法或者⽅法参数)
    1.  包名使⽤ * 表⽰任意包(⼀层包使⽤⼀个*)
    2.  类名使⽤ * 表⽰任意类
    3.  返回值使⽤ * 表⽰任意返回值类型
    4. ⽅法名使⽤ * 表⽰任意⽅法
    5. 参数使⽤ * 表⽰⼀个任意类型的参数
  2. .. :匹配多个连续的任意符号, 可以通配任意层级的包, 或任意类型, 任意个数的参数
    •  使⽤ .. 配置包名,标识此包以及此包下的所有⼦包
    • 可以使⽤ .. 配置参数,任意个任意类型的参数

2.6.2@annotation表达式

        execution表达式更适⽤有规则的, 如果我们要匹配多个⽆规则的⽅法呢, 
问:如果我们 匹配两个不同类的一个方法,怎么操作呢?
我们可以借助⾃定义注解的⽅式以及另⼀种切点表达式 @annotation 来描述这⼀类的切点

第一步准备测试方法:

@RequestMapping("/test")
@RestController
public class TestController {@RequestMapping("/t1")public String t1() {return "t1";}@RequestMapping("/t2")public boolean t2() {return true;}
}
@RequestMapping("/user")
@RestController
public class UserController {@RequestMapping("/u1")public String u1(){return "u1";}@RequestMapping("/u2")public String u2(){return "u2";}
}

第二步自定义注解@MyAspect

代码内容:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {}

注解解释:

一.@Target 标识了 Annotation 所修饰的对象范围, 即该注解可以⽤在什么地⽅. 常⽤取值:
  • ElementType.TYPE: ⽤于描述类、接⼝(包括注解类型) 或enum声明
  • ElementType.METHOD: 描述⽅法
  • ElementType.PARAMETER: 描述参数
  • ElementType.TYPE_USE: 可以标注任意类型
二. @Retention 指Annotation被保留的时间⻓短, 标明注解的⽣命周期,@Retention 的取值有三种:
  • RetentionPolicy.SOURCE:表⽰注解仅存在于源代码中, 编译成字节码后会被丢弃. 这意味着在运⾏时⽆法获取到该注解的信息, 只能在编译时使⽤. ⽐如 @SuppressWarnings , 以及 lombok提供的注解 @Data , @Slf4j
  • RetentionPolicy.CLASS:编译时注解. 表⽰注解存在于源代码和字节码中, 但在运⾏时会被丢弃. 这意味着在编译时和字节码中可以通过反射获取到该注解的信息, 但在实际运⾏时⽆法获 取. 通常⽤于⼀些框架和⼯具的注解.
  • RetentionPolicy.RUNTIME:运⾏时注解. 表⽰注解存在于源代码, 字节码和运⾏时中. 这意味着在编译时, 字节码中和实际运⾏时都可以通过反射获取到该注解的信息. 通常⽤于⼀些需要 在运⾏时处理的注解, 如Spring的 @Controller @ResponseBody

第三步:切面类定义,将@execution修改为@annotation  ,但是目标源为自定义的注解@MyAspect

@Slf4j
@Component
@Aspect
public class MyAspectDemo {//前置通知@Before("@annotation(com.example.demo.aspect.MyAspect)")public void before(){log.info("MyAspect -> before ...");}//后置通知@After("@annotation(com.example.demo.aspect.MyAspect)")public void after(){log.info("MyAspect -> after ...");}
}

第四步:在测试方法当中添加自定义的注解--@MyAspect

@MyAspect
@RequestMapping("/t1")
public String t1() {return "t1";
}@MyAspect
@RequestMapping("/u1")
public String u1(){return "u1";
}

第五步,访问

http://127.0.0.1:8080/test/t1, 切⾯通知执⾏
http://127.0.0.1:8080/user/u1 , 切⾯通知执⾏.
未添加注解:
http://127.0.0.1:8080/test/t2, 切⾯未通知执⾏
http://127.0.0.1:8080/user/u2 , 切⾯未通知执⾏.

总结

        Spring AOP(Aspect-Oriented Programming,面向切面编程)是 Spring 框架中的一个模块,用于实现横切关注点的模块化开发。代理是 Spring AOP 实现的一种方式。

        在 Spring AOP 中,代理是实现切面的一种方式之一。通过代理,Spring AOP 可以在目标对象的方法执行前、执行后或抛出异常时,执行额外的逻辑(如日志记录、性能监控、事务管理等)。Spring AOP 使用代理机制来实现横切关注点的织入。 

Spring AOP 实现代理的方式有两种:

  1. 基于 JDK 动态代理: 如果目标对象实现了至少一个接口,Spring AOP 就会使用 JDK 动态代理来为目标对象创建代理。在运行时,Spring AOP 会动态生成一个实现了目标对象所有接口的代理对象,并在代理对象的方法中织入切面逻辑。

  2. 基于 CGLIB 代理: 如果目标对象没有实现任何接口,Spring AOP 就会使用 CGLIB(Code Generation Library)来为目标对象创建代理。CGLIB 使用字节码生成技术,在运行时生成目标对象的子类,并重写其中的方法来织入切面逻辑。

详细见此章:http://t.csdnimg.cn/iAZZG

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

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

相关文章

天地人和•大道不孤——卢禹舜中国画作品展在重庆美术馆隆重开幕

2024年4月12日&#xff0c;由中国国家画院、重庆市文化和旅游发展委员会主办&#xff0c;重庆美术馆&#xff08;重庆画院、重庆国画院&#xff09;、北京八荒锦绣美术馆、中国国际文化交流基金会卢禹舜艺术基金承办的“天地人和•大道不孤——卢禹舜中国画作品展”开幕式在重庆…

jmeter实验 模拟:从CSV数据到加密请求到解密返回数据再到跨越线程组访问解密后的数据

注意,本实验所说的加密只是模拟加密解密,您需要届时写自己的加解密算法或者引用含有加密算法的相关jar包才行. 思路: 线程组1: 1.从CSV文件读取原始数据 2.将读取到的数据用BeanShell预习处理器进行加密 3.HTTP提取器使用加密后的数据发起请求 4.使用BeanShell后置处理器…

底层文件操作的各种函数(二)------printf,fprintf,sprintf,scanf,fscanf,sscanf的对比以及文件缓冲区

偷得几日清闲&#xff0c;又因一瞬之间对蹉跎时间的愧疚&#xff0c;由此而来到CSDN这个高手云集和新手求学的平台来也写上那么一篇博客。虽然自己的博客那么久不温不热&#xff0c;但坚持写作&#xff0c;巩固自己就好。今天要讲的是续接上一篇文章的补充与继续吧。上期文章&a…

MQ:延迟队列

6.1场景&#xff1a; 1.定时发布文章 2.秒杀之后&#xff0c;给30分钟时间进行支付&#xff0c;如果30分钟后&#xff0c;没有支付&#xff0c;订单取消。 3.预约餐厅&#xff0c;提前半个小时发短信通知用户。 A -> 13:00 17:00 16:30 延迟时间&#xff1a; 7*30 * 60 *…

GB/T 28181标准中的错误码,国标28181中可能出现的SIP协议相关的错误码及其含义

目录 一、GB/T 28181标准介绍 &#xff08;一&#xff09;概述 &#xff08;二&#xff09;关键内容和特点 1. 系统架构&#xff1a; 2. 设备接入&#xff1a; 3. 网络通信&#xff1a; 4. 业务功能&#xff1a; 5. 安全保护&#xff1a; 6. 平台管理&#xff1a; &a…

【C语言】字符串函数和内存函数及其模拟实现

文章目录 前言 一、常见字符串库函数1.strlen函数2.长度不受限制的字符串函数2.1 strcpy2.2 strcat2.3 strcmp 3.长度受限制的字符串函数3.1 strncpy3.2 strncat3.3 strncmp 二、字符串查找函数strstrstrtok 三、strerror函数四、内存操作函数1.memcpy2.memmove3.memcmp 五、字…

力扣刷题 二叉树层序遍历相关题目II

NO.116 填充每个节点的下一个右侧节点指针 给定一个 完美二叉树 &#xff0c;其所有叶子节点都在同一层&#xff0c;每个父节点都有两个子节点。二叉树定义如下&#xff1a; struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针&#xff0c;…

iOS开发如何更改xcode中的Apple ID

在Xcode中更改Apple ID是一项常见的任务&#xff0c;尤其是当你需要切换到另一个开发者账号或者团队时。下面是一个简单的步骤指南&#xff0c;帮助你更改Xcode中的Apple ID&#xff1a; 步骤一&#xff1a;退出当前的Apple ID 1.打开Xcode应用程序。 2.在菜单栏中&#xff0c;…

外贸公司应该怎么选择企业邮箱?哪个企业邮箱最好?

外贸公司业务的特殊性需要他们频繁进行跨国的沟通交流&#xff0c;那么外贸公司应该如何选择适合的企业邮箱呢&#xff1f;首先&#xff0c;传输邮件的稳定安全是前提&#xff0c;另外由于沟通多是国外客户&#xff0c;邮件的翻译也成为外贸公司企业邮箱的刚需。小编今天就详细…

ARMv8-A架构下的外部debug模型之外部调试事件(external debug events)概述

外部调试器与处理器之间的握手与external debug events 一&#xff0c;External Debug的使能二&#xff0c;外部调试器和CPU之间的握手三&#xff0c;外部调试事件 External debug events1. External debug request event2. Halt instruction debug event3. Halting step debug…

【从浅学到熟知Linux】环境变量详谈(含使用程序获取环境变量的3种方法、如何查看环境变量)

&#x1f3e0;关于专栏&#xff1a;Linux的浅学到熟知专栏用于记录Linux系统编程、网络编程及数据库等内容。 &#x1f3af;每天努力一点点&#xff0c;技术变化看得见 文章目录 环境变量基本概念查看环境变量的方法环境变量相关命令环境变量组织方式及获取环境变量的3种方法验…

Cesium.js--》探秘Cesium背后的3D模型魔力—加载纽约模型

今天简单实现一个Cesium.js的小Demo&#xff0c;加强自己对Cesium知识的掌握与学习&#xff0c;先简单对这个开源库进行一个简单的介绍吧&#xff01; Cesium 是一个开源的地理空间可视化引擎&#xff0c;用于创建基于 Web 的三维地球应用程序。它允许开发人员在网页上呈现高度…

Java基础第十一课——类与对象(2)

由于类与对象这一部分的知识点很多&#xff0c;而且操作方法也有很多&#xff0c;所以这次将继续深入讨论一下关于类与对象中方法传参、方法重载、构造方法以及this关键字使用方面的知识。 一、方法传参 1.return关键字 return关键字作用 作用场景&#xff1a;方法内 作用…

天猫精灵要会员,不能听歌,还能用来干什么呢?榨干它的剩余价值

目录 起因&#xff1a;以听歌为主要功能的设备&#xff0c;却不能听歌了 1.蓝牙音箱 2.控制智能家电 3.万能遥控器&#xff0c;需要一个外接设备 4.倒计时/提醒&#xff0c;闹钟提醒&#xff0c;整点提醒&#xff08;这功能有人不喜欢&#xff0c;闲吵&#xff0c;还不能关…

LeetCode题练习与总结:最小路径和--64

一、题目描述 给定一个包含非负整数的 m x n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。 说明&#xff1a;每次只能向下或者向右移动一步。 示例 1&#xff1a; 输入&#xff1a;grid [[1,3,1],[1,5,1],[4,2,1]] 输出…

UI设计规范

一套商城系统的诞生&#xff0c;除了代码的编写&#xff0c;UI设计也至关重要。UI设计关系到商城系统的最终呈现效果&#xff0c;关乎整体商城的风格展现&#xff0c;如果UI设计做不好&#xff0c;带来的负面影响也是不容小觑的。 1、在很多商城系统开发中&#xff0c;有时会有…

【Sql Server】锁表如何解锁,模拟会话事务方式锁定一个表然后进行解锁

大家好&#xff0c;我是全栈小5&#xff0c;欢迎来到《小5讲堂》。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言创建表模拟…

运放噪声评估的来龙去脉

运放噪声评估的来龙去脉 友情提示&#xff0c;运放电路的噪声分析还是比较复杂的&#xff0c;不论是基础理论还是对应的推导过程&#xff0c;都不是特别容易。考虑到兄弟们的基础参差不齐&#xff0c;所以我还是尽量说清楚点&#xff0c;这样导致看起来就有点罗里吧嗦&#xff…

10 Php学习:循环

在 PHP 中&#xff0c;提供了下列循环语句&#xff1a; while - 只要指定的条件成立&#xff0c;则循环执行代码块do…while - 首先执行一次代码块&#xff0c;然后在指定的条件成立时重复这个循环for - 循环执行代码块指定的次数foreach - 根据数组中每个元素来循环代码块 当…

【Java EE】获取Cookie和Session

文章目录 &#x1f38d;Cookie简介&#x1f340;理解Session&#x1f333;Cookie 和 Session 的区别&#x1f332;获取Cookie&#x1f338;传统获取Cookie&#x1f338;简洁获取Cookie &#x1f334;获取Session&#x1f338;Session存储&#x1f338;Session读取&#x1f33b;…