Spring AOP基于注解方式实现和细节

目录

一、Spring AOP底层技术

二、初步实现AOP编程

三、获取切点详细信息

四、 切点表达式语法

五、重用(提取)切点表达式


一、Spring AOP底层技术

SpringAop的核心在于动态代理,那么在SpringAop的底层的技术是依靠了什么技术呢?

  • 动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口(兄弟两个拜把子模式)。
  • cglib:通过继承被代理的目标类实现代理,所以不需要目标类实现接口。
  • AspectJ:早期的AOP实现的框架,SpringAOP借用了AspectJ中的AOP注解。

二、初步实现AOP编程

2.1实现AOP需要以下注解:

注解说明
@SpringJUnitConfig在JUnit测试类中使用Spring测试上下文配置
@Aspect将类标记为切面类,定义切面逻辑和增强方法的位置
@EnableAspectJAutoProxy开启AspectJ自动代理,用于启用Spring AOP的功能

2.2需要导入以下依赖

<!-- 切面实现 --><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>6.0.6</version>
</dependency>
<!-- spring核心 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.2.RELEASE</version></dependency>
<!-- spring-test容器测试 --><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>6.0.6</version><scope>test</scope></dependency>

2.3 增强(通知)注解

注解说明
@Before在目标方法执行前执行的增强逻辑
@AfterReturning在目标方法成功返回后执行的增强逻辑
@AfterThrowing在目标方法抛出异常后执行的增强逻辑
@After在目标方法执行后执行的增强逻辑
@Around包裹目标方法,在目标方法执行前后都可以执行自定义的增强逻辑

实现增强(通知)的步骤

  1. 定义方法存储增强代码
  2. 使用注解配置,指定插入目标的位置
  3. 配置切点表达式(选中插入的方法,切点)
  4. 补全注解,加入到ioc容器,并且设置切面@Aspect
  5. 开启Aspect注解注释

案例代码:

//4.补全注解
@Component
@Aspect//1.创建增强类与增强方法start(),after,Error
public class advice {//    2.使用注解配置,配置插入位置@Before @After @AfterThrowing
//    3.配入切点表达式execution(* com.alphamilk.Impl.*.*(..))表明需要插入的方法为所有com.alhpamilk.Impl包下所有类的所有方法@Before("execution(* com.alphamilk.Impl.*.*(..))")public void  start(){System.out.println("方法起始处插入");}@After("execution(* com.alphamilk.Impl.*.*(..))")public void after(){System.out.println("方法结束后插入");}@AfterThrowing("execution(* com.alphamilk.Impl.*.*(..))")public void Error(){System.out.println("方法异常时候插入");}
}
@ComponentScan(value = "com.alphamilk")
@Configuration
//6.注解类中开启注解注释
@EnableAspectJAutoProxy
public class JavaConfig {
}


三、获取切点详细信息

虽然已经初步实现了AOP的实现,但是还不够,在调用多个方法时候如果都是输入,调用方法前,调用方法后等等,这样并不能区分是调用了哪个方法,所以为了区分我们需要获取调用这个方法的相关信息,比如参数,方法名,返回值等等。

具体实现方式:
通过JoinPoint接口的下面几个方法获取

方法说明
getTarget()获取被代理的目标对象
getClass()获取被代理的目标对象的类
getSimpleName()获取被代理的目标对象的简单类名(不含包名)
getArgs()获取方法参数数组
getSignature()获取方法签名,包括方法名、返回类型、参数类型等信息
getModifiers()获取方法修饰符

有三个案例分别是一般情况,需要返回值情况,还有异常情况

一般情况(前置通知、后置通知)

案例代码:

需要在方法调用中参数加入JoinPoint接口实例化对象用以创建对应的动态代理,并通过动态代理获取对象相关信息。

public class advice {@Before("execution(* com.alphamilk.*.*(..))")public void Before(JoinPoint joinPoint) {
//        获取类名String simpleName = joinPoint.getTarget().getClass().getSimpleName();
//        获取方法修饰符int modifiers = joinPoint.getSignature().getModifiers();String Moidfier = Modifier.toString(modifiers);
//        获取方法名称String name = joinPoint.getSignature().getName();
//        获取参数Object[] args = joinPoint.getArgs();
//System.out.println("调用的方法是" + name);System.out.println("调用的类是" + simpleName);for (Object arg : args) {System.out.println(arg);}System.out.println("调用方法前");}@After("execution(* com.alphamilk.*.*(..))")public void After(JoinPoint joinPoint) {System.out.println("调用方法后");}
}

有返回值的情况(返回通知)

在一般情况的前提下,还需要多增加Object result参数用以接收返回值.和注解增加returning输入确切的返回对象的名称。

案例代码

public class advice {@AfterReturning(value = "execution(* com.alphamilk.*.*(..))",returning = "result")public void AfterReturning(JoinPoint joinPoint,Object result) {System.out.println("调用拥有返回值的方法");System.out.println("获取到的返回值为"+result);}
}

异常情况(异常通知)

异常通知,获取异常信息,需要在一般情况的前提下,在注解中多声明一个注解throwing,在方法参数增加一个Throwable对象,并且throwing注解对应的值就是Throwable的对象名称。

案例代码:

