JAVA注解处理API实战

news2024/11/16 7:56:04

简介

​ 插件化注解处理(Pluggable Annotation Processing)API JSR 269提供一套标准API来处理Annotations( JSR 175),实际上JSR 269不仅仅用来处理Annotation,它建立了Java 语言本身的一个模型,它把method、package、constructor、type、variable、enum、annotation等Java语言元素映射为Types和Elements,从而将Java语言的语义映射成为对象,我们可以在javax.lang.model包下面可以看到这些类。所以我们可以利用JSR 269提供的API来构建一个功能丰富的元编程(metaprogramming)环境。

JSR 269Annotation Processor编译期间而不是运行期间处理Annotation, Annotation Processor相当于编译器的一个插件,所以称为插入式注解处理.如果Annotation Processor处理Annotation时(执行process方法)产生了新的Java代码,编译器会再调用一次Annotation Processor,如果第二次处理还有新代码产生,就会接着调用Annotation Processor,直到没有新代码产生为止。每执行一次process()方法被称为一个"round",这样整个Annotation processing过程可以看作是一个round的序列。JSR 269主要被设计成为针对Tools或者容器的API。这个特性在JavaSE 6已经存在。

lombok就是使用这个特性实现编译期的代码插入的。

Pluggable Annotation Processing API的核心是Annotation Processor即注解处理器,一般需要继承抽象类javax.annotation.processing.AbstractProcessor

注意,与运行时注解RetentionPolicy.RUNTIME不同,注解处理器只会处理编译期注解,也就是RetentionPolicy.SOURCE的注解类型,处理的阶段位于Java代码编译期间。

使用步骤

​ 插件化注解处理API的使用步骤大概如下:

  1. 自定义一个注解,注解的元注解需要指定@Retention(RetentionPolicy.SOURCE)

  2. 自定义一个Annotation Processor,需要继承javax.annotation.processing.AbstractProcessor,并覆写process方法。

  3. 需要在声明的自定义Annotation Processor中使用javax.annotation.processing.SupportedAnnotationTypes指定在第2步创建的注解类型的名称(注意需要全类名,“包名.注解类型名称”,否则会不生效)。

    支持* 号,参考:@SupportedAnnotationTypes("lombok.*")

  4. 需要在声明的自定义Annotation Processor中使用javax.annotation.processing.SupportedSourceVersion指定编译版本。

  5. 可选操作,可以通在声明的自定义Annotation Processor中使用javax.annotation.processing.SupportedOptions指定编译参数。

  6. 编写代码,使用第1步定义的注解。

  7. 启用 自定义Processor。

  8. 验证

第3、4、5中的注解,可以通过接口方法指定

Set<String> getSupportedOptions();
Set<String> getSupportedAnnotationTypes();
SourceVersion getSupportedSourceVersion();

示例

目标

为注释了 VersionFill 的类加一个方法,返回编译时的时间戳

第1步:定义注解

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(value = RetentionPolicy.SOURCE)
public @interface VersionFill {
}

第2、3、4步:定义processor

package demon.research;

import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Names;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.time.LocalDateTime;
import java.util.Set;

@SupportedAnnotationTypes({"demon.research.VersionFill"})
@SupportedSourceVersion(value = SourceVersion.RELEASE_8)
public class VersionFillProcessor extends AbstractProcessor {
    /**
     * 用于在编译器打印消息的组件
     */
    Messager messager;

    /**
     * 语法树
     */
    JavacTrees trees;

    /**
     * 用来构造语法树节点
     */
    TreeMaker treeMaker;

    /**
     * 用于创建标识符的对象
     */
    Names names;

    static final String VERSION_METHOD_NAME = "version";

    String versionStr = "";

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        this.messager = processingEnv.getMessager();
        this.trees = JavacTrees.instance(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        this.treeMaker = TreeMaker.instance(context);
        this.names = Names.instance(context);

        versionStr = LocalDateTime.now().toString();
    }

