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

Nop入门:极简AOP实现

讲解视频:哔哩哔哩_bilibili

AOP(Aspect Oriented Programming)本质上是一个非常简单的概念(定位+局部逻辑修改),但是它的实现却往往并不简单。作为Spring框架的一个核心功能,SpringAOP中引入的异常复杂的切点定义语法。但是在实际使用场景中,唯一得到广泛应用的切点(Pointcut)定义方法就是使用注解(Annotation)。
另外一方面,SpringAOP会使用动态代码生成或者动态代理类,这会影响到应用的启动速度或者运行时性能,对于GraalVM原生编译也不友好。Nop平台采用代码生成的方式实现了一个最简单的AOP机制,具有最优的性能且完全满足我们一般应用的需求,全部代码仅有不到2000行。

NopIoC容器内置了对AOP的支持,这也简化了NopIoC容器的实现,使得它不必像Spring那样为了支持AOP被迫引入多级对象缓存(NopIoC仅使用了一级对象缓存)。

本文将简单介绍一下NopAOP的实现原理和使用方法,详细介绍参见aop.md

一. 注册用于AOP切点的注解类

NopAOP并不使用动态字节码生成技术,而是直接生成代理类的源码,因此它需要在编译的时候知道需要为哪些类生成代理类。具体的做法就是扫描所有类的方法,识别哪些方法使用了特殊的注解,并为这些类生成代理类。

例如在nop-quarkus-demo项目中,我们增加了一个SendEmail的注解类和/_vfs/nop/aop/nop-quarkus-demo.annotations文件。

io.nop.demo.annotations.SendEmail
  • AOP的应用需要程序的结构空间存在一种稳定的定位坐标体系。

  • 复杂的Pointcut相当于是通过动态计算得到的一种不稳定的定位坐标。例如如果pointcut是匹配函数名的前缀或者后缀,那么当程序员不按照特定的命名规范编写代码时,编译器不会报任何错误,但是AOP却拦截不到对应的方法。

  • 使用自定义的注解相当于是在程序的结构空间提供一种专用于AOP的稳定坐标。对比之下,方法名可能因为各种情况而改变,无法保证稳定。

  • Nop平台在生成AOP代理类时会扫描/nop/aop/目录下的所有后缀名为annotations的文件。一般情况下这个文件名会与所在的模块名同名,避免不同模块定义的文件名发生冲突。

二. 生成AOP代理类

Java生态中用于编译期代码生成的标准技术方案是APT(Annotation Processing Tool),它是 Java编译器的一个功能,用于在编译期间执行对源代码中注解(Annotations)的处理。APT 可以读取和分析源代码中的注解,并生成新的代码(如类、接口、枚举等)或者生成额外的文件(如XML、配置文件等)。但是使用这个技术需要了解一定的APT的相关知识,所编写的代码还不能独立于Java编译过程来使用,因此NopAOP没有使用APT技术,而是直接提供了一个GenAopProxy帮助类使用Java反射技术获取类的方法信息,然后生成AOP代理类。

在nop-entropy项目的根pom文件中预定义了exec-maven-plugin插件的参数,设置了aop执行阶段,它会负责执行AOP代码生成工作。

<!-- nop-entropy的pom文件 -->
<pom>...<pluginManagement><plugins><plugin><groupId>org.codehaus.mojo</groupId><artifactId>exec-maven-plugin</artifactId><version>3.0.0</version><executions><execution><id>aop</id><phase>compile</phase><goals><goal>java</goal></goals><configuration><arguments><argument>${project.basedir}</argument><argument>aop</argument></arguments></configuration></execution></executions><configuration><classpathScope>compile</classpathScope><includePluginDependencies>true</includePluginDependencies><includeProjectDependencies>true</includeProjectDependencies><addResourcesToClasspath>true</addResourcesToClasspath><mainClass>io.nop.codegen.task.CodeGenTask</mainClass><cleanupDaemonThreads>false</cleanupDaemonThreads></configuration></plugin></plugins></pluginManagement>
</pom>

在需要生成代理类的模块中,我们可以从nop-entropy的根pom继承,并引入exec-maven-plugin插件即可。

<pom><parent><groupId>io.github.entropy-cloud</groupId><artifactId>nop-entropy</artifactId><version>2.0.0-SNAPSHOT</version></parent><build><plugins><plugin><groupId>org.codehaus.mojo</groupId><artifactId>exec-maven-plugin</artifactId></plugin></plugins></build>
</pom>

在示例应用中,我们为DemoBizModel的testMethod1方法增加@SendEmail注解。

@BizModel("Demo")
public class DemoBizModel {@BizQuery@SendEmailpublic void testMethod1(@RequestBean MyRequest request) {System.out.println("doSomething");}
}

然后执行mvn package或者mvn install指令,通过exec-maven-plugin插件会自动生成一个对应的DemoBizModel__aop类。