 @AfterThrowing(value = "execution(* com.alphamilk.*.*(..))",throwing = "throwable")public void AfterThrowing(JoinPoint joinPoint,Throwable throwable) {System.out.println("调用有异常的方法");System.out.println("异常对象为"+throwable.getClass().getName());}
@SpringJUnitConfig(value = JavaConfig.class)
public class newaopTest {@Autowiredprivate Caculate caculate;@Testpublic void Test(){caculate.div(2,0);}
}


四、 切点表达式语法

1.切点表达式作用

AOP切点表达式(Pointcut Expression)是一种用于指定切点的语言,它可以通过定义匹配规则,来选择需要被切入的目标对象。

2.切点表达式语法

  • 具体值:

    • (String, int):第一个参数是字符串,第二个参数是整数。
    • (int, String):第一个参数是整数,第二个参数是字符串。
    • ():没有参数。
  • 模糊值:

    • (..):任意参数,有或者没有。
  • 部分具体和模糊:

    • (String..):第一个参数是字符串,后面可能有其他参数。
    • (..String):最后一个参数是字符串,前面可能有其他参数。
    • (String..int):字符串开头,最后一个参数是整数,中间可能有其他参数。
    • (..int..):包含整数类型的参数,位置不限,可能有其他参数。

具体实战案例:

1.查询某包某类下,访问修饰符是公有,返回值是int的全部方法

execution public int 某包.某类.*(..)

2.查询某包下类中第一个参数是String的方法

execution * 某包.某类.*(String..)

3.查询全部包下,无参数的方法!

execution * *..*.*( )

4.查询com包下,以int参数类型结尾的方法

execution * com..*.*(..int)

5.查询指定包下,Service开头类的私有返回值int的无参数方法

execution private int 指定包.Service*.*()


五、重用(提取)切点表达式

如果在每一个方法前都加上一个固定的切点表达式,那么将会十分麻烦,所以下面介绍切点表达式的重用

1.在当前类中提取

特定注解@Pointcut

注解描述
@Pointcut声明切点表达式的方法,用于定义切点的匹配规则。

通过定义一个空方法,使用@Pointcut注解并带上特定的切点表达式

案例代码:

@Component
@Aspect
public class advice {/*定义空方法空方法上加上注解@Pointcut并带上相应的切点表达式在其他增强方法上调用方法*/@Pointcut("execution(* com.alphamilk.*.*(..))" )public void blank(){}@Before("blank()")public void Before(JoinPoint joinPoint) {System.out.println("调用方法前");}@After("blank()")public void After(JoinPoint joinPoint) {System.out.println("调用方法后");}@AfterReturning(value = "blank()",returning = "result")public void AfterReturning(JoinPoint joinPoint,Object result) {System.out.println("调用拥有返回值的方法");}@AfterThrowing(value = "blank()",throwing = "throwable")public void AfterThrowing(JoinPoint joinPoint,Throwable throwable) {System.out.println("调用有异常的方法");}
}

2.创建一个存储切点类

(推荐)通过创建一个单独的存储切点的类,更加容易进行维护表达式

  使用时候加上特定类的方法名即可

案例:

存储切点的类

@Component
public class MyPointcut {@Pointcut("execution(* com.alphamilk.Impl.*.*(..))")public void pointcut1(){}
}

对应引用类

@Component
@Aspect
public class advice {@Before("com.alphamilk.Advice.MyPointcut.pointcut1()")public void Before(JoinPoint joinPoint) {System.out.println("调用方法前");}@After("com.alphamilk.Advice.MyPointcut.pointcut1()")public void After(JoinPoint joinPoint) {System.out.println("调用方法后");}@AfterReturning(value = "com.alphamilk.Advice.MyPointcut.pointcut1()",returning = "result")public void AfterReturning(JoinPoint joinPoint,Object result) {System.out.println("调用拥有返回值的方法");}@AfterThrowing(value = "com.alphamilk.Advice.MyPointcut.pointcut1()",throwing = "throwable")public void AfterThrowing(JoinPoint joinPoint,Throwable throwable) {System.out.println("调用有异常的方法");}
}

本章总结

1.SpringAop底层技术