    /**
     * {@inheritDoc}
     *
     * @param annotations
     * @param roundEnv
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        System.out.println("Log in AnnotationProcessor.process");
        for (TypeElement typeElement : annotations) {
            Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(typeElement);

            annotatedElements.forEach(element -> {

                if (element instanceof TypeElement) {
                    //获取当前元素的JCTree对象
                    JCTree jcTree = trees.getTree(element);

                    //JCTree利用的是访问者模式,将数据与数据的处理进行解耦,TreeTranslator就是访问者,这里我们重写访问类时的逻辑
                    jcTree.accept(new TreeTranslator() {

                        @Override
                        public void visitClassDef(JCTree.JCClassDecl jcClass) {
                            messager.printMessage(Diagnostic.Kind.NOTE, "");
                            JCTree.JCMethodDecl methodDecl = createVersion();
                            jcClass.defs = jcClass.defs.append(methodDecl);

                            super.visitClassDef(jcClass);
                        }
                    });
                }

                if (element instanceof Symbol.MethodSymbol) {
                    //获取当前元素的JCTree对象
                    JCTree jcTree = trees.getTree(element);

                    //JCTree利用的是访问者模式,将数据与数据的处理进行解耦,TreeTranslator就是访问者,这里我们重写访问类时的逻辑
                    jcTree.accept(new TreeTranslator() {

                        @Override
                        public void visitMethodDef(JCTree.JCMethodDecl jcMethodDecl) {
                            super.visitMethodDef(jcMethodDecl);
                            updateVersion(jcMethodDecl);
                        }
                    });
                }
            });

            System.out.println(annotatedElements);
        }

        System.out.println(roundEnv);
        return true;
    }

    /**
     * 创建全参数构造方法
     *
     * @return 全参构造方法语法树节点
     */
    private JCTree.JCMethodDecl createVersion() {

        ListBuffer<JCTree.JCStatement> jcStatements = new ListBuffer<>();
        jcStatements.append(treeMaker.Return(
                treeMaker.Literal(versionStr)
        ))
        ;

        JCTree.JCBlock jcBlock = treeMaker.Block(
                0 //访问标志
                , jcStatements.toList() //所有的语句
        );

        return treeMaker.MethodDef(
                treeMaker.Modifiers(Flags.PUBLIC), //访问标志
                names.fromString(VERSION_METHOD_NAME), //名字
                treeMaker.Ident(names.fromString("String")), //返回类型
                List.nil(), //泛型形参列表
                List.nil(), //参数列表
                List.nil(), //异常列表
                jcBlock, //方法体
                null //默认方法(可能是interface中的那个default)
        );
    }

    private void updateVersion(JCTree.JCMethodDecl jcMethodDecl) {
        ListBuffer<JCTree.JCStatement> jcStatements = new ListBuffer<>();
        jcStatements.append(treeMaker.Return(
                treeMaker.Literal(versionStr)
        ))
        ;
        JCTree.JCBlock body = treeMaker.Block(0, jcStatements.toList());
        jcMethodDecl.body = body;
    }
}

如果使用JCMaker, maven项目需要引入:

<dependency>
    <groupId>com.sun</groupId>
    <artifactId>tools</artifactId>
    <version>1.8</version>
    <scope>system</scope>
    <systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>

第6步:编写需要处理的代码

package demon.research;

@VersionFill
public class VersionController {


    @VersionFill
    public String version2() {
        return "default2";
    }
}



public class MainClass {
    public static void main(String[] args) {
        VersionController controller = new VersionController();
        //此处调用的是自动生成的方法
        System.out.println(controller.version());
        int a;
    }
}

第7步:启用自定义Processor

注意:如果使用IDEA的话,Compiler->Annotation Processors中的Enable annotation processing必须勾选

使用mvn配置

pom.xml中配置 build 元素,添加 annotationProcessor


    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.6.0</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                    <encoding>UTF-8</encoding>
                    <annotationProcessors>
                        <!-- 添加自定义的Processor -->
                        <annotationProcessor>
                            demon.research.VersionFillProcessor
                        </annotationProcessor>
                    </annotationProcessors>

                </configuration>
            </plugin>
        </plugins>
    </build>

直接使用编译参数指定

javac -processor demon.research.VersionFillProcessor MainClass.java。

通过服务注册指定

META-INF/services/javax.annotation.processing.Processor文件中添加demon.research.VersionFillProcessor

具体见SPI 。

问题 1

第一次编译的时候,出现以下错误:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project plugin-anno: Compilation failure
[ERROR] 找不到注释处理程序 'demon.research.VersionFillProcessor'
[ERROR]
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.

原因:Processor 生效的前提是自定义的Processor 是 已经被编译过,否则编译的时候就会报错。

解决方法

1、先单独编译Processor 类

image-20221229180506956

2、把Processor类作为单独一个jar包,引入

正常输出:

[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ plugin-anno ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 4 source files to E:\projects_study\research\plugin-anno\target\classes
Log in AnnotationProcessor.process  
demon.research.VersionFill
[errorRaised=false, rootElements=[demon.research.VersionFill, demon.research.VersionFillProcessor, demon.research.VersionController, demon.research.MainCl
ass], processingOver=false]
Log in AnnotationProcessor.process   xxxxx
[errorRaised=false, rootElements=[], processingOver=true]
[INFO] ------------------------------------------------------------------------

问题2:生成的类,没有改变。

JCTree.JCClassDecldefs 属性是 com.sun.tools.javac.util.List。不是list的数据结构,添加元素的时候就把自己赋给自己的tail,新来的元素放进head。不过需要注意的是这个东西不支持链式调用,prepend或者 append之后还要将新值赋给自己。

// 注意:append之后,要把修改后的值赋值给 defs。
jcClass.defs = jcClass.defs.append(methodDecl);

编译输出

package demon.research;

public class VersionController {
    public VersionController() {
    }

    public String version2() {
        return "2022-12-30T13:31:26.816";
    }

    public String version() {
        return "2022-12-30T13:31:26.816";
    }
}

IDEA调试自定义Processor

1、设置构建过程的调试端口

选择菜单:Help -> Edit Custom VM Options,添加下面的内容,并重启 IDEA:

(端口与 mvnDebug的 端口一致)

-Dcompiler.process.debug.port=8889

2、 配置 Run/Debug Configurations。

image-20221229160254194

3、启动一个编译过程。

E:\projects_study\research>mvnDebug compile
Listening for transport dt_socket at address: 8889

此时 编译过程会被挂起。等待绑定

4、启动调试。

在代码中加断点。

使用第2步配置的Configuration,启动调试。

image-20221229173312353

参数 annotations 的值为 Processor 定义上注解 @SupportedAnnotationTypes 的value值

image-20221229173422808

image-20221229180950176

注意:如果 target 目录下 包含所有待编译的类,就有可能不再触发 process 过程。即有编译需要,才触发,没有要编译的java文件,则不触发。

附录

参考

https://www.cnblogs.com/flyingskya/p/10970350.html

element:https://nowjava.com/docs/java-api-11/java.compiler/javax/lang/model/element/package-summary.html

debug 端口被占用问题解决

mvnDebug 命令所在的文件为 %MAVEN_HOME%/bin/mvnDebug.cmd,设置了debug 端口,默认为8000

@setlocal
@set MAVEN_DEBUG_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000

@call "%~dp0"mvn.cmd %*

在启动 mvnDebug命令时,如果输出以下异常信息:

ERROR: transport error 202: bind failed: Address already in use
ERROR: JDWP Transport dt_socket failed to initialize, TRANSPORT_INIT(510)
JDWP exit error AGENT_ERROR_TRANSPORT_INIT(197): No transports initialized [debugInit.c:750]

则表示 debug 端口被占用。

在windows 下可以通过以下命令查看端口被占用的情况:

E:\projects_study\research\plugin-anno>netstat -ano |findstr 8000
  TCP    0.0.0.0:8000           0.0.0.0:0              LISTENING       8280
  TCP    0.0.0.0:18000          0.0.0.0:0              LISTENING       8280
  TCP    127.0.0.1:8000         127.0.0.1:61597        TIME_WAIT       0
  TCP    127.0.0.1:8000         127.0.0.1:61608        TIME_WAIT       0
  TCP    127.0.0.1:8000         127.0.0.1:61614        TIME_WAIT       0

8280 为 占用端口8000的进程的pid。通过任务管理器,查看进程。

image-20221229170540287

可以结束进程,或者修改 mvnDebug的端口。

@setlocal
@set MAVEN_DEBUG_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8889

@call "%~dp0"mvn.cmd %*

Element

接口描述
AnnotationMirror表示注释。
AnnotationValue表示注释类型元素的值。
AnnotationValueVisitor<R,P>使用访问者设计模式的变体访问注释类型元素的值。
Element表示程序元素,例如模块,包,类或方法。
ElementVisitor<R,P>程序元素的访问者,以访问者设计模式的风格。
ExecutableElement表示类或接口的方法,构造函数或初始化程序(静态或实例),包括注释类型元素。
ModuleElement表示模块程序元素。
ModuleElement.Directive表示此模块声明中的指令。
ModuleElement.DirectiveVisitor<R,P>模块指令的访问者,以访问者设计模式的样式。
ModuleElement.ExportsDirective导出的模块包。
ModuleElement.OpensDirective一个打开的模块包。
ModuleElement.ProvidesDirective模块提供的服务的实现。
ModuleElement.RequiresDirective模块的依赖关系。
ModuleElement.UsesDirective对模块使用的服务的引用。
Name不可变的字符序列。
PackageElement表示包程序元素。
Parameterizable具有类型参数的元素的mixin接口。
QualifiedNameable具有限定名称的元素的mixin接口。
TypeElement表示类或接口程序元素。
TypeParameterElement表示泛型类,接口,方法或构造函数元素的正式类型参数。
VariableElement表示字段, 枚举常量,方法或构造函数参数,局部变量,资源变量或异常参数。

JCTree

参考:

​ Java 中的屠龙之术:如何修改语法树?

​ java AST JCTree简要分析 (已转载)

​ Java-JSR-269-插入式注解处理器 (已转载)

JCTree是语法树元素的基类,包含一个重要的字段pos,该字段用于指明当前语法树节点(JCTree)在语法树中的位置,因此们不能直接用new关键字来创建语法树节点,即使创建了也没有意义。

JCTree是一个抽象类,这里重点介绍几个JCTree的子类:

  • JCStatement:声明语法树节点,常见的子类如下

  • JCBlock:语句块语法树节点

  • JCReturn:return语句语法树节点

  • JCClassDecl:类定义语法树节点

  • JCVariableDecl:字段/变量定义语法树节点

  • JCMethodDecl:方法定义语法树节点

  • JCModifiers:访问标志语法树节点

  • JCExpression:表达式语法树节点,常见的子类如下

  • JCAssign:赋值语句语法树节点

  • JCIdent:标识符语法树节点,可以是变量,类型,关键字等等

TreeMaker

TreeMaker用于创建一系列的语法树节点,们上面说了创建JCTree不能直接使用new关键字来创建,所以Java为们提供了一个工具,就是TreeMaker,它会在创建时为们创建的JCTree对象设置pos字段,所以必须使用上下文相关的TreeMaker对象来创建语法树节点。

一些语句构造

//字面量 
treeMaker.Literal("this is a literal");
//获取String 类型
treeMaker.Ident(names.fromString("String"))
//返回的是原生类型:JCPrimitiveTypeTree
treeMaker.TypeIdent(TypeTag.BYTE)

参考: java AST JCTree简要分析

**只要是能修改,节点被修改了,就会生效。**例如,bodydefs等。

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

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

相关文章

工业远程I/O模块 CANopen 通讯

1.对象字典OD 对象字典是每个CANopen设备必须具有的功能&#xff0c;它包含了设备所有可以被访问的参数&#xff0c;客户端使用索引(Index)和子索引(Sub-index)来读写对象字典里的参数。 对象字典主要用来设定设备组态及进行非即时的通讯。每个对象采用16位索引的方式来寻址&…

01Editor最新破解

文章目录01Editor最新版注册算法逆向1.定位注册算法代码2.整体注册算法3.Check1算法分析4.Check2算法分析5.获得正确的任意用户名对应的序列号01Editor最新版注册算法逆向 1.定位注册算法代码 【版本】13.0.164bit\textcolor{green}{【版本】13.0.1\ 64bit}【版本】13.0.1 64b…

Redis集群系列七 —— 散列插槽分析

集群状态日志分析 Redis 分片集群引入了一个逻辑上的插槽或哈希槽概念&#xff0c;将集群划分为16384&#xff08;0~16383&#xff09;个槽位&#xff0c;集群中的每个节点占据一部分槽位数&#xff0c;在逻辑上将集群中的所有节点构成了一块完整的内存空间。 这个日志中可以通…

对象定义-解构-枚举属性遍历以及对象内函数

属性名表达式 定义对象的属性有两种方式 1、直接使用标识符作为属性名 obj.name 2、以表达式作为属性名 obj[ab] 10 let obj {} obj.name 孙悟空 // 孙悟空 obj[a b] 10 // 10 console.log(obj); // {name: 孙悟空, ab: 10}es5中字面量定义对象只能使用一种方法 var …

4.3.3、划分子网的 IPv4 地址

若有一个大型的局域网需要连接到因特网 若需要申请一个 C 类网络地址&#xff0c;其可分配的 IP 地址数量只有 254254254 个&#xff0c;不够使用 因此申请了一个 B 类网络地址&#xff0c;其可分配的 IP 地址数量达到了 655346553465534 个 给每台计算机和路由器的接口分配一…

实验九、消除互补输出级交越失真方法的研究

一、题目 互补输出级交越失真消除方法的研究。 二、仿真电路 基本互补电路和消除交越失真互补输出级如图1所示。晶体管采用 NPN 型晶体管 2N3904 和 PNP 型晶体管 2N3906。二极管采用 1N4009。 在实际的实验中&#xff0c;几乎不可能得到具有理想对称性的 NPN 型和 PNP 型管…

网络编程套接字----UDP协议

文章目录前言一、理解源IP地址和目的IP地址二、认识端口号理解"端口号"和"进程ID"理解源端口号和目的端口号三、认识TCP协议四、认识UDP协议五、网络字节序六、socket编程接口socket常见APIsockaddr结构sockaddr结构sockaddr_in 结构in_addr结构七、地址转…

第三方软件测试▏有效保障软件产品质量的关键性步骤

软件测试作为软件产品生命周期中不可或缺的重要步骤&#xff0c;被许多软件企业所重视。主要是通过对软件产品进行全面的测试&#xff0c;确保软件质量以及满足用户需求。但软件测试不仅仅是个简单的检测工作&#xff0c;而是一个系统性的、有组织性的测试过程&#xff0c;包含…

Linux:安装 telnet 命令

我是 ABin-阿斌&#xff1a;写一生代码&#xff0c;创一世佳话&#xff0c;筑一览芳华。如果小伙伴们觉得不错就一键三连吧~ 声明&#xff1a;原文地址&#xff1a;https://www.pudn.com/news/6332b44a272bb74d44053074.html 其他参考文章&#xff1a;https://www.cnblogs.com…

尚医通-上传医院接口-需求准备(十七)

目录&#xff1a; &#xff08;1&#xff09;数据接口-上传医院接口-需求准备 &#xff08;1&#xff09;数据接口-上传医院接口-需求准备 在医院接口设置的时候说过&#xff0c;我们做的是预约挂号平台&#xff0c;里面有数据的显示&#xff0c;挂号等等相关业务&#xff0c;…

计算机组成原理【by王道考研计算机】

文章目录第一章1. 什么是计算机系统2. 硬件的发展3. 计算机硬件的基本组成冯诺依曼结构现代计算机结构主存储器运算器控制器工作过程实例4. 计算机系统的层次结构五层结构三种级别的语言5. 计算机的性能指标存储器的容量CPU其他常用时间单位第二章1. 进制转换2. 字符与字符串3.…

下一代,容器工具Podman

一、简介 Kubernetes 团队发布了最新的 1.20 版本时&#xff0c;有一枚重磅炸弹&#xff1a;正式宣布弃用 Docker 支持的功能。弃用 Docker 之后&#xff0c;开发者们对其替代品的讨论逐渐热烈&#xff0c;其中 Containerd 和 Podman 倍受期待。 Podman 是一个开源的容器运行…

云原生、20.3k Star......时序数据库 TDengine 的 2022 年精彩纷呈

日月其迈&#xff0c;时盛岁新 2022 的进度条已经加载至“100%” 疫情肆虐下&#xff0c;毫无疑问 这仍然是头顶风雪攀登山峰的一年 好在如今曙光已现 回望这一年&#xff0c;TDengine 也硕果满满 2022 年是 TDengine 创立的第六个年头 我们付出着&#xff0c;也在收获着…

英伟达Orin芯片平台使用TensorRT加速部署YOLO

前言 自动驾驶行业想要在开发板上部署一套算法还是需要花费很大功夫的&#xff0c;因为计算资源的限制以及实时性要求高。并不像在服务器端部署算法那样粗犷。接下来就记录一下YOLO系列部署的过程&#xff0c;后期会把开发板上的问题都记录在此。 一、部署环境 计算平台&…

11.简单的CSS按钮悬停特效

效果 源码 <!DOCTYPE html> <html> <head><title>Button Hover Effects</title><link rel="stylesheet" type="text/css" href="style.css"> </head> <body><a href="#"><…

信贷产品年终总结之风控评分模型

叮咚&#xff0c;信贷年终总结的又一个专题来了&#xff0c;作为报告总结类的系列型文章&#xff0c;近期我们番茄知识星球平台陆续发布了相关年终总结专题&#xff0c;依次为客群特征画像、贷中行为分析、贷后逾期表现等&#xff0c;以上文章可详见之前陆续发布的内容。该业务…

nacos配置部署与管理

nacos配置部署与管理配置文件配置获取顺序&#xff1a;nacos配置热部署方式一&#xff1a;RefreshScope方式二&#xff1a;ConfigurationProperties配置文件 首先新建配置文件 Data ID&#xff1a;配置文件的名称&#xff08;唯一&#xff09;命名规范&#xff1a;服务名称-运…

广州车展|继原力技术之后,长安深蓝半固态电池呼之欲出

新年将至&#xff0c;万象更新。12月30日&#xff0c;2022第二十届广州国际汽车展览会在广州中国进出口商品交易会展馆隆重举办。全新数字纯电品牌长安深蓝携旗下首款战略车型深蓝SL03重磅参展&#xff0c;并邀请深蓝SL03车主亲临车展现场&#xff0c;或进行产品讲解&#xff0…

面向对象分析与设计的底层逻辑

作者&#xff1a;高福来 阿里全球化业务平台团队 在面向对象出现之前&#xff0c;已有面向过程的分析方法&#xff0c;那为什么面向对象被提出了呢&#xff1f;究其本质&#xff0c;人们发现面向过程并非按照人正常认识事物的方式去分析软件。面向过程是一种归纳的分析方法&a…

virtio技术(3)virtqueue机制

virtio技术&#xff08;3&#xff09;virtqueue机制 virtio的关键技术是virtqueue机制&#xff0c;其提供了一套统一的用于virito前端和后端的通信机制。virtqueue的核心数据结构是vring&#xff0c;这是virtio前端驱动和后端Hypervisor虚拟设备之间传输数据的载体。 vring数…