手写SpringAOP

一、非注解式简易版AOP

整体流程
在这里插入图片描述

1.1 代码

public class Test {public static void main(String[] args){// Aop代理工厂DefaultAopProxyFactory factory = new DefaultAopProxyFactory();// 测试对象AOPDemoImpl demo = new AOPDemoImpl();// 支撑类:用于存放目标对象、各种通知以及目标对象的接口AdvisedSupport advisedSupport = new AdvisedSupport();advisedSupport.setTarget(demo);// 生成三个通知类,分别代表环绕通知,前置通知和后置通知Advice logAdvice = new LogAdvice();Advice beforeAdvice = new BeforeAdvice();Advice afterAdvice = new AfterAdvice();// 通知器:将切入点和通知连接起来,是对切面的实现Advisor advisor1 = new DefaultPointcutAdvisor(null, logAdvice, "0");Advisor advisor2 = new DefaultPointcutAdvisor(null, beforeAdvice, "1");Advisor advisor3 = new DefaultPointcutAdvisor(null, afterAdvice, "2");advisedSupport.addAroundAdvisors(advisor1);advisedSupport.addBeforeAdvisors(advisor2);advisedSupport.addAfterAdvisors(advisor3);Class<?>[] interfaces = demo.getClass().getInterfaces();advisedSupport.setInterfaces(interfaces);// 构建代理对象AopProxy aopProxy = factory.createAopProxy(advisedSupport);AOPDemo proxyObj = (AOPDemo) aopProxy.getProxy();// 执行方法proxyObj.send();}
}

这种AOP实现方式,没有借助注解,并且也不像Spring官方代码中,通知类型可以细化到方法上。

可以看出目前实现方式还是在类的层面,每一个类被定义为是环绕通知还是前置等。

此外,也没有涉及到切入点的实现,不过通过这个简易版AOP,可以帮助我们更好的理解AOP的实现原理。

1.2 关键类和接口

  1. 通知链:一个或多个List;
  2. Advice:通知,表示增强的功能;
  3. MethodInvocation:方法调用,用来对通知链中的通知进行依次调用;
  4. MethodInterceptor:方法拦截器,被MethodInvocation调用后,执行对应的通知,之后再调用回MethodInvocation
1.2.1 通知链

其实本质就是一个List,包括前置通知链、环绕通知链和后置通知链。

通知链中存放的是一个一个的通知类。

执行顺序为:前环绕通知—>前置通知—>目标方法—>后置通知—>后环绕通知。

实现的难点:怎么将环绕通知分开呢?前置/后置我们可以根据方法放置在目标方法的前面或后面来实现。对于环绕通知是一个方法,我们怎么将内部的代码分成两部分呢?

要实现这个需求,我们应该使用递归。当我们检测到要执行环绕通知后,会在进入到环绕通知内部执行后续所有操作,直到全部通知执行完成,才会从环绕通知中出来。

在这里插入图片描述

1.2.2 Advice

在这里插入图片描述

Advice:通知,表示增强的功能。我们根据情况定义了三个子接口。

实现这些接口的类,就是通知类。

值得注意的是:beforeafter都是无参的方法,而around需要传递一个ProceedingJoinPoint对象。

环绕通知比较特殊,它需要先执行前一部分通知,再执行目标方法(或before–>target),最后执行后一部分通知
基于此,我们应该在环绕通知内部进行通知链调用。

其实ProceedingJoinPoint正是MethodInvocation的封装。这一点,我们后面会讲到。

pjp.proceed()内部执行的是通知链调用,会执行后续的通知,等全部执行完毕,就会回调执行后环绕。

public class BeforeAdvice implements MethodBeforeAdvice {@Overridepublic void before() throws Throwable {System.out.println("-----before-----");}
}
public class LogAdvice implements MethodAroundAdvice {@Overridepublic Object around(ProceedingJoinPoint pjp) throws Throwable {System.out.println("-----Around Start-----");Object ret = pjp.proceed();System.out.println("-----Around End-----");return ret;}
}
public class AfterAdvice implements MethodAfterAdvice {@Overridepublic void after() throws Throwable {System.out.println("-----after-----");}
}
1.2.3 MethodInvocation

上一节提到了MethodInvocaiton,它是继承Joinpoint的一个子接口。Joinpoint是连接点,可以看作目标对象的全部方法。

它提供了四个方法:

  1. getThis获取Joinpoint代表方法所属对象;
  2. getArgs获取Joinpoint代表方法的参数;
  3. getMethod获取所代表的方法;
  4. proceed执行所代表的方法。

MethodInvocation的含义是:方法调用,它的实现类ReflectiveMethodInvocation,是AOP的核心,用来完成通知的调用(通知也是方法)。

具体来说proceed内部会实现依据通知链顺序完成对每一个通知的调用,通知的具体执行交给下一节的MethodInterceptor实现。

1.2.1图,MethodInvocation的proceed方法完成的是,通知类的拿取过程,如绿蓝红色线过程。

MethodInterceptor完成的是下部分的执行过程。两者是交错进行的相互调用

在这里插入图片描述

1.2.4 MethodInterceptor

拦截器,表示对执行目标方法的流程进行拦截后,做一些自己的事情,然后再放回去执行正常流程。
在这里插入图片描述
它的invokeMethodInvocationproceed方法是交替执行的,相互调用,这一点从下面的代码中也能看出。

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {private MethodBeforeAdvice advice;public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {this.advice=advice;}@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {advice.before();// invocation.proceed() 这个方法表示继续调用通知链中的下一个return invocation.proceed();}
}
public class MethodAroundAdviceInterceptor implements MethodInterceptor, Serializable {private MethodAroundAdvice advice;public MethodAroundAdviceInterceptor(MethodAroundAdvice advice) {this.advice = advice;}@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {return advice.around(new ProceedingJoinPoint(invocation));}
}public class MethodAfterAdviceInterceptor implements MethodInterceptor, Serializable {private MethodAfterAdvice advice;public MethodAfterAdviceInterceptor(MethodAfterAdvice advice) {this.advice=advice;}@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {advice.after();// invocation.proceed() 这个方法表示继续调用通知链中的下一个return invocation.proceed();}
}
1.2.5 MethodAdviceAdapter

方法装饰器。作用就是将通知转为对应的拦截器,拦截器内部包含通知,拦截下来后,执行通知。

public class MethodAdviceAdapter implements AdvisorAdapter {@Overridepublic MethodInterceptor getInterceptor(Advisor advisor) {Advice advice = advisor.getAdivce();if(advice instanceof MethodBeforeAdvice){return new MethodBeforeAdviceInterceptor((MethodBeforeAdvice)advice);} else if (advice instanceof MethodAroundAdvice) {return new MethodAroundAdviceInterceptor((MethodAroundAdvice)advice);}else{return new MethodAfterAdviceInterceptor((MethodAfterAdvice)advice);}}
}
1.2.6 ProceedingJoinPoint

它的作用其实就是对MethodInvocation对象进行封装,完成对环绕通知的调用。这里不封装也可以,直接向环绕通知传递MethodInvocation

public class ProceedingJoinPoint {private MethodInvocation invocation;public ProceedingJoinPoint(MethodInvocation invocation) {this.invocation = invocation;}public Object proceed() throws Throwable {return invocation.proceed();}
}

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

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

相关文章

配置策略路由实战 附带基础网络知识

背景 作为一个软件开发人员&#xff0c;不可能做到只负责业务开发工作&#xff0c;一旦功能上线或者系统切换就会遇到非常多考验开发人员个人能力的场景&#xff0c;网络调整就是非常重要的一个方面&#xff0c;如果你在系统上线的过程中无法处理一些简单的网络问题或者听不懂…

文件包含漏洞(1)

目录 PHP伪协议 php://input Example 1&#xff1a; 造成任意代码执行 Example 2&#xff1a; 文件内容绕过 php://filer zip:// PHP伪协议 php://input Example 1&#xff1a; 造成任意代码执行 搭建环境 <meta charset"utf8"> <?php error_repo…

Modern C++——不准确“类型声明”引发的非必要性能损耗

大纲 案例代码地址 C是一种强类型语言。我们在编码时就需要明确指出每个变量的类型&#xff0c;进而让编译器可以正确的编译。看似C编译器比其他弱类型语言的编译器要死板&#xff0c;实则它也做了很多“隐藏”的操作。它会在尝试针对一些非预期类型进行相应转换&#xff0c;以…

QT Quick QML 网络助手——TCP客户端

GitHub 源码: QmlLearningPro &#xff0c;选择子工程 Nettools.pro QML 其它文章请点击这里: QT QUICK QML 学习笔记 ● 运行效果&#xff1a; 左侧为常用的网络调试工具&#xff0c;右侧为本项目 UI 效果&#xff0c;前端使用 QML &#xff0c;后端使用C &#xff…

【文档智能 RAG】浅看开源的同质化的文档解析框架-Docling

前言 RAG的兴起&#xff0c;越来越多的人开始关注文档结构化解析的效果&#xff0c;这个赛道变得非常的同质化。 关于文档智能解析过程中的每个技术环节的技术点&#xff0c;前期文章详细介绍了很多内容&#xff1a; 下面我们简单的看看Docling这个PDF文档解析框架里面都有什…

GPIO(通用输入/输出)、中断(hal库)

目录 GPIO&#xff08;通用输入/输出)&#xff08;hal库&#xff09; GPIO工作模式 推挽输出&#xff08;Push-Pull Output&#xff09; 开漏输出&#xff08;Open-Drain Output&#xff09; 复用推挽输出&#xff08;Alternate Function Push-Pull Output&#xff09; 复…

在Ubuntu 22.04测试ebpf-go入门例子

文章目录 1、eBPF-Go依赖1.1 Ubuntu安装ssh server1.2 安装go1.3 安装llvm和clang1.4 安装libbpf和Linux kernel headers 2 编写eBPF C程序3 使用bpf2go编译eBPF C程序4 编写Go程序5 编译运行Go应用程序 eBPF-Go是一个使用eBPF的Go库。它不依赖于C、libbpf或除标准库之外的任何…

Windows单机安装配置mongodb+hadoop+spark+pyspark用于大数据分析

目录 版本选择安装配置Java环境配置Hadoop配置Spark配置 安装pyspark使用Jupyter Notebook进行Spark MongoDB测试参考 版本选择 根据Spark Connector&#xff1a;org.mongodb.spark:mongo-spark-connector_2.13:10.3.0 的前提要求 这里选择使用最新的MongoDB 7.0.12社区版 ht…

基于R语言进行AMMI分析3

参考资料&#xff1a;https://cran.r-project.org/web/packages/agricolae/agricolae.pdf 1、plot()函数 本次介绍的是Agricolae包中的plot.AMMI()函数。此函数可以绘制AMMI双标图&#xff0c;也可以绘制三标图&#xff08;三个坐标轴&#xff0c;IPCA1&#xff0c;IPCA2&…

TiggerRamDisk绕过激活界面,支持最新iOS17.4.1绕过

&#x1f427;技术交流&#xff1a;582022476 ——————— iOS15等待越狱的日子实在太久了&#xff01;checkra1n越狱目前还未发布iOS15系统越狱。 可很多朋友不小心或者大意已经升级到了最新iOS15系统。一般来说这并没有什么大碍&#xff0c;但如果是绕过激活的设备&#…

Cesium 展示——绘制水面动态升高

文章目录 需求分析需求 如图,绘制水面动态升高,作为洪水淹没的效果 分析 我们首先需要绘制一个面然后给这个面一个高度,在回调函数中进行动态设置值【这里有两种,一种是到达水面一定高度停止升高,一种是水面重新升高】/*** @description :洪水淹没* @author : Hukang*…

一起学Java(3)-Java项目构建工具Gradle和Maven场景定位和优缺点对比

在第一步创建的项目&#xff08;java-all-in-one&#xff09;项目里&#xff0c;我们提到了使用Gradle作为项目构建工具。看到这里&#xff0c;不知道你是否有疑惑&#xff0c;什么是项目构建工具。Java项目常用构建工具有哪些&#xff1f;都有什么特点&#xff1f; 带着疑惑&…

工厂现场多功能帮手,三防平板改善管理体验

随着制造业的智能化变革&#xff0c;信息化、自动化和智能化逐渐成为工厂管理的新常态。在这一波技术浪潮中&#xff0c;三防平板作为一种多功能的工作工具&#xff0c;正在逐步改善工厂现场的管理体验。 一、三防平板的定义与特点 三防平板&#xff0c;顾名思义&#xff0c;是…

Python测试框架之—— pytest介绍与示例

Pytest是一个功能强大且易于使用的Python测试框架&#xff0c;它提供了丰富的功能和灵活的用法&#xff0c;使得编写和运行测试变得简单而高效。 一、Pytest的特点 简单灵活&#xff1a;Pytest的语法简洁清晰&#xff0c;容易上手&#xff0c;并且支持复杂的测试场景。自动发…

python库(21):

1 TextBlob简介 TextBlob 是一个基于 Python 的文本处理库&#xff0c;能够让基础的自然语言处理任务变得异常简单。 它提供了一个简单直观的 API&#xff0c;让你能够轻松执行词性标注、名词短语提取、情感分析、文本分类和关键词提取等功能。 值得一提的是&#xff0c;Tex…

Qt+FFmpeg开发视频播放器笔记(二):界面UI搭建

效果图 主要使用无边框窗体搭建,实现窗体的拖动和缩放&#xff0c;播放列表、文件打开等。 主要代码 manwindow设计类 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QMutex> #include "framelesshelper.h"QT_BEGIN_NA…

【网络】传输层协议——TCP协议(进阶)

目录 1.TCP连接的11种状态 2.再次理解三次挥手 2.1.再次理解三次握手 2.2.Socket编程和三次握手的关系 2.2.1.listen的参数 2.2.2.accept函数 2.3.TCP三次握手的优点 2.3.1.阻止重复历史连接的初始化 2.3.2.为什么两次握手不能防止旧的重复连接初始化造成混乱呢&am…

【附源码】Python :PYQT界面点击按钮随机变色

系列文章目录 Python 界面学习&#xff1a;PYQT界面点击按钮随机变色 文章目录 系列文章目录一、项目需求二、源代码三、代码分析3.1 导入模块&#xff1a;3.2 定义App类&#xff1a;3.3 构造函数&#xff1a;3.4 初始化用户界面&#xff1a;3.5 设置窗口属性&#xff1a;3.6 …

基于YOLOv7算法的高精度实时课堂场景下人脸检测系统

项目目标 开发一套基于YOLOv7的高精度实时人脸检测系统&#xff0c;适用于课堂场景。实现对图片、视频、文件夹内的图像及摄像头流的实时人脸检测。提供直观的图形界面&#xff0c;方便用户操作。支持模型权重的灵活选择与加载&#xff0c;以及后处理参数的调整。 主要功能 模…

基础算法--递推算法[信奥一本通]

本节所讲题源自【信奥一本通】C版&#xff1a;基础算法-第三章-递推算法 相信大家应该都接触过数列的概念。哎哟&#xff0c;一直在跟数组打交道&#xff0c;说数列感觉好陌生&#xff0c;哈哈。数列中的迭代法大家都还记得吗&#xff1a;通过反复应用特定规则&#xff0c;推导…