当前位置: 首页 > backend >正文

Spring系列四:AOP切面编程第三部分

🐋AOP-JoinPoint

1.通过JoinPoint可以获取到调用方法的签名
2.其他常用方法

●代码实现
1.com.zzw.spring.aop.aspectj.SmartAnimalAspect

@Aspect //表示是一个切面类
@Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect {//给Car配置一个最终通知@After(value = "execution(public void Car.run())")public void ok4(JoinPoint joinPoint) {//演示joinPoint常用的方法String methodName = joinPoint.getSignature().getName();//获取目标方法名 runString simpleName = joinPoint.getSignature().getDeclaringType().getSimpleName();//获取目标方法所属类的简单类名 CarString declaringTypeName = joinPoint.getSignature().getDeclaringTypeName();//获取目标方法所属类的全类名 com.zzw.spring.aop.aspectj.CarString fullName = joinPoint.getSignature().getDeclaringType().getName();//获取目标方法所属类的全类名 com.zzw.spring.aop.aspectj.Carint modifiers = joinPoint.getSignature().getModifiers();//获取目标方法声明类型(public, private, protected) 1Object[] args = joinPoint.getArgs();//获取传入目标方法的参数, 返回一个数组Object target = joinPoint.getTarget();//获取被代理的对象 Car对象Object aThis = joinPoint.getThis();//获取代理对象自己 代理对象System.out.println("ok");System.out.println("ok");     }
}

🐋返回通知获取结果

1.修改切面类com.zzw.spring.aop.aspectj.SmartAnimalAspect

@Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定)]
@Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect {//返回通知: 即把showSuccessEndLog方法切入到目标对象方法正常执行完毕后的地方//解读//1.如果我们希望把目标方法, 执行的结果, 返回给切入方法//2.可以在 @AfterReturning 增加属性,  比如 returning = "res"//3.同时在切入方法增加 Object res//4.注意: returning = "res" 和 Object res 的 res名字一样@AfterReturning(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))", returning = "res")public void showSuccessEndLog(JoinPoint joinPoint, Object res) {Signature signature = joinPoint.getSignature();System.out.println("切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-" + signature.getName() + " 返回的结果是=" + res);}
}

2.测试com.zzw.spring.aop.aspectj.AspAspectjTest

public class AopAspectjTest {@Testpublic void smartDogTestByProxy() {ApplicationContext ioc =new ClassPathXmlApplicationContext("beans08.xml");//这里我们需要通过接口类型来获取到注入的SmartDog对象-就是代理对象SmartAnimalAble smartAnimalAble =ioc.getBean(SmartAnimalAble.class);smartAnimalAble.getSum(1f, 2f);//System.out.println("smartAnimalAble运行类型是 " + smartAnimalAble.getClass());//class com.sun.proxy.$Proxy16}
}

🐋异常通知获取异常信息

如何在异常通知方法中获取异常信息

1.修改com.zzw.spring.aop.aspectj.SmartDog

@Component //使用@Component 当spring容器启动时, 将 SmartDog 注入到容器
public class SmartDog implements SmartAnimalAble {@Overridepublic float getSum(float i, float j) {float result = i + j;int res = 1 / 0;//模拟一个算数异常System.out.println("方法内部打印result = " + result);return result;}
}

2.修改切面类com.zzw.spring.aop.aspectj.SmartAnimalAspect

@Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定)]
@Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect {//异常通知: 即把showExceptionLog方法切入到目标对象方法执行发生异常后的catch{}    //解读//1.如果我们希望把目标方法, 异常信息, 返回给切入方法//2.可以在 @AfterThrowing 增加属性,  比如 throwing = "throwable"//3.同时在切入方法增加 Throwable throwable//4.注意: throwing = "throwable" 和 Throwable throwable 的 throwable名字一样@AfterThrowing(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))", throwing = "throwable")public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {Signature signature = joinPoint.getSignature();System.out.println("切面类showExceptionLog()-方法执行异常-日志-方法名-" + signature.getName() + " 异常信息=" + throwable);}
}

