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

AOP切面编程

  • 💗AOP-官方文档
    • 🍝AOP 讲解
    • 🍝AOP APIs
  • 💗动态代理
    • 🍝初探动态代理
    • 🍝动态代理深入
    • 🍝AOP问题提出
      • 📗使用土方法解决
      • 📗 对土方法解耦-开发最简单的AOP类
      • 📗 土方法缺点

上文中, 我们学习到了 Spring系列三:基于注解配置bean

接下来我们学习, AOP切面编程

Spring项目
在这里插入图片描述

💗AOP-官方文档

🍝AOP 讲解


AOP 讲解: spring-framework-5.3.8\docs\reference\html/core.html
在这里插入图片描述

🍝AOP APIs


AOP APIs: spring-framework-5.3.8\docs\reference\html/core.html

在这里插入图片描述
spring-framework-5.3.8/docs/javadoc-api/index.html

在这里插入图片描述

💗动态代理

🍝初探动态代理

需求说明
1.由Vehicle (交通工具接口, 有一个run方法), 下面有两个类 Car 和 Ship
2.当运行Car对象的 run 方法 和 ship对象的 run 方法时, 输入如下内容

交通工具开始运行了…
轮船在海上航行…
交通工具停止运行了…

交通工具开始运行了…
小汽车在路上跑…
交通工具停止运行了…


解决方案一: 传统方案

1.新建com.zzw.spring.aop.proxy.Vehicle接口

//接口, 该接口有run方法
public interface Vehicle {void run();
}

2.新建com.zzw.spring.aop.proxy.Car

public class Car implements Vehicle {@Overridepublic void run() {System.out.println("交通工具开始运行了....");System.out.println("小汽车在路上 running....");System.out.println("交通工具停止运行了....");}
}

3.新建com.zzw.spring.aop.proxy.Ship

public class Ship implements Vehicle {@Overridepublic void run() {System.out.println("交通工具开始运行了....");System.out.println("大轮船在路上 running....");System.out.println("交通工具停止运行了....");}
}

4.新建com.zzw.spring.aop.proxy.TestVehicle