package io.nop.demo.biz;@io.nop.api.core.annotations.aop.AopProxy({io.nop.demo.annotations.SendEmail.class})
public class DemoBizModel__aop extends io.nop.demo.biz.DemoBizModel implements io.nop.core.reflect.aop.IAopProxy {private io.nop.core.reflect.aop.IMethodInterceptor[] $$interceptors;@Overridepublic void $$aop_interceptors(io.nop.core.reflect.aop.IMethodInterceptor[] interceptors) {this.$$interceptors = interceptors;}private static io.nop.core.reflect.IFunctionModel $$testMethod1_0;static {try {$$testMethod1_0 = io.nop.core.reflect.impl.MethodModelBuilder.from(io.nop.demo.biz.DemoBizModel.class, io.nop.demo.biz.DemoBizModel.class.getDeclaredMethod("testMethod1", io.nop.demo.biz.MyRequest.class));} catch (Exception e) {e.printStackTrace();}}public DemoBizModel__aop() {super();}@Overridepublic void testMethod1(final io.nop.demo.biz.MyRequest arg0) {if (this.$$interceptors == null || this.$$interceptors.length == 0) {super.testMethod1(arg0);return;}io.nop.core.reflect.aop.CallableMethodInvocation $$methodInv = new io.nop.core.reflect.aop.CallableMethodInvocation(this,new java.lang.Object[]{arg0}, $$testMethod1_0, () -> {super.testMethod1(arg0);return null;});io.nop.core.reflect.aop.AopMethodInvocation $$inv = new io.nop.core.reflect.aop.AopMethodInvocation($$methodInv, this.$$interceptors);try {$$inv.proceed();} catch (java.lang.Exception e) {throw io.nop.api.core.exceptions.NopException.adapt(e);}}
}
  • 生成的代理类上通过@AopProxy注解来快速获取实际用到的注解类
  • NopAOP不使用Java内置的Method类,而是使用IFunctionModel接口,使得在Java反射系统之外也可以复用这些Interceptor

三. 增加IMethodInterceptor实现类

IMethodInterceptor接口类似于Spring中使用的MethodInterceptor接口,只是使用IFunctionModel代替Java反射模型。

public class SendEmailInterceptor implements IMethodInterceptor {@Overridepublic Object invoke(IMethodInvocation inv) throws Exception {if (inv.getArguments().length <= 0)return inv.proceed();Object arg = inv.getArguments()[0];if (arg instanceof MyRequest) {System.out.println("sendEmail:message=" + ((MyRequest) arg).getMessage());}return inv.proceed();}
}

四. 在IoC容器中注册Interceptor,并声明Pointcut

    <bean id="sendEmailInterceptor" class="io.nop.demo.interceptors.SendEmailInterceptor"><ioc:pointcut annotations="io.nop.demo.annotations.SendEmail"/></bean>
  • NopIoC为Spring1.0的语法扩展了<ioc:pointcut>节点,通过它表示Interceptor所作用的AOP切点。
  • 在bean容器初始化的时候会检查容器中是否有可应用的Interceptor,如果有,则用代理类的Constructor代替原始类的Constructor。

基于可逆计算理论设计的低代码平台NopPlatform已开源:

  • gitee: canonical-entropy/nop-entropy
  • github: entropy-cloud/nop-entropy
  • 开发示例:docs/tutorial/tutorial.md
  • 可逆计算原理和Nop平台介绍及答疑_哔哩哔哩_bilibili

http://www.mrgr.cn/news/66272.html

相关文章:

  • 【矩阵的大小和方向的分解】
  • thrift rpc 四种类型的服务端的实现详细介绍
  • 入门react-native安装react-native-router-flux路由踩坑日记
  • 基于Multisim数控直流稳压电源电路(含仿真和报告)
  • C语言案例——青蛙跳台阶问题
  • Linux_shell编程
  • Android问题 -- DJ多多的下载文件在哪里? DJ多多dat格式转换为mp3
  • LoRA(Low-Rank Adaptation)的工作机制 - 在 GPT-2 的注意力层中添加 LoRA 低秩适配器
  • Git遇到“fatal: bad object refs/heads/master - 副本”问题的解决办法
  • 基于 GADF+Swin-CNN-GAM 的高创新轴承故障诊断模型
  • 41.第二阶段x86游戏实战2-C++实现lua寻路
  • 基于STM32的自动化植物浇灌系统教学
  • 【Qt】使用Qt发送http请求封装一个通用类
  • 劫持微信聊天记录并分析还原 —— 解密数据库(二)
  • 工作中问题
  • 新一代跟踪器StrongSORT: Make DeepSORT Great Again论文解析—让 DeepSORT 再次伟大
  • nacos本地虚拟机搭建切换wiff问题
  • 基于SpringBoot的免税商品优选购物商城的设计与实现
  • 小美和大富翁
  • 动态规划 —— dp问题-按摩师
  • Docker 的基本概念和优势
  • 气体传感器种类详解:从半导体到红外吸收型的全面解析
  • 仿真APP助力汽车零部件厂商打造核心竞争力
  • 解决从huggingface.co下载模型失败问题
  • EasyQBlog .NET 8 + Q-Blog 2.0博客模板 + easyweb iframe后台模板 开发的个人博客
  • 树莓派开发相关知识十 -小车服务器