3.测试com.zzw.spring.aop.aspectj.AspAspectjTest

public class AopAspectjTest {@Testpublic void smartDogTestByProxy() {ApplicationContext ioc =new ClassPathXmlApplicationContext("beans08.xml");//这里我们需要通过接口类型来获取到注入的SmartDog对象-就是代理对象SmartAnimalAble smartAnimalAble =ioc.getBean(SmartAnimalAble.class);smartAnimalAble.getSum(1, 2);//System.out.println("smartAnimalAble运行类型是 " + smartAnimalAble.getClass());//class com.sun.proxy.$Proxy16}
}

🐋环绕通知

需求: 如何使用环绕通知完成其它四个通知的功能.

1.新建com.zzw.spring.aop.aspectj.SmartAnimalAspect2切面类, 并把SmartAnimalAspect切面类注解注释, 避免干扰; 去掉SmartDog的异常代码.

//切面类
@Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定)]
@Component //会将SmartAnimalAspect2注入到容器
public class SmartAnimalAspect2 {//演示环绕通知的使用//1. @Around 表示这是一个环绕通知[可以完成其它四个通知的功能]//2. (value = "execution(public float getSum(float, float))") 切入点表达式//3. doAround 表示要切入的方法 - 调用的基本结构 try-catch-finally@Around(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))")public Object doAround(ProceedingJoinPoint joinPoint) {Object result = null;String methodName = joinPoint.getSignature().getName();try {//1.相当于前置通知完成的事情Object[] args = joinPoint.getArgs();List<Object> argList = Arrays.asList(args);System.out.println("AOP环绕通知[=前置通知]--" + methodName + "方法开始了--参数有: " + argList);//在环绕通知中一定要调用joinPoint.proceed()来执行目标方法result = joinPoint.proceed();//2.相当于返回通知完成的事情System.out.println("AOP环绕通知[=返回通知]--" + methodName + "方法结束了--结果是: " + result);} catch (Throwable throwable) {//3.相当于异常通知完成的事情System.out.println("AOP环绕通知[=异常通知]--" + methodName + "方法抛出异常--异常对象: " + throwable);} finally {//4.相当于最终通知完成的事情System.out.println("AOP环绕通知[=最终通知]--" + methodName + "方法最终结束了...");}return result;}
}

2.测试com.zzw.spring.aop.aspectj.AopAspectjTest

public class AopAspectjTest {@Testpublic void testDoAround() {//获取Spring容器ApplicationContext ioc =new ClassPathXmlApplicationContext("beans08.xml");SmartAnimalAble smartAnimalAble = ioc.getBean(SmartAnimalAble.class);smartAnimalAble.getSum(10f, 2f);}
}

3.结果

Connected to the target VM, address: '127.0.0.1:60160', transport: 'socket'
AOP环绕通知[=前置通知]--getSum方法开始了--参数有: [10.0, 2.0]
方法内部打印result = 12.0
AOP环绕通知[=返回通知]--getSum方法结束了--结果是: 12.0
AOP环绕通知[=最终通知]--getSum方法最终结束了...

🐋切入点表达式重用

为了统一管理切入点表达式, 我们可以使用切入点表达式重用技术

1.对com.zzw.spring.aop.aspectj.SmartAnimalAspect.java稍作修改

//切面类, 类似于我们前面自己写的MyProxyProvider, 但是功能强大很多
@Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定)]
@Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect {//定义一个切入点, 在后面使用时可以直接引用, 提高了复用性@Pointcut(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))")public void myPointCut() {}//@Before(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))")//这里我们使用定义好的切入点@Before(value = "myPointCut()")public void showBeginLog(JoinPoint joinPoint) {//通过连接点对象joinPoint, 可以获取方法签名Signature signature = joinPoint.getSignature();System.out.println("切面类showBeginLog()[使用的myPointCut()]-方法执行前-日志-方法名-" + signature.getName() + "-参数 "+ Arrays.asList(joinPoint.getArgs()));}//返回通知: 即把showSuccessEndLog方法切入到目标对象方法正常执行完毕后的地方//@AfterReturning(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))", returning = "res")//使用切入点@AfterReturning(value = "myPointCut()", returning = "res")public void showSuccessEndLog(JoinPoint joinPoint, Object res) {Signature signature = joinPoint.getSignature();System.out.println("切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-" + signature.getName() + " 返回的结果是=" + res);}//异常通知: 即把showExceptionLog方法切入到目标对象方法执行发生异常后的catch{}//@AfterThrowing(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))", throwing = "throwable")//直接使用切入点表达式@AfterThrowing(value = "myPointCut()", throwing = "throwable")public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {Signature signature = joinPoint.getSignature();System.out.println("切面类showExceptionLog()-方法执行异常-日志-方法名-" + signature.getName() + " 异常信息=" + throwable);}//最终通知: 即把showFinallyEndLog方法切入到目标方法执行后, 不管是否发生异常都要执行, finally{}//@After(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))")//直接使用切入点表达式@After(value = "myPointCut()")public void showFinallyEndLog(JoinPoint joinPoint) {Signature signature = joinPoint.getSignature();System.out.println("切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-" + signature.getName());}
}