public class TestVehicle {@Testpublic void run() {//OOP基础=>java基础Vehicle vehicle = new Ship();//动态绑定vehicle.run();}
}

来思考一下, 这个解决方案好吗? ====> 代码冗余, 其实就是单个对象的调用, 并没有很好的解决.


解决方案二: 动态代理方式
动态代理解决思路: 在调用方法时, 使用反射机制, 根据方法去决定调用哪个对象方法

1.新建com.zzw.spring.aop.proxy.VehicleProxyProvider

public class VehicleProxyProvider {//定义一个属性//target_vehicle 表示真正要执行的对象//该对象实现了Vehicle接口private Vehicle target_vehicle;//构造器public VehicleProxyProvider(Vehicle target_vehicle) {this.target_vehicle = target_vehicle;}//编写一个方法, 可以返回一个代理对象public Vehicle getProxy() {//得到类加载器ClassLoader classLoader =target_vehicle.getClass().getClassLoader();//得到要代理对象/被执行对象 的接口信息, 底层是通过接口来完成调用Class<?>[] interfaces = target_vehicle.getClass().getInterfaces();//创建InvocationHandler 对象//因为 InvocationHandler 是接口, 所以我们可以通过匿名对象的方式来创建该对象/*** public interface InvocationHandler {*     public Object invoke(Object proxy, Method method, Object[] args)*          throws Throwable;* }* invoke 方法是将来执行target_vehicle的方法时, 会调用到*/InvocationHandler invocationHandler = new InvocationHandler() {/*class VehicleProxyProvider$01 implements InvocationHandler {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("交通工具开始运行了....");//这里是我们的反射基础 => OOPObject result = method.invoke(target_vehicle, args);System.out.println("交通工具停止运行了....");return result;}}InvocationHandler invocationHandler = new VehicleProxyProvider$01();*//*** invoke 方法是将来执行我们的target_vehicle的方法时, 会调用到** @param proxy  表示代理对象* @param method 就是通过代理对象调用方法时, 的那个方法 代理对象.run()* @param args   表示调用 代理对象.run(xx) 传入的参数* @return 表示 代理对象.run(xx) 执行后的结果.* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("交通工具开始运行了....");//这里是我们的反射基础 => OOP//method 是 public abstract void com.zzw.spring.aop.proxy.Vehicle.run()//target_vehicle 是 Ship对象//args 是 null//这里通过反射+动态绑定机制, 就会执行到被代理对象的方法//执行完毕就返回Object result = method.invoke(target_vehicle, args);System.out.println("交通工具停止运行了....");return result;}};/*public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)解读1.Proxy.newProxyInstance() 可以返回一个代理对象2.ClassLoader loader: 类加载器,3.Class<?>[] interfaces 就是将来要代理的对象的接口信息4.InvocationHandler h 调用处理器/对象, 有一个非常重要的方法invoke*/Vehicle proxy =(Vehicle) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);return proxy;}
}

2.修改com.zzw.spring.aop.proxy.Vehicle

public interface Vehicle {void run();public String fly(int height);
}

3.修改com.zzw.spring.aop.proxy.Ship

public class Ship implements Vehicle {@Overridepublic void run() {//System.out.println("交通工具开始运行了....");System.out.println("大轮船在路上 running....");//System.out.println("交通工具停止运行了....");}@Overridepublic String fly(int height) {return "轮船可以飞行 高度=" + height + "米";}
}

4.com.zzw.spring.aop.proxy.TestVehicle

public class TestVehicle {@Testpublic void proxyRun() {//创建Ship对象Ship ship = new Ship();//创建VehicleProxyProvider对象, 并且我们要传入代理的对象VehicleProxyProvider vehicleProxyProvider= new VehicleProxyProvider(ship);//获取代理对象, 该对象可以代理执行方法//解读//1.proxy 编译类型Vehicle,//2.运行类型 是代理类型, 即 class com.sun.proxy.$Proxy8Vehicle proxy = vehicleProxyProvider.getProxy();System.out.println("proxy的编译类型是 Vehicle");System.out.println("proxy的运行类型是" + proxy.getClass());//下面解读/debug怎么执行到 代理对象的 public Object invoke(Object proxy, Method method, Object[] args)//梳理完毕, proxy的编译类型是Vehicle, 运行类型是Proxy  class com.sun.proxy.$Proxy8//所以当执行run方法时, 会执行到 代理对象的invoke//如果体现动态 [1.被代理的对象 2.方法]//proxy.run();String result = proxy.fly(10000);System.out.println("result=" + result);System.out.println("ok");}

5.debug
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

🍝动态代理深入

需求说明
1.有一个SmartAnimal 接口, 可以完成简单的加减法, 要求在执行 getSum()getSub() 时, 输出执行前, 执行过程, 执行后的日志结果. 输出内容如下:

日志-方法名-getSum-参数 1.5 4.5
方法内部打印result = 6.0
日志-方法名-getSum-结果result= 6.0
=================================
日志-方法名-getSub-参数 1.4 3.3
方法内部打印result = -1.9
日志-方法名-getSub-结果result= -1.9


解决方案一: 传统方案

1.新建com.zzw.spring.aop.proxy2.SmartAnimalAble接口

public interface SmartAnimalAble {//求和float getSum(float i, float j);//求差float getSub(float i, float j);
}

2.com.zzw.spring.aop.proxy2.SmartCat

public class SmartCat implements SmartAnimalAble {@Overridepublic float getSum(float i, float j) {System.out.println("日志-方法名-getSum-参数 " + i + " " + j);float result = i + j;System.out.println("方法内部打印result = " + result);System.out.println("日志-方法名-getSum-结果result= " + (i + j));return result;}@Overridepublic float getSub(float i, float j) {System.out.println("日志-方法名-getSub-参数 " + i + " " + j);float result = i - j;System.out.println("方法内部打印result = " + result);System.out.println("日志-方法名-getSub-结果result= " + (i - j));return result;}
}

3.com.zzw.spring.aop.proxy2.AopTest

public class AopTest {@Testpublic void run() {SmartAnimalAble smartAnimalAble = new SmartCat();smartAnimalAble.getSum(1.5f, 4.5f);System.out.println("=================================");smartAnimalAble.getSub(1.4f, 3.3f);}
}

解决方案二: 动态代理方式
考虑代理对象调用方法(底层是反射调用)时, 可能出现的异常- [横切关注点]

1.新建com.zzw.spring.aop.proxy2.MyProxyProvider

//可以返回一个动态代理对象, 可以执行SmartCat对象的方法
public class MyProxyProvider {//这是一个属性, 是我们要执行的目标对象//该对象实现了SmartAnimalAble接口private SmartAnimalAble target_obj;//构造器MyProxyProvider(SmartAnimalAble target_obj) {this.target_obj = target_obj;}//编写一个方法, 可以返回一个代理对象//该代理对象可以执行目标方法public SmartAnimalAble getProxy() {//1.得到类加载器ClassLoader classLoader =target_obj.getClass().getClassLoader();//2.得到要执行的目标对象的接口信息Class<?>[] interfaces = target_obj.getClass().getInterfaces();//3.创建InvocationHandler 对象InvocationHandler invocationHandler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String name = method.getName();//方法名Object result = null;try {System.out.println("方法执行前-日志-方法名-" + name + "-参数 "+ Arrays.asList(args));//这里从aop的角度看,就是一个横切关注点-前置通知//使用反射调用方法result = method.invoke(target_obj, args);System.out.println("方法执行正常结束-日志-方法名-" + name + "-结果result= "+ result);//这里从aop的角度看, 也是一个横切关注点-返回通知return result;} catch (Exception e) {e.printStackTrace();//如果反射执行方法时, 出现异常, 就会进入到catch{}System.out.println("方法执行异常-日志-方法名-" + name + "-异常类型="+ e.getClass().getName());//这里从aop的角度看, 又是一个横切关注点-异常通知} finally {//不管你是否出现了异常, 最终都会执行到 finally {}//这里从aop的角度看, 还是一个横切关注点-最终通知System.out.println("方法最终结束-日志-方法名-" + name);}return result;}};//创建代理对象SmartAnimalAble proxy =(SmartAnimalAble) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);return proxy;}
}

2.修改com.zzw.spring.aop.proxy2.SmartCat

public class SmartCat implements SmartAnimalAble {@Overridepublic float getSum(float i, float j) {//System.out.println("日志-方法名-getSum-参数 " + i + " " + j);float result = i + j;System.out.println("方法内部打印result = " + result);//System.out.println("日志-方法名-getSum-结果result= " + (i + j));return result;}@Overridepublic float getSub(float i, float j) {//System.out.println("日志-方法名-getSub-参数 " + i + " " + j);float result = i - j;System.out.println("方法内部打印result = " + result);//System.out.println("日志-方法名-getSub-结果result= " + (i - j));return result;}
}

3.com.zzw.spring.aop.proxy2.AopTest

public class AopTest {@Testpublic void smartCatTestProxy() {//创建SmartCat对象SmartAnimalAble smartAnimalAble = new SmartCat();MyProxyProvider myProxyProvider= new MyProxyProvider(smartAnimalAble);//获取代理对象, 该对象可以代理执行方法SmartAnimalAble proxy = myProxyProvider.getProxy();System.out.println("proxy的编译类型是 SmartAnimalAble");System.out.println("proxy的运行类型是 " + proxy.getClass());//proxy的编译类型是SmartAnimalAble, 运行类型是 Class com.sun.proxy.$Proxy8//所以当执行getSum方法时, 会执行到 代理对象的invokeproxy.getSum(1.2f, 2.4f);System.out.println("=================================");proxy.getSub(1.3f, 4.5f);System.out.println("ok");}
}

🍝AOP问题提出

MyProxyProvider.java中, 我们的输出语句功能比较弱, 在实际开发中, 我们希望是以一个方法的形式, 嵌入到真正执行的目标方法前.

如图分析
在这里插入图片描述

📗使用土方法解决

需求分析
使用土方法解决前面的问题, 后面使用Spring的AOP组件完成

1.先建一个包, 把相关文件拷贝过来, 进行修改完成. ----这里只是模拟, 并没有真的新建包

//我们的一个方法, 在目标对象执行前执行
public void before(Method method, Object[] args) {System.out.println("before方法执行前-日志-方法名-" + method.getName() + "-参数 "+ Arrays.asList(args));//这里从aop的角度看,就是一个横切关注点-前置通知
}//我们的一个方法, 在目标对象执行后执行
public void after(Method method, Object result) {System.out.println("after方法执行正常结束-日志-方法名-" + method.getName() + "-结果result= "+ result);//这里从aop的角度看, 也是一个横切关注点-返回通知
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String name = method.getName();//方法名Object result = null;before(method, args);//使用反射调用方法result = method.invoke(target_obj, args);after(method, result);return result;
} 

2.该方法问题分析: 耦合度高

📗 对土方法解耦-开发最简单的AOP类

1.新建com.zzw.spring.aop.proxy2.ZzwAOP

public class ZzwAOP {//我们的一个方法, 在目标对象执行前执行public static void before(Method method, Object[] args) {System.out.println("ZzwHsp-方法执行前-日志-方法名-" + method.getName() + "-参数 "+ Arrays.asList(args));//这里从aop的角度看,就是一个横切关注点-前置通知}//我们的一个方法, 在目标对象执行后执行public static void after(Method method, Object result) {System.out.println("ZzwHsp-方法执行正常结束-日志-方法名-" + method.getName() + "-结果result= "+ result);//这里从aop的角度看, 也是一个横切关注点-返回通知}
}

2.修改com.zzw.spring.aop.proxy2.MyProxyProvider

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String name = method.getName();//方法名Object result = null;try {//before(method, args);ZzwAOP.before(method, args);//使用反射调用方法result = method.invoke(target_obj, args);//after(method, result);ZzwAOP.after(method, result);return result;} catch (Exception e) {}
}

📗 土方法缺点

土方法 不够灵活;
土方法 复用性差;
土方法 是一种硬编码 (因为没有注解和反射支撑)

Spring AOP 闪亮登场 - 底层是ASPECTJ

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

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

相关文章

【云计算】云计算中IaaS、PaaS、SaaS介绍

0 随着云计算、大数据、人工智能发展迅速,布局“云”已经是互联网企业共识。云计算的服务类型分为三种,分别为IaaS、PaaS、SaaS,这三个分别是什么意思,今天做一个简单的介绍和了解。 一、云计算 云计算是用户需求通过Internet获取计算资源,把计算资源包装成服务,提供给…

使用HYPRE库并行装配IJ稀疏矩阵指南: 矩阵预分配和重复利用

使用HYPRE库并行装配IJ稀疏矩阵指南 HYPRE是一个流行的并行求解器库&#xff0c;特别适合大规模稀疏线性系统的求解。下面介绍如何并行装配IJ格式的稀疏矩阵&#xff0c;包括预先分配矩阵空间和循环使用。 1. 初始化矩阵 首先需要创建并初始化一个IJ矩阵&#xff1a; #incl…

目标跟踪最新文章阅读列表

AAAI2025 TrackFormer: Multi-Object Tracking with Transformers 论文:https://arxiv.org/abs/2101.02702 代码:https://github.com/timmeinhardt/trackformer AAAI2025 SUTrack 单目标跟踪 论文:https://pan.baidu.com/s/10cR4tQt3lSH5V2RNf7-3gg?pwd=pks2 代码:htt…

分布式GPU上计算长向量模的方法

分布式GPU上计算长向量模的方法 当向量分布在多个GPU卡上时&#xff0c;计算向量模(2-范数)需要以下步骤&#xff1a; 在每个GPU上计算本地数据的平方和跨GPU通信汇总所有平方和在根GPU上计算总和的平方根 实现方法 下面是一个完整的CUDA示例代码&#xff0c;使用NCCL进行多…

高并发下单库存扣减异常?飞算 JavaAI 自动化生成分布式事务解决方案

在电商、旅游等行业业务量激增&#xff0c;高并发下单场景中&#xff0c;传统库存扣减方式弊端尽显。超卖问题因缺乏有效并发控制机制频发&#xff0c;多个订单同时访问库存数据&#xff0c;导致同一商品多次售出&#xff0c;订单无法履约引发客户投诉&#xff1b;同时&#xf…

MVCWebAPI使用FromBody接受对象的方法

近期在做软件升级操作的时候突然想着需要的参数比较多&#xff0c;如果需要参数的话参数比较多&#xff0c;所有想着使用frombody来集合数据统一操作做了个样张希望对您有帮助 代码如下&#xff1a; /// <summary>/// 入口当前文件接口下的操作数据/// </summary>/…

Atlas 800I A2 离线部署 DeepSeek-R1-Distill-Llama-70B

一、环境信息 1.1、硬件信息 Atlas 800I A2 1.2、环境信息 注意&#xff1a;这里驱动固件最好用商业版&#xff0c;我这里用的社区版有点小问题 操作系统&#xff1a;openEuler 22.03 LTS NPU驱动&#xff1a;Ascend-hdk-910b-npu-driver_24.1.rc3_linux-aarch64.run NPU固…

NLP预处理:如何 处理表情符号

一、说明 本系列文总结了在NLP处理中&#xff0c;进行文本预处理的一些内容、步骤、处理工具包应用。本篇专门谈论网上文章表情符号处理&#xff0c;对于初学者具有深刻学习和实验指导意义。 二、介绍 表情符号已成为现代交流不可或缺的一部分&#xff0c;尤其是在社交媒体、…

C++/SDL 进阶游戏开发 —— 双人塔防(代号:村庄保卫战 14)

&#x1f381;个人主页&#xff1a;工藤新一 &#x1f50d;系列专栏&#xff1a;C面向对象&#xff08;类和对象篇&#xff09; &#x1f31f;心中的天空之城&#xff0c;终会照亮我前方的路 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 文章目录 二…

解锁空间数据新质生产力暨:AI(DeepSeek、ChatGPT)、Python、ArcGIS Pro多技术融合下的空间数据分析、建模与科研绘图及论文写作

人工智能&#xff08;AI&#xff09;与ArcGIS Pro的结合&#xff0c;为空间数据处理和分析开辟了前所未有的创新路径。AI通过强大的数据挖掘、深度学习及自动化能力&#xff0c;可高效处理海量、多源、异构的空间数据&#xff0c;极大提升了分析效率与决策支持能力。而ArcGIS P…

18.2.go语言redis中使用lua脚本

在 Redis 中使用 Lua 脚本可以实现原子性操作、减少网络开销以及提高执行效率。 Redis 执行 Lua 脚本的原理 Redis 内置了 Lua 解释器&#xff0c;能够直接在服务器端执行 Lua 脚本。当执行 Lua 脚本时&#xff0c;Redis 会将脚本作为一个整体执行&#xff0c;保证脚本执行期…

⭐Unity_Demolition Media Hap (播放Hap格式视频 超16K大分辨率视频 流畅播放以及帧同步解决方案)

播放大分辨率视频以及实现局域网视频同步是许多开发者会遇到的需求,AVPro有一个 Ultra Edition版本,也能播放Hap格式视频,之外就是Demolition Media Hap插件啦,实测即使是 7208*3808 大分辨率的视频帧率还是能稳定在30帧,它能帮助我们轻松解决这些问题😎。 一、插件概述 …

AI大模型知识与医疗项目实践 - Java架构师面试实战

AI大模型知识与医疗项目实践 - Java架构师面试实战 本文模拟了一场互联网大厂的Java架构师面试&#xff0c;围绕AI大模型知识、工具以及其在医疗项目中的实践和趋势展开讨论。 第一轮提问 面试官&#xff1a; 马架构&#xff0c;请您介绍一下AI大模型的基本概念及其在医疗领…

Windows 的文件系统不区分大小写,Linux区分

在 Windows 系统中&#xff0c;文件系统默认是不区分大小写的。这意味着在 Windows 上&#xff0c;文件名 ui_BalanceMeasureScreenUI.h 和 ui_balancemeasurescreenui.h 被视为同一个文件。因此&#xff0c;即使你在代码中使用了不同的大小写方式来引用同一个文件&#xff0c;…

Unity 资源合理性检测

一&#xff1a;表格过度配置&#xff0c;表格资源是否在工程中存在&#xff0c;并输出不存在的资源 import pandas as pd import glob import osassets [] count 0# 遍历configs文件夹下所有xlsx文件 for file_path in glob.glob(configs/*.xlsx):count 1try:sheets pd.re…

Python爬虫实战:获取高考资源网各学科精品复习资料

一、引言 高考资源网拥有丰富的高考复习资料,对于我们而言,获取这些资源并整理分享能为考生提供有价值的帮助。然而,手动从网站查找和下载资源效率低且易出错。利用 Python 爬虫技术可实现自动化资源获取,提高工作效率。但在爬取过程中,需考虑网站反爬机制,采取相应措施…

DuckDB:现代数据分析的“SQLite“内核革命

在数据工程、数据科学快速演进的今天&#xff0c;一个新的名字正在快速蹿红&#xff1a;DuckDB。 有人称它是数据分析领域的SQLite&#xff0c;也有人称它为下一代轻量级OLAP引擎。 无论哪种称呼&#xff0c;都离不开一个事实&#xff1a; DuckDB 重新定义了小型数据仓库和本地…

GIS开发笔记(16)解决基于osg和osgearth三维地图上添加placeNode图标点击不易拾取的问题

一、实现效果 二、实现原理 在图标添加的位置同时添加一个红色圆球,半径为5000~8000米,图标和圆球挂接到同一个group节点,group节点再挂接到根节点,当点击到圆球时,通过遍历父节点就可以找到被点击的图标节点。 三、参考代码 //添加图标代码 #pragma once #include &…

计算机网络学习笔记 1-3章

第 1 章 计算机网络体系结构 【考纲内容】 &#xff08;一&#xff09;计算机网络概述 计算机网络的概念、组成与功能&#xff1b;计算机网络的分类&#xff1b; 计算机网络的性能指标 &#xff08;二&#xff09;计算机网络体系结构与参考模型 计算机网络分层结构&#xff…

基于NVIDIA RTX 4090的COLMAP 3.7安装指南:Ubuntu 20.04 + CUDA 11.8环境配置【2025最新版!!】

一、引言 三维重建技术作为计算机视觉领域的核心方向&#xff0c;在数字孪生、自动驾驶等领域具有重要应用价值。COLMAP作为开源的SfM&#xff08;Structure-from-Motion&#xff09;工具&#xff0c;其GPU加速特性可显著提升重建效率。由于最新研究三维重建的需要&#xff08…