spring之AOP(面向切面编程)之详结

news/2024/5/9 16:20:51/文章来源:https://blog.csdn.net/qq_62636650/article/details/137434000

AOP面向切面编程,一种编程范式,指导开发者如何组织程序结构

作用:

在不惊动原始设计的基础上为其进行功能增强

核心概念

  • 代理:SpringAOP的核心本质是采用代理模式实现的
  • 连接点:程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等
    • 在springAOP中,理解为方法的执行
  • 切入点:匹配连接点的式子
    • 在springAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
  • 通知:在切入点出执行的操作,也就是共性功能,在AOP中,功能最终以方法的形式呈现
  • 通知类:定义通知的类
  • 切面:描述通知与切入点的对应关系

如果不理解的话可以看这一段话帮助理解:连接点(所有英雄)、切入点(增强的英雄)、通知类(buff库)、通知(各种buff)、切面(可以理解为设计师,从buff库里面拿出具体的buff加强给英雄)

现给一个入门案例:

注解配置类:

@Configuration
@ComponentScan("com.example")
@EnableAspectJAutoProxy  //切面相关注解
public class SpringConfig {
}

切面:

@Component  //交给spring来管理
@Aspect //声明这个类是切面
public class MyAdvice {//定义一个私有的切入点@Pointcut("execution(void com.example.dao.BookDao.update())")private void pt(){}//通知在方法前执行@Before("pt()")public void method(){System.out.println(System.currentTimeMillis());}
}

连接点及实现类:

public interface BookDao {public void save();public void update();
}
@Repository
public class BookDaoImpl implements BookDao {@Overridepublic void save() {System.out.println("book dao save...");}@Overridepublic void update() {System.out.println("book dao update...");}
}

测试:

public class Aop {public static void main(String[] args) {ApplicationContext ctx= new AnnotationConfigApplicationContext(SpringConfig.class);BookDao bookDao = ctx.getBean(BookDao.class);bookDao.update();}
}
//输出
1712407510166
book dao update...