2.测试com.zzw.spring.aop.aspectj.AspAspectjTest

public class AopAspectjTest {@Testpublic void smartDogTestByProxy() {ApplicationContext ioc =new ClassPathXmlApplicationContext("beans08.xml");//这里我们需要通过接口类型来获取到注入的SmartDog对象-就是代理对象SmartAnimalAble smartAnimalAble =ioc.getBean(SmartAnimalAble.class);smartAnimalAble.getSum(1, 2);//System.out.println("smartAnimalAble运行类型是 " + smartAnimalAble.getClass());//class com.sun.proxy.$Proxy16}
}

🐋切面类执行顺序

如果同一个方法, 有多个切面在同一个切入点切入, 那么执行的优先级如何控制

●基本语法
import org.springframework.core.annotation.Order;
通过@order(value=n) 来控制. n值越小, 优先级越高

●代码实现
1.新建com.zzw.spring.aop.aspectj.SmartAnimalAspect3.java, 将SmartAnimalAspect2.java注销, 保留SmartAnimalAspect.java

//切面类, 类似于我们前面自己写的MyProxyProvider, 但是功能强大很多
@Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定)]
@Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect3 {@Before(value = "execution(public float com.zzw.spring.aop.aspectj_.SmartDog.getSum(float, float))")public void showBeginLog(JoinPoint joinPoint) {//通过连接点对象joinPoint, 可以获取方法签名Signature signature = joinPoint.getSignature();System.out.println("SmartAnimalAspect3-切面类showBeginLog()-方法执行前-日志-方法名-" + signature.getName() + "-参数 "+ Arrays.asList(joinPoint.getArgs()));}//返回通知: 即把showSuccessEndLog方法切入到目标对象方法正常执行完毕后的地方@AfterReturning(value = "execution(public float com.zzw.spring.aop.aspectj_.SmartDog.getSum(float, float))", returning = "res")public void showSuccessEndLog(JoinPoint joinPoint, Object res) {Signature signature = joinPoint.getSignature();System.out.println("SmartAnimalAspect3-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-" + signature.getName() + " 返回的结果是=" + res);}//异常通知: 即把showExceptionLog方法切入到目标对象方法执行发生异常后的catch{}@AfterThrowing(value = "execution(public float com.zzw.spring.aop.aspectj_.SmartDog.getSum(float, float))", throwing = "throwable")public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {Signature signature = joinPoint.getSignature();System.out.println("SmartAnimalAspect3-切面类showExceptionLog()-方法执行异常-日志-方法名-" + signature.getName() + " 异常信息=" + throwable);}//最终通知: 即把showFinallyEndLog方法切入到目标方法执行后, 不管是否发生异常都要执行, finally{}@After(value = "execution(public float com.zzw.spring.aop.aspectj_.SmartDog.getSum(float, float))")public void showFinallyEndLog(JoinPoint joinPoint) {Signature signature = joinPoint.getSignature();System.out.println("SmartAnimalAspect3-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-" + signature.getName());}
}

2.如果这两个类不加@order注解, 那么执行结果如下

SmartAnimalAspect-切面类showBeginLog()[使用的myPointCut()]-方法执行前-日志-方法名-getSum-参数 [1.0, 2.0]
SmartAnimalAspect3-切面类showBeginLog()-方法执行前-日志-方法名-getSum-参数 [1.0, 2.0]
方法内部打印result = 3.0
SmartAnimalAspect3-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-getSum 返回的结果是=3.0
SmartAnimalAspect3-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-getSum
SmartAnimalAspect-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-getSum 返回的结果是=3.0
SmartAnimalAspect-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-getSum

3.如果两个类加了@order注解

@Order(value = 2)//表示该切面类执行的顺序, value的值越小, 优先级越高
@Aspect
@Component 
public class SmartAnimalAspect {//内容省略
}
@Order(value = 1)//表示该切面类执行的顺序, value的值越小, 优先级越高
@Aspect
@Component 
public class SmartAnimalAspect3 {//内容省略
}

4.那么执行结果如下

SmartAnimalAspect3-切面类showBeginLog()-方法执行前-日志-方法名-getSum-参数 [1.0, 2.0]
SmartAnimalAspect-切面类showBeginLog()[使用的myPointCut()]-方法执行前-日志-方法名-getSum-参数 [1.0, 2.0]
方法内部打印result = 3.0
SmartAnimalAspect-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-getSum 返回的结果是=3.0
SmartAnimalAspect-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-getSum
SmartAnimalAspect3-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-getSum 返回的结果是=3.0
SmartAnimalAspect3-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-getSum

💧注意事项和细节

🐧不能理解成: 优先级高的每个消息通知都先执行. 这个方法调用机制和Filter过滤器链式调用类似

🐧方法调用机制如下图所示
👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇
在这里插入图片描述

http://www.xdnf.cn/news/2869.html

相关文章:

  • MySQL-排序
  • Finish技术生态计划: FinishRpc
  • print用法讲解(Python)
  • 数字人接大模型第二步:语音克隆
  • 洛谷P1003[NOIP 2011 提高组] 铺地毯
  • GPU虚拟化实现(四)
  • XMOS人工智能降噪——AI降噪让极端嘈杂环境下的通话和拾音变得可能
  • 说说stack reconciler 和fiber reconciler
  • 算法题(136):逛画展
  • 如何利用谷歌趋势精确估算关键词搜索量?
  • DDI0487--A1.3
  • 阿里云服务器云盘扩容
  • 【Machine Learning Q and AI 读书笔记】- 01 嵌入、潜空间和表征
  • 更新日期自动填充
  • LeetCode 热题 100_最小路径和(92_64_中等_C++)(多维动态规划)
  • TypeScript之type
  • IEEE会议:第十届网络安全与信息工程国际会议(ICCSIE 2025)
  • 资产定位解决方案:蓝牙Beacon如何实现低成本高效追踪
  • 【Android】谈谈DexClassLoader
  • dx11 龙书学习 第四章 dx11 准备工作
  • Unity AI-使用Ollama本地大语言模型运行框架运行本地Deepseek等模型实现聊天对话(二)
  • 天梯——链表去重
  • 基于STM32、HAL库的ATSHA204A安全验证及加密芯片驱动程序设计
  • 深度学习大模型: AI 阅卷替代人工阅卷
  • Field访问对象int字段,对象访问int字段,通过openjdk17 C++源码看对象字段访问原理
  • J-Link RTT打印输出调试信息
  • 深入蜂窝物联网:第二章 深度解读 NB-IoT:协议栈、部署与典型应用
  • 两地三中心
  • MySQL数据库(14)—— 使用C操作MySQL
  • 【ACL系列论文写作指北03-相关工作怎么写】-展示视野与定位创新