        了解底层代理技术有jdk 与 cglib

2.初步实现AOP编程

        掌握增强注解(@Before、@AfterReturning、@AfterThrowing、@After、@Around)

        掌握@Aspect注解的使用

3.获取切点详细信息

        掌握如何通过JoinPoint接口对象获取对应方法的类,方法名称,参数,方法修饰符

        掌握三种不同情况下获取对应信息的情况(一般情况、返回通知、异常通知)

4.切点表达式语法

        熟悉切点表达式的格式

      (execution +权限修饰 +方法返回值类型+方法所在全类名+方法名称+参数列表)

5.重用(提取)切点表达式

        

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

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

相关文章

CSS按钮-跑马灯边框

思路很简单&#xff0c;实现方法有很多很多。但是大体思路与实现方法都类似&#xff1a;渐变色 动画&#xff0c;主要区别在动画的具体实现 0、HTML 结构 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><titl…

服务网格实施周期缩短 50%,丽迅物流基于阿里云 ACK 和 ASM 的云原生应用管理实践

作者&#xff1a;王夕宁、 刘强、 华相 公司介绍 丽迅物流是百丽旗下专注于时尚产业、为企业提供专业物流及供应链解决方案的服务商。其产品服务主要包括城市落地配、仓配一体、干线运输及定制化解决方案。通过自研智能化物流管理平台&#xff0c;全面助力企业合作集约化发展…

4年外包出来,5次面试全挂....

我的情况 大概介绍一下个人情况&#xff0c;男&#xff0c;毕业于普通二本院校非计算机专业&#xff0c;18年跨专业入行测试&#xff0c;第一份工作在湖南某软件公司&#xff0c;做了接近4年的外包测试工程师&#xff0c;今年年初&#xff0c;感觉自己不能够再这样下去了&…

leetcode 739. 每日温度

2023.8.28 本题用暴力双层for循环解会超时&#xff0c;所以使用单调栈来解决&#xff0c;本质上是用空间换时间。维护一个单调递减栈&#xff0c;存储的是数组的下标。 代码如下&#xff1a; class Solution { public:vector<int> dailyTemperatures(vector<int>&…

CPU和GPU的区别

介绍什么是GPU, 那就要从CPU和GPU的比较不同中能更好更快的学习到什么是GPU CPU和GPU的总体区别 CPU&#xff1a; 叫做中央处理器&#xff08;central processing unit&#xff09; 可以形象的理解为有25%的ALU(运算单元)、有25%的Control(控制单元)、50%的Cache(缓存单元)…

笔记:transformer系列

1、和其他网络的比较 CNN归纳偏置&#xff08;inductive bias&#xff0c;目标函数的必要假设) 1、平移不变性&#xff1a;平移旋转缩放等变化&#xff0c;CNN依旧能够识别 2、空间局部性&#xff1a;局部像素联系密切&#xff0c;因此每个神经元无需有全局感知&#xff0c;在…

webassembly003 ggml ADAM (暂记)

Adam优化器的工作方式是通过不断更新一阶矩估计和二阶矩估计来自适应地调整学习率&#xff0c;并利用动量法来加速训练过程。这种方式可以在不同的参数更新方向和尺度上进行自适应调整&#xff0c;从而更有效地优化模型。 https://arxiv.org/pdf/1412.6980.pdf 参数 这些参数…

Confluence使用教程(用户篇)

1、如何创建空间 可以把空间理解成一个gitlab仓库&#xff0c;空间之间相互独立&#xff0c;一般建议按照部门&#xff08;小组的人太少&#xff0c;没必要创建空间&#xff09;或者按照项目分别创建空间 2、confluence可以创建两种类型的文档&#xff1a;页面和博文 从内容上来…

开始MySQL之路——MySQL 事务(详解分析)

MySQL 事务概述 MySQL 事务主要用于处理操作量大&#xff0c;复杂度高的数据。比如说&#xff0c;在人员管理系统中&#xff0c;你删除一个人员&#xff0c;你即需要删除人员的基本资料&#xff0c;也要删除和该人员相关的信息&#xff0c;如信箱&#xff0c;文章等等&#xf…

oracle 启停操作

1. 监听端口启停 # 根据实际情况 切换至oracle用户 su - oracle# 状态查看 lsnrctl stat# 启动1521端口监听 lsnrctl start# 关闭1521监听 lsnrctl stop 2. 数据库服务启停 # 立即关闭服务 shutdown immediate# 启动服务 startup

C语言——类型转换

数据有不同的类型&#xff0c;不同类型数据之间进行混合运算时涉及到类型的转换问题。 转换的方法有两种&#xff1a; 自动转换(隐式转换)&#xff1a;遵循一定的规则&#xff0c;由编译系统自动完成强制类型转换&#xff1a;把表达式的运算结果强制转换成所需的数据类型 语法格…

Python学习笔记:Requests库安装、通过url下载文件

1.下载安装requests库 在pipy或者github下载&#xff0c;通常是个zip&#xff0c;解压缩后在路径输入cmd&#xff0c;并运行以下代码 Python setup.py install 安装完成后&#xff0c;输入python再输入import requests得到可以判断时候完成安装 2.通过url下载文件 使用的是u…

RSA私钥解密操作

RSA私钥解密操作 一、背景二、操作三、常见问题3.1 invalid key format3.2 解密的数据太长3.3 Decryption error 一、背景 项目数据库中存放的敏感字段已使用rsa加密的方式&#xff0c;将内容加密成密文存放, 现在需要在使用的时候&#xff0c;使用私钥进行解密。 二、操作 …

cvc-complex-type.2.4.a: 发现了以元素 ‘base-extension‘ 开头的无效内容。应以 ‘{layoutlib}‘ 之一开头

不能飞的猪只是没用的猪。 —— 宫崎骏 《红猪》 常见的1种case 记录一下&#xff0c;新电脑安装android studio导入公司那些gradle还是5.5左右的工程以后&#xff0c;各种不适应。编译问题出现了。老电脑都是好好的。 cvc-complex-type.2.4.a: 发现了以元素 ‘base-extensi…

LeetCode 44题:通配符匹配

题目 给你一个输入字符串 (s) 和一个字符模式 (p) &#xff0c;请你实现一个支持 ? 和 * 匹配规则的通配符匹配&#xff1a; ? 可以匹配任何单个字符。* 可以匹配任意字符序列&#xff08;包括空字符序列&#xff09;。 判定匹配成功的充要条件是&#xff1a;字符模式必须…

【PLSQL】PLSQL基础

文章目录 一&#xff1a;记录类型1.语法2.代码实例 二&#xff1a;字符转换三&#xff1a;%TYPE和%ROWTYPE1.%TYPE2.%ROWTYPE 四&#xff1a;循环1.LOOP2.WHILE&#xff08;推荐&#xff09;3.数字式循环 五&#xff1a;游标1.游标定义及读取2.游标属性3.NO_DATA_FOUND和%NOTFO…

【Spring】Spring循环依赖的处理

循环依赖是指两个或多个组件之间相互依赖&#xff0c;形成一个闭环&#xff0c;从而导致这些组件无法正确地被初始化或加载。这种情况可能会在软件开发中引起问题&#xff0c;因为循环依赖会导致初始化顺序混乱&#xff0c;组件之间的关系变得复杂&#xff0c;甚至可能引发死锁…

【前车之鉴】: 2023最新教程-将java程序打包到maven私服的正确打开方式,详细流程介绍不怕你掌握不了

文章目录 为什么看这篇整体流程1. 注册账号【首次需要】2. 工单申请【新项目必须】3. 项目配置【新项目必须】4. 授权认证【新项目必须】5. 一键发布 最后也很重要 为什么看这篇 一是当前网络上一些博客有遗漏部分&#xff0c;这里做补充&#xff0c;二是网上思路没错&#xff…

_数字矩阵

题目&#xff1a;一个3阶的数字矩阵如下&#xff1a; 1 2 3 8 9 4 7 6 5 现在给定数字n(1<n≤20)&#xff0c;输出n阶数字矩阵。 思路&#xff1a; 放出一条好玩的贪吃蛇&#xff0c;按照右下左上的顺序吃蛋糕&#xff0c;一边吃蛋糕&#xff0c;一边拉数字&#xff1b…

vue h5项目 打包加载优化

打包美化: 1&#xff09;npx browserslistlatest --update-db 更新去除警告 2&#xff09;打包进度条 npm add progress-bar-webpack-plugin -D npm add webpackbar -D npm install --save-dev webpack-bundle-analyzer 优化&#xff1a; 1.各个插件和loader所花费的时间 …