工作流程

  1. Spring容器启动
  2. 读取所有切面配置中的切入点
  3. 初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
    1. 匹配失败,创建对象
      public static void main(String[] args) {ApplicationContext ctx= new AnnotationConfigApplicationContext(SpringConfig.class);BookDao bookDao = ctx.getBean(BookDao.class);//bookDao.update();System.out.println(bookDao);System.out.println(bookDao.getClass());}//输出
      com.example.dao.impl.BookDaoImpl@b3ca52e
      class com.example.dao.impl.BookDaoImpl

    2. 匹配成功,创建原始对象(目标对象)的代理对象。目标对象:原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的。代理:目标对象无法直接完成工作,需要对其进行功能回调,通过原始对象的代理对象实现
      public static void main(String[] args) {ApplicationContext ctx= new AnnotationConfigApplicationContext(SpringConfig.class);BookDao bookDao = ctx.getBean(BookDao.class);//bookDao.update();System.out.println(bookDao);System.out.println(bookDao.getClass());}//输出
      com.example.dao.impl.BookDaoImpl@7530ad9c
      class com.sun.proxy.$Proxy19
  4. 获取bean的执行方法
    1. 获取bean,调用方法并执行,完成操作
    2. 获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作

切入点表达式

  • 切入点:要进行增强的方法
  • 切入点表达式:要进行增强的方法的描述方式

1. 描述方式

  1. 方式1:执行com.example.dao包下的BookDao接口中的无参数update方法:execution(void com.example.dao.BookDao.update())
  2. 方式2:执行com.example.dao.impl包下的BookDaoImpl类中的无参数update方法:execution(void com.example.dao.impl.BookDaoImpl.update())

2. 切入点表达式的标准格式

格式:动作关键字(访问修饰符  返回值  包名.类/接口名.方法名(参数)异常名)

execution(public void com.example.dao.impl.BookDaoImpl.update(int))
  • 动作关键字:描述切入点的动作行为,例如execution表示执行到指定切入点
  • 访问修饰符:public、private等,可以省略
  • 返回值
  • 包名
  • 类/接口名
  • 方法名
  • 参数
  • 异常名:方法定义中抛出指定异常,可以省略

通配符描述切入点:

  • * :单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配出现
    //匹配com.example包下任意包中的BookDao类或接口中所有update开头的带有一个参数的方法
    execution(public * com.example.*.BookDao.update*(*))
  • .. :多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
    //匹配com包下任意包中的BookDao类或接口中所有名称为update的方法
    execution(public Book com..BookDao.update(..))
  • + :专用于匹配子类型
    execution(* *..*Dao+.*(..))

通知类型

  • AOP通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置
  • AOP通知共分为5种类型:
    • 前置通知:@Before
      @Component  //交给spring来管理
      @Aspect //声明这个类是切面
      public class MyAdvice {//定义一个私有的切入点@Pointcut("execution(void com.example.dao.BookDao.update())")private void pt(){}//通知在方法前执行@Before("pt()")public void method(){System.out.println(System.currentTimeMillis());}
      }
      //输出
      1712407510166
      book dao update...
    • 后置通知:@After。如果方法异常也会执行
      @Component  //交给spring来管理
      @Aspect //声明这个类是切面
      public class MyAdvice {//定义一个私有的切入点@Pointcut("execution(void com.example.dao.BookDao.update())")private void pt(){}@After("pt()")public void method(){System.out.println(System.currentTimeMillis());}
      }//输出
      book dao update...
      1712407576367
      
    • 环绕通知:@Around
      @Component  //交给spring来管理
      @Aspect //声明这个类是切面
      public class MyAdvice {//定义一个私有的切入点@Pointcut("execution(void com.example.dao.BookDao.update())")private void pt(){}@Around("pt()")public void method(ProceedingJoinPoint pjp) throws Throwable {System.out.println("around before advice...");//表示对原始操作的调用pjp.proceed();//如果方法有返回值的话用以下//Object proceed = pjp.proceed();System.out.println("around after advice...");}
      }//输出
      around before advice...
      book dao update...
      around after advice...
      
      • 注意事项:
        • 环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知
        • 通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行
        • 对原始方法的调用可以不接收返回值,通知方法设置成void即可,如果接收返回值,必须设定为Object类型
        • 原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object类型
        • 由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象
    • 返回后通知:@AfterRunning。方法执行完后执行,如果方法异常不会执行
    • 抛出异常后通知:@AfterThrowing。方法异常会执行,无异常则不执行

通知获取数据

  • 获取切入点方法的参数
    • JoinPoint:适用于前置、后置、返回后、抛出异常后通知,必须是第一个参数。现以前置为例:
      //接口实现类的方法
      @Override
      public String findName(int id) {System.out.println("id:"+id);return "test";
      }//前置通知
      //定义一个私有的切入点@Pointcut("execution(* com.example.dao.BookDao.findName(..))")private void pt(){}@Before("pt()")public void before(JoinPoint joinPoint){//获取方法的参数Object[] args = joinPoint.getArgs();System.out.println(Arrays.toString(args));System.out.println("before advice...");}//测试public static void main(String[] args) {ApplicationContext ctx= new AnnotationConfigApplicationContext(SpringConfig.class);BookDao bookDao = ctx.getBean(BookDao.class);String name = bookDao.findName(100);System.out.println(name);}//输出
      [100]
      before advice...
      id:100
      test
      
    • ​​​​​​​ProceedingJoinPoint:适用于环绕通知
       @Around("pt()")public Object around(ProceedingJoinPoint pjp) throws Throwable {//获取方法的参数Object[] args = pjp.getArgs();System.out.println(Arrays.toString(args));//可以对原始操作的参数进行修改args[0]=666;Object proceed = pjp.proceed(args);//args可加可不加,默认是方法的参数,修改后传递的是修改的数据return proceed;
      }
      //测试public static void main(String[] args) {ApplicationContext ctx= new AnnotationConfigApplicationContext(SpringConfig.class);BookDao bookDao = ctx.getBean(BookDao.class);String name = bookDao.findName(100);System.out.println(name);}//输出
      [100]
      id:666
      test
  • 获取切入点方法返回值
    • ​​​​​​​返回后通知
      @AfterReturning(value = "pt()",returning = "ret")public void afterReturning(Object ret){System.out.println("方法的返回值是:"+ret);System.out.println("afterReturning advice...");}
    • 环绕通知。
       @Around("pt()")public Object around(ProceedingJoinPoint pjp) throws Throwable {//获取方法的参数Object[] args = pjp.getArgs();System.out.println(Arrays.toString(args));//可以对原始操作的参数进行修改args[0]=666;Object proceed = pjp.proceed(args);//args可加可不加,默认是方法的参数,修改后传递的是修改的数据return proceed;//proceed就是方法的返回值
      }
      
  • 获取切入点方法运行异常信息
    • ​​​​​​​抛出异常后通知
      @AfterThrowing(value = "pt()",throwing = "t")public void afterThrowing(Throwable t){System.out.println("afterThrowing advice...");}
    • 环绕通知。
      @Around("pt()")public Object around(ProceedingJoinPoint pjp) throws Throwable {Object ret=null;try {ret= pjp.proceed();} catch (Throwable e) {e.printStackTrace();}return ret;}

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

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

相关文章

水牛社:互联网赚钱秘籍,免费项目,你真敢要吗?

免费是最贵的。真正理解并使用这句话的只有少数人,今天在网上分享一下免费项目背后的逻辑,抛开现象, 本质是最重要的。 我从事互联网工作15年。不管是过去还是现在,总有人喜欢问有没有免费项目? 其实我平时懒得回答…

如何使用 ChatGPT

原文:How To Use Chatgpt 译者:飞龙 协议:CC BY-NC-SA 4.0 总体介绍 在人工智能和在线创业不断扩张的世界中,ChatGPT 的出现为寻求利用 AI 推动在线成功的个人和企业开辟了令人兴奋的新途径。本书《如何使用 ChatGPT:…

【Linux】进程初步理解

个人主页 : zxctscl 如有转载请先通知 文章目录 1. 冯诺依曼体系结构1.1 认识冯诺依曼体系结构1.2 存储金字塔 2. 操作系统2.1 概念2.2 结构2.3 操作系统的管理 3. 进程3.1 进程描述3.2 Linux下的PCB 4. task_struct本身内部属性4.1 启动4.2 进程的创建方式4.2.1 父…

3 突破编程_前端_SVG(rect 矩形)

1 rect 元素的基本属性和用法 在SVG中&#xff0c;<rect> 元素用于创建矩形。 <rect> 元素有一些基本的属性&#xff0c;可以用来定义矩形的形状、位置、颜色等。以下是这些属性的详细解释&#xff1a; x 和 y &#xff1a;这两个属性定义矩形左上角的位置。 x …

106. 跑步锻炼(结果填空)

public class Main { public static void main(String[] args) { int startYear 2000; int startMonth 1; int startDay 1; // 周六 int endYear 2020; int endMonth 10; int endDay 1; // 周四 int totalDistance 0; // 计算开始日期到结束日期之间的每一天 …

应急响应-挖矿脚本检测指南威胁情报样本定性文件清除入口修复

一、演示案例-挖矿样本-Win&Linux-危害&定性 危害&#xff1a;CPU拉满&#xff0c;网络阻塞&#xff0c;服务器卡顿等 定性&#xff1a;威胁情报平台上传解析分析&#xff0c;文件配置查看等windows样本 linux样本 二、演示案例-Linux-Web安全漏洞导致挖矿事件 某公司…

一例简单的文件夹病毒的分析

概述 这是一个典型的文件夹病毒&#xff0c;使用xp时代的文件夹图标&#xff0c;通过可移动存储介质传播&#xff0c;会向http://fionades.com/ABIUS/setup.exe下载恶意载荷执行。 其病毒母体只是一个加载器&#xff0c;会在内存是解密加载一个反射型的dll&#xff0c;主要的…

【C++】缺省参数和函数重载

目录 1.缺省参数 1.1缺省参数的定义 1.2 缺省参数的简单应用 1.3 缺省参数分类&#xff1a;全缺省参数和半缺省参数 1.3.1半缺省参数 1.3.2全缺省参数 3.缺省参数注意事项&#xff1a;缺省参数不能在函数声明和定义中同时出现 4.函数重载 4.1 函数重载概念 4.2 函数参数类型…

2024年32款数据分析工具分五大类总览

数据分析工具在现代商业和科学中扮演着不可或缺的角色&#xff0c;为组织和个人提供了深入洞察和明智决策的能力。这些工具不仅能够处理大规模的数据集&#xff0c;还能通过强大的分析和可视化功能揭示隐藏在数据背后的模式和趋势。数据分析工具软件主要可以划分为以下五个类别…

uniapp Android 开发手机模拟器调试接口出现 Failed to connect to localhost/127.0.0.1:9999

{“errMsg”:“request:fail abort statusCode:-1 Failed to connect to localhost/127.0.0.1:9999”} 原因&#xff1a;使用模拟器或者手机调用API接口&#xff0c;首先保证在同一局域网&#xff0c;然后要使用 IPV4 的 IP 地址。 打开 cmd 输入 ipconfig 查看 ip 地址 替换代…

【java】spring打包找不到主类

背景 使用IDEA打包spring 一直报错&#xff0c;&#xff1a;IDEA spring Error: Could not find or load main class 解决 添加maven的打包命令&#xff1a; 添加&#xff0c;打包依赖到 jar包中 package assembly:single

蓝桥杯练习系统(算法训练)ALGO-958 P0704回文数和质数

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 一个数如果从左往右读和从右往左读数字是完全相同的&#xff0c;则称这个数为回文数&#xff0c;比如898,1221,15651都是回文数。编写…

创新指南|贝恩的产品经理RAPID框架:解决问题的分步指南,使决策过程既高效又民主

您是否曾发现自己陷入项目的阵痛之中&#xff0c;决策混乱、角色不明确、团队成员之间的冲突不断升级&#xff1f;作为产品经理&#xff0c;驾驭这艘船穿过如此汹涌的水域可能是令人畏惧的。应对这些挑战的关键在于采用清晰、结构化的决策方法。输入贝恩的 RAPID 框架&#xff…

Linux文件搜索工具(gnome-search-tool)

opensuse下安装: sudo zypper install gnome-search-tool 操作界面:

【Spring】SpringBoot整合Redis,用Redis实现限流(附Redis解压包)

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 本文介绍SpringBoot整合Redis并且进行接口的限流&#xff0c;文章主要介绍的是一种思想&#xff0c;具体代码还要结合实际。 一、Windows安装Redis Redis的解压包我放在了百度网盘上&#xff0c;有需要的可以下载。 R…

【第七篇】使用BurpSuite进行主动、被动扫描和主动、被动爬虫

文章目录 前言主动扫描被动扫描主动爬虫被动爬虫前言 Burp Scanner 既可以用作全自动扫描仪,也可以用作增强手动测试工作流程的强大手段。 扫描网站涉及两个阶段: 抓取内容和功能: Burp Scanner 首先在目标站点周围导航,密切反映真实用户的行为。它对站点的结构和内容以及…

06 Php学习:字符串

PHP 中的字符串变量 在 PHP 中&#xff0c;字符串是一种常见的数据类型&#xff0c;用于存储文本数据。字符串变量可以包含字母、数字、符号等字符&#xff0c;并且可以进行各种操作和处理。以下是关于 PHP 中字符串变量的一些重要信息&#xff1a; 定义字符串变量&#xff1…

Spring boot 入门 ---(一),2024年最新java进阶训练营

spring-snapshots http://repo.spring.io/snapshot spring-milestones http://repo.spring.io/milestone spring-boot-starter-parent是使用Spring Boot的一种不错的方式&#xff0c;但它 并不总是最合适的。有时你可能需要继承一个不同的父POM&#xff0c;或只是不喜欢我…

JVM面试整理--对象的创建和堆

文章目录 对象的创建过程是怎样的?对象在内存中的结构是怎样的&#xff08;专业的叫法&#xff1a;对象的内存布局&#xff09;对象在内存分配时使用的哪种方式&#xff08;有的地方也称为&#xff1a;分配算法&#xff09;知道什么是“指针碰撞”吗&#xff1f;知道什么是“空…

不允许在constexpr函数中进行声明

这是我用pycharm在windows系统下复现sfm深度学习网络(Deep Two-View Structure-from-Motion Revisited&#xff09;遇见的问题&#xff0c;复现时有段代码pytorch扩展cuda/c&#xff0c;pycharm中出现C标准相关的报错如下&#xff1a; 在网上查找很久无果&#xff0c;后面通过…