Spring-AOP概念及相关术语

news2024/12/22 18:49:34

AOP概念

  • 一、概述
    • 1、相关术语
    • 2、作用
  • 二、基于注解的AOP
    • 1、准备工作
    • 2、各种通知
    • 3、切入点表达式语法
    • 4、重用切入点表达式
    • 5、获取通知的相关信息
    • 6、切面的优先级


一、概述

回顾:
什么是面向对象
什么是AOP
AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善,它以通过预编译方式和运行期动态代理方式实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术。

1、相关术语

①横切关注点
从每个方法中抽取出来的同一类非核心业务。在同一个项目中,我们可以使用多个横切关注点对相关方法进行多个不同方面的增强。这个概念不是语法层面天然存在的,而是根据附加功能的逻辑上的需要:有十个附加功能,就有十个横切关注点。
②通知
每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。
前置通知:在被代理的目标方法前执行
返回通知:在被代理的目标方法成功结束后执行(寿终正寝)
异常通知:在被代理的目标方法异常结束后执行(死于非命)
后置通知:在被代理的目标方法最终结束后执行(盖棺定论)
环绕通知:使用try…catch…finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所
有位置.
在这里插入图片描述
③切面
封装通知方法的类。
④目标
被代理的目标对象。
⑤代理:向目标对象应用通知之后创建的代理对象。AOP中不需要我们手动的创建,封装的是代理模式。
⑥连接点:抽取横切关注点的位置
⑦切入点:定位连接点的方式

2、作用

简化代码:把方法中固定位置的重复的代码抽取出来,让被抽取的方法更专注于自己的核心功能,
提高内聚性。
代码增强:把特定的功能封装到切面类中,看哪里有需要,就往上套,被套用了切面逻辑的方法就
被切面给增强了。

二、基于注解的AOP

1、准备工作

①添加依赖

<!-- spring-aspects会帮我们传递过来aspectjweaver -->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aspects</artifactId>
	<version>5.3.1</version>
</dependency>

②准备被代理的目标资源
接口:

public interface Calculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}

实现类:

@Component
public class CalculatorImpl implements Calculator {
    @Override
    public int add(int i, int j) {
        int result = i + j;
        System.out.println("方法内部,result:"+result);
        return result;
    }

    @Override
    public int sub(int i, int j) {
        int result = i - j;
        System.out.println("方法内部,result:"+result);
        return result;
    }

    @Override
    public int mul(int i, int j) {
        int result = i * j;
        System.out.println("方法内部,result:"+result);
        return result;
    }

    @Override
    public int div(int i, int j) {
        int result = i / j;
        System.out.println("方法内部,result:"+result);
        return result;
    }
}

创建切面类并配置:

@Component
//将当前组件标识为切面
@Aspect
public class LoggerAspect {

    @Pointcut("execution(* org.example.spring.aop.annotation.CalculatorImpl.*(..))")
    public void pointCut(){}

    //@Before("execution(public int com.atguigu.spring.aop.annotation.CalculatorImpl.add(int, int))")
    //@Before("execution(* com.atguigu.spring.aop.annotation.CalculatorImpl.*(..))")
    @Before("pointCut()")
    public void beforeAdviceMethod(JoinPoint joinPoint) {
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        //获取连接点所对应方法的参数
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",参数:"+ Arrays.toString(args));
    }

    @After("pointCut()")
    public void afterAdviceMethod(JoinPoint joinPoint){
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",执行完毕");
    }

    /**
     * 在返回通知中若要获取目标对象方法的返回值
     * 只需要通过@AfterReturning注解的returning属性
     * 就可以将通知方法的某个参数指定为接收目标对象方法的返回值的参数
     */
    @AfterReturning(value = "pointCut()", returning = "result")
    public void afterReturningAdviceMethod(JoinPoint joinPoint, Object result){
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",结果:"+result);
    }

    /**
     * 在异常通知中若要获取目标对象方法的异常
     * 只需要通过AfterThrowing注解的throwing属性
     * 就可以将通知方法的某个参数指定为接收目标对象方法出现的异常的参数
     */
    @AfterThrowing(value = "pointCut()", throwing = "ex")
    public void afterThrowingAdviceMethod(JoinPoint joinPoint, Throwable ex){
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",异常:"+ex);
    }

    @Around("pointCut()")
    //环绕通知的方法的返回值一定要和目标对象方法的返回值一致
    public Object aroundAdviceMethod(ProceedingJoinPoint joinPoint){
        Object result = null;
        try {
            System.out.println("环绕通知-->前置通知");
            //表示目标对象方法的执行
            result = joinPoint.proceed();
            System.out.println("环绕通知-->返回通知");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕通知-->异常通知");
        } finally {
            System.out.println("环绕通知-->后置通知");
        }
        return result;
    }

}

在Spring的配置文件中配置:

    <!--
        AOP的注意事项:
        切面类和目标类都需要交给IOC容器管理
        切面类必须通过@Aspect注解标识为一个切面
        在Spring的配置文件中设置<aop:aspectj-autoproxy />开启基于注解的AOP
    -->
    <context:component-scan base-package="org.example.spring.aop.annotation">
    </context:component-scan>

    <!--开启基于注解的AOP-->
    <aop:aspectj-autoproxy />

2、各种通知

前置通知:使用@Before注解标识,在被代理的目标方法前执行
返回通知:使用@AfterReturning注解标识,在被代理的目标方法成功结束后执行(寿终正寝)
异常通知:使用@AfterThrowing注解标识,在被代理的目标方法异常结束后执行(死于非命)
后置通知:使用@After注解标识,在被代理的目标方法最终结束后执行(盖棺定论)
环绕通知:使用@Around注解标识,使用try…catch…finally结构围绕整个被代理的目标方法,包
括上面四种通知对应的所有位置
各种通知的执行顺序:
Spring版本5.3.x以前:
前置通知
目标操作
后置通知
返回通知或异常通知
Spring版本5.3.x以后:
前置通知
目标操作
返回通知或异常通知
后置通知

3、切入点表达式语法

语法细节

  • 用*号代替“权限修饰符”和“返回值”部分表示“权限修饰符”和“返回值”不限
  • 在包名的部分,一个“*”号只能代表包的层次结构中的一层,表示这一层是任意的。
    • 例如:*.Hello匹配com.Hello,不匹配com.spring.Hello
  • 在包名的部分,使用“*…”表示包名任意、包的层次深度任意
  • 在类名的部分,类名部分整体用*号代替,表示类名任意
  • 在类名的部分,可以使用*号代替类名的一部分
    • 例如:*Service匹配所有名称以Service结尾的类或接口
  • 在方法名部分,可以使用*号表示方法名任意
  • 在方法名部分,可以使用*号代替方法名的一部分
    • 例如:*Operation匹配所有方法名以Operation结尾的方法
  • 在方法参数列表部分,使用(…)表示参数列表任意
  • 在方法参数列表部分,使用(int,…)表示参数列表以一个int类型的参数开头
  • 在方法参数列表部分,基本数据类型和对应的包装类型是不一样的
    • 切入点表达式中使用 int 和实际方法中 Integer 是不匹配的
  • 在方法返回值部分,如果想要明确指定一个返回值类型,那么必须同时写明权限修饰符
    • 例如:execution(public int …Service.*(…, int)) 正确
    • 例如:execution(* int …Service.*(…, int)) 错误
      在这里插入图片描述
      举例:CalculatorImpl类下的具有任意参数的任意方法
  @Pointcut("execution(* org.example.spring.aop.annotation.CalculatorImpl.*(..))")

4、重用切入点表达式

①声明

    @Pointcut("execution(* org.example.spring.aop.annotation.CalculatorImpl.*(..))")
    public void pointCut(){}

②在同一个切面中使用

@Before("pointCut()")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}

③在不同切面中使用

@Before("org.example.spring.aop.annotation.LoggerAspect.pointCut()")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}

5、获取通知的相关信息

①获取连接点信息

    @Before("pointCut()")
    public void beforeAdviceMethod(JoinPoint joinPoint) {
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        //获取连接点所对应方法的参数
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",参数:"+ Arrays.toString(args));
    }

②获取目标方法的返回值
@AfterReturning中的属性returning,用来将通知方法的某个形参,接收目标方法的返回值

    /**
     * 在返回通知中若要获取目标对象方法的返回值
     * 只需要通过@AfterReturning注解的returning属性
     * 就可以将通知方法的某个参数指定为接收目标对象方法的返回值的参数
     */
    @AfterReturning(value = "pointCut()", returning = "result")
    public void afterReturningAdviceMethod(JoinPoint joinPoint, Object result){
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",结果:"+result);
    }

③获取目标方法的异常
@AfterThrowing中的属性throwing,用来将通知方法的某个形参,接收目标方法的异常

    /**
     * 在异常通知中若要获取目标对象方法的异常
     * 只需要通过AfterThrowing注解的throwing属性
     * 就可以将通知方法的某个参数指定为接收目标对象方法出现的异常的参数
     */
    @AfterThrowing(value = "pointCut()", throwing = "ex")
    public void afterThrowingAdviceMethod(JoinPoint joinPoint, Throwable ex){
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",异常:"+ex);
    }

环绕通知

    @Around("pointCut()")
    //环绕通知的方法的返回值一定要和目标对象方法的返回值一致
    public Object aroundAdviceMethod(ProceedingJoinPoint joinPoint){
        Object result = null;
        try {
            System.out.println("环绕通知-->前置通知");
            //表示目标对象方法的执行
            result = joinPoint.proceed();
            System.out.println("环绕通知-->返回通知");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕通知-->异常通知");
        } finally {
            System.out.println("环绕通知-->后置通知");
        }
        return result;
    }

6、切面的优先级

  • 相同目标方法上同时存在多个切面时,切面的优先级控制切面的内外嵌套顺序。
    • 优先级高的切面:外面
    • 优先级低的切面:里面
  • 使用@Order注解可以控制切面的优先级:
    • @Order(较小的数):优先级高
    • @Order(较大的数):优先级低
      在这里插入图片描述

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

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

相关文章

STM32F103的GPIO

文章目录 STM32F103的地址STM32F103的GPIOA的地址推算 STM32F103的地址 GPIO 都是挂载在 APB2 总线之上。 STM32F103的GPIOA的地址推算 &#xff08;出自STM32F103开发指南P127&#xff09; GPIOA 的 7 个寄存器都是 32 位的&#xff0c;所以每个寄存器占有 4个地址&#x…

Web入门笔记

Web入门笔记 HTTP协议 超文本传输协议 规定了浏览器和服务器之间数据传输的规则&#xff0c;请问数据和响应数据的格式 基于TCP请求-响应模式一次请求对应一次响应无状态的协议 请问数据格式 浏览器版本&#xff1a;解决浏览器兼容问题。GET请求体&#xff1a;存放请求参数…

缓解大模型幻觉问题的解决方案

本文记录大模型幻觉问题的相关内容。 参考&#xff1a;Mitigating LLM Hallucinations: a multifaceted approach 地址&#xff1a;https://amatriain.net/blog/hallucinations &#xff08;图&#xff1a;解决大模型幻觉的不同方式&#xff09; 什么是幻觉&#xff1f; 幻觉…

木疙瘩的进度动画实现文字逐个出现路径描边

第一步 创建素材 第二步 插入进度动画 第三步&#xff0c;在最后一帧插入关键帧即可&#xff01;

Linux之系统编程

1.yum 1.yum list可以出现所有可下载的程序 辅助grep进行查找 2.yum install可以下载并安装 3.yum remove可以卸载程序 不同的商业操作系统内核都是一样的&#xff0c;主要是配套社区不一样。 开源组织&#xff0c;各大公司&#xff0c;既得利益者。 同上 基础软件源可以保证…

腾讯云轻量服务器地域选择教程,一篇文章就够了

腾讯云轻量应用服务器地域是指轻量服务器数据中心所在的地理位置&#xff0c;如上海、广州和北京等地域&#xff0c;如何选择地域&#xff1f;腾讯云百科txybk.com建议地域选择遵循就近原则&#xff0c;用户距离轻量服务器地域越近&#xff0c;网络延迟越低&#xff0c;速度就越…

Linux mv命令:移动文件或改名

mv 命令&#xff08;move 的缩写&#xff09;&#xff0c;既可以在不同的目录之间移动文件或目录&#xff0c;也可以对文件和目录进行重命名。该命令的基本格式如下&#xff1a; [rootlocalhost ~]# mv 【选项】 源文件 目标文件 -f&#xff1a;强制覆盖&#xff0c;如果目标文…

问题 Y: LELE的RPG难题(分类讨论,三维)

算法如下 &#xff1a; eg.f(n,1,3)表示 以N为长度&#xff0c;以第一种颜色开头&#xff0c;3为结尾的合法字符串数量 故代码实现&#xff1a;

Docker:数据卷挂载

Docker&#xff1a;数据卷挂载 1. 数据卷2. 数据卷命令补充 1. 数据卷 数据卷(volume)是一个虚拟目录&#xff0c;是容器内目录与宿主机目录之间映射的桥梁。 Nginx容器有自己独立的目录(Docker为每个镜像创建一个独立的容器,每个容器都是基于镜像创建的运行实例)&#xff0c;…

macOS M1安装wxPython报错

macOS12.6.6 M1安装wxPython失败&#xff1a; 报错如下&#xff1a; imagtiff.cpp:37:14: fatal error: tiff.h file not found解决办法&#xff1a; 下载源文件重新编译&#xff08;很快&#xff0c;5分钟全部搞定&#xff09;&#xff0c;分三步走&#xff1a; 第一步&…

蚁群算法求包含34个国内城市的TSP,和最优解相差没那么大

文章目录 引言蚁群觅食算法原理代码实现ACO求解TSP整数规划求解TSP 相关阅读 引言 上一篇介绍的差分进化算法&#xff0c;很适合求解连续变量的优化问题&#xff1b;但针对组合优化问题&#xff0c;就不是很适用了。 至于哪一种智能优化算法更适合求解组合优化问题&#xff0…

STM32F10xx 存储器和总线架构

一、系统架构 在小容量、中容量和大容量产品 中&#xff0c;主系统由以下部分构成&#xff1a; 四个驱动单元 &#xff1a; Cotex-M3内核、DCode总线&#xff08;D-bus&#xff09;和系统总线&#xff08;S-bus&#xff09; 通用DMA1和通用DMA2 四个被动单元 内部SRAM 内部…

二叉树的概念

文章目录 二叉树一、树的概念1.树形结构1.1. 树的特点&#xff1a;1.2 概念&#xff1a;1.3 树的表示形式 2.树的应用 二、二叉树1.二叉数的概念2.满二叉树3.完全二叉树4.二叉树的性质练习&#xff1a; 二叉树 一、树的概念 1.树形结构 1.1. 树的特点&#xff1a; 1.根节点没…

SOLIDWORKS Simulation2024仿真10大新功能

SOLIDWORKS Simulation新增功能 1. 增强型轴承接头 •通过指定压缩、拉伸和弯曲的刚度&#xff0c;轻松创建自定义轴承接头。•通过向非线性和大型位移算例添加自定义条件&#xff0c;提高模拟精度。 优点&#xff1a;使用功能强大的接口&#xff0c;更轻松 、 更 准 确 地 设…

Spring-声明式事务

声明式事务 一、简介1、准备工作2、测试 二、声明式事务概念1、编程式事务2、声明式事务3、基于注解的声明式事务1.测试无事务情况2.加入事务①Transactional注解标识的位置②事务属性&#xff1a;只读③事务属性&#xff1a;超时④事务属性&#xff1a;回滚策略⑤事务属性&…

表单(form)

1、表单的作用&#xff1a; 收集 用户填入的 数据&#xff0c;并将这些数据 提交给服务器表单的语法 <form action"服务器地址" method"请求方式" enctype"数据格式"><!-- 表单项 --><input type"submit" value"提…

[毕设记录]@开题调研:外文博客搬运整理

搜college chatbot 好像比搜 university chatbot能搜出来的东西更多哈哈 我感觉&#xff0c;AI时代&#xff0c;粗看这种博客比较高效的方式是&#xff0c;用翻译插件迅速概览中文&#xff0c;然后复制给gpt让它总结要点&#xff0c;然后再去看重点部分的原文 文章目录 blog1&a…

【计算机网络】路由器的工作原理

文章目录 输入端口处理和基于目的地转发交换结构输出端口处理排队问题参考资料 路由器的四个组件 输入端口(input port)&#xff1a;执行物理层功能&#xff08;input port 左边方框、output port 右边方框&#xff09;、数据链路层功能&#xff08;input/output port 中间方框…

帮你快速解锁忘记密码手机的十个工具

将手机解锁到任时候都会让人感觉呼吸新鲜空气。这就像摆脱无形的锁链一样&#xff0c;让您有权选择并避免那些讨厌的限制。但如何解锁手机呢&#xff1f;这就是解锁软件发挥作用的地方。这些方便的工具可以帮助您摆脱束缚并打开一个充满可能性的世界。 解锁手机的合法性 现在&…

【C++代码】爬楼梯,不同路径,整数拆分,不同搜索树,动态规划--代码随想录

动态规划&#xff0c;英文&#xff1a;Dynamic Programming&#xff0c;简称DP&#xff0c;如果某一问题有很多重叠子问题&#xff0c;使用动态规划是最有效的。所以动态规划中每一个状态一定是由上一个状态推导出来的&#xff0c;这一点就区分于贪心&#xff0c;贪心没有状态推…