[转载]MSIL Instruction Table

简介:
原文地址:http://safari.oreilly.com/0735616485/IDABH1W

Throughout this text, I've often employed the ILDASM utility to illustrate the Microsoft intermediate language (MSIL) that the C# compiler generates. I used ILDASM to give you a more complete understanding of the lower-level workings of the C# compiler. Although I briefly explain what each MSIL instruction does in the context of the chapter in which the instruction is used, I thought it would be nice to have a central listing of all the instructions. In the following table, I list each MSIL instruction, its opcode, the parameters that are passed to the instruction, and the resulting stack transition.

Here are a few notes that should help you when reading the table:

  • Because pushing means to place something on the stack, I use the phrase pushing a value rather than the more verbose pushing the value onto the stack.

  • Because popping is widely understood to mean taking something off the stack, I use the phrase popping the value instead of the more verbose popping the value off the stack.

  • When I use the word indirect, I'm referring to retrieving a value through its address. Therefore, the phrase pushes (indirect) value should be understood to mean pushes the value pointed at by an address onto the stack.

  • The term native int represents a 32-bit integer on all 32-bit versions of Microsoft Windows.

  • The letter F represents the native floating point.

  • The state transition column is intended to illustrate a before-and-after picture of the stack. For example, the stack transition: "…, value1, value2-> …, result" indicates that before the instruction is executed, the stack must contain two values (value1,value2), and that after the instruction is executed, these values will be replaced with one value (result). Note that the ellipsis character simply indicates unknown values on the stack that have nothing to do with this instruction.

Table A-1. MSIL Instructions
Instruction (Opcode) Description Stack Transition add (0x58) Adds two values, pushing result ...,val1,val2->...,result add.ovf (0xD6) Adds integer values, pushing result; throws exception on overflow ...,val1,val2->...,result add.ovf.un (0xD7) Adds unsigned integer values, pushing result; throws exception on overflow ...,val1,val2->...,result and (0x5F) Bitwise AND of two integral values, pushing an integral value ...val1,val2->...,result arglist (0xFE 0x00) Pushes argument list handle for current method ...->...,argListHandle beq int32 (0x3B) Branches to specified offset if two top stack values are equal ...,value,value->... beq.s int8 (0x2E) Branches to specified offset if two top stack values are equal ...,value,value->... bge int32 (0x3C) Branches to specified offset if value1 is greater than or equal to value2 ...,value1,value2->... bge.s int8 (0x2F) Branches to specified offset if value1 is greater than or equal to value2 ...,value1,value2->... bge.un int32 (0x41) Branches to specified offset if value1 is greater than or equal to value2 (unsigned or unordered) ...,value1,value2->... bge.un.s int8 (0x34) Branches to specified offset if value1 is greater than or equal to value2 (unsigned or unordered) ...,value1,value2->... bgt int32 (0x3D) Branches to specified offset if value1 is greater than value2 ...,value1,value2->... bgt.s int8 (0x30) Branches to specified offset if value1 is greater than value2 ...,value1,value2->... bgt.un int32 (0x42) Branches to specified offset if value1 is greater than value2 (unsigned or unordered) ...,value1,value2->... bgt.un.s int8 (0x35) Branches to specified offset if value1 is greater than value2 (unsigned or unordered) ...,value1,value2->... ble int32 (0x3E) Branches to specified offset if value1 is less than or equal to value2 ...,value1,value2->... ble.s int8 (0x31) Branches to specified offset if value1 is less than or equal to value2 ...,value1,value2->... ble.un int32 (0x43) Branches to specified offset if value1 is less than or equal to value2 (unsigned or unordered) ...,value1,value2->... ble.un.s int8 (0x36) Branches to specified offset if value1 is less than or equal to value2 (unsigned or unordered) ...,value1,value2->... blt.un int32 (0x44) Branches to specified offset if value1 is less than value2 (unsigned or unordered) ...,value1,value2->... blt.un.s int8 (0x37) Branches to specified offset if value1 is less than value2 (unsigned or unordered) ...,value1,value2->... bne.un.s int8 (0x33) Branches to specified offset if value1 isn't equal to value2 (unsigned or unordered) ...,value1,value2->... blt int32 (0x3F) Branches to specified offset if value1 is less than value2 ...,value1,value2->... blt.s int8 (0x32) Branches to specified offset if value1 is less than value2 ...,value1,value2->... bne.un int32 (0x40) Branches to specified offset if value1 isn't equal to value2 (unsigned or unordered) ...,value1,value2->... box type (0x8C) Converts value type to object reference ...,valType->...,obj br int32 (0x38) Unconditional branch to specified offset ...->... br.s int8 (0x2B) Branches to specified offset ...->... Break (0x01) Informs the debugger that a breakpoint has been reached ...->... brfalse int32 (0x39) Branches to specified offset if value on stack is false ...,value->... brfalse.s int8 (0x2C) Branches to specified offset if value on stack is false ...,value->... brtrue int32 (0x3A) Branches to specified offset if value on stack is true ...,value->... brtrue.s int8 (0x2D) Branches to specified offset if value on stack is true ...,value->... call method (0x28) Calls a method ...->... calli signature (0x29) Calls method indicated by address on stack; stack also contains 1...n arguments

...,arg1,arg2,argN, method->retVal

(retVal is not always pushed onto the stack as a result of the calli signature instruction.)

callvirt method (0x6F) Calls virtual method of obj

...,obj,arg1... argn->...,retVal

(Not always pushed onto the stack for the callvirt method instruction.)

castclass type (0x74) Casts obj to class ...,obj->...,obj2 ceq (0xFE 0x01) Compares equality of two values on stack; pushes 1 if equal; otherwise, pushes 0 ...,val1,val2->...,result cgt (0xFE 0x02) Compares to see whether val1 is greater than val2; pushes 1 if true and 0 if false ...,val1,val2->...,result cgt.un (0xFE 0x03) Compares to see whether val1 is greater than val2 (unsigned or unordered); pushes 1 if true and 0 if false ...,val1,val2->...,result ckfinite (0xC3) Checks for a finite real number; exception thrown if not a number (NaN) or (+/-)infinity; otherwise, value is left on stack ...,value->...,value clt (0xFE 0x04) Compares to see whether val1 is less than val2; pushes 1 if true and 0 if false ...,val1,val2->...,result clt.un (0xFE 0x05) Compares to see whether val1 is less than val2 (unsigned or unordered); pushes 1 if true and 0 if false ...,val1,val2->...,result conv.i (0xD3) Converts value to native int, pushing resulting native int ...,value->...,result conv.i1 (0x67) Converts value to int8, pushing int32 ...,value->...,result conv.i2 (0x68) Converts value to int16, pushing int32 ...,value->...,result conv.i4 (0x69) Converts value to int32, pushing int32 ...,value->...,result conv.i8 (0x6A) Converts value to int64, pushing int64 ...,value->...,result conv.ovf.i (0xD4) Converts value to native int, pushing resulting native int; throws exception on overflow ...,value->...,result conv.ovf.i.un (0x8A) Converts unsigned value to native int, pushing resulting native int; throws exception on overflow ...,value->...,result conv.ovf.i1 (0xB3) Converts value to int8, pushing resulting int32; throws exception on overflow ...,value->...,result conv.ovf.i1.un (0x82) Converts value to uint8, pushing resulting int32; throws exception on overflow ...,value->...,result conv.ovf.i2 (0xB5) Converts value to int16, pushing resulting int32; throws exception on overflow ...,value->...,result conv.ovf.i2.un (0x83) Converts value to uint16, pushing resulting int32; throws exception on overflow ...,value->...,result conv.ovf.i4 (0xB7) Converts value to int32, pushing resulting int32; throws exception on overflow ...,value->...,result conv.ovf.i4.un (0x84) Converts value to uint32, pushing resulting int32; throws exception on overflow ...,value->...,result conv.ovf.i8 (0xB9) Converts value to int64, pushing resulting int64; throws exception on overflow ...,value->...,result conv.ovf.i8.un (0x85) Converts value to uint64, pushing resulting int64; throws exception on overflow ...,value->...,result conv.ovf.u (0xD5) Converts value to native unsigned int, pushing resulting native unsigned int; throws exception on overflow ...,value->...,result conv.ovf.u.un (0x8B) Converts unsigned value to native unsigned int, pushing resulting native int; throws exception on overflow ...,value->...,result conv.ovf.u2 (0xB6) Converts value to uint16, pushing resulting int32; throws exception on overflow ...,value->...,result conv.ovf.u2.un (0x87) Converts unsigned value to uint16, pushing resulting int32; throws exception on overflow ...,value->...,result conv.ovf.u4 (0xB8) Converts value to uint32, pushing resulting int32; throws exception on overflow ...,value->...,result conv.ovf.u4.un (0x88) Converts unsigned value to uint32, pushing resulting int32; throws exception on overflow ...,value->...,result conv.ovf.u8 (0xBA) Converts value to uint16, pushing resulting int64; throws exception on overflow ...,value->...,result conv.ovf.u8.un (0x89) Converts unsigned value to uint64, pushing resulting int64; throws exception on overflow ...,value->...,result conv.ovf.u1 (0xB4) Converts value to uint8, pushing resulting int32; throws exception on overflow ...,value->...,result conv.ovf.u1.un (0x86) Converts unsigned value to uint8, pushing resulting int32; throws exception on overflow ...,value->...,result conv.r.un (0x76) Converts unsigned integer to floating point, pushing F ...,value->...,result conv.r4 (0x6B) Converts value to float32, pushing F ...,value->...,result conv.r8 (0x6C) Converts value to float64, pushing F ...,value->...,result conv.u (0xE0) Converts value to native unsigned int, pushing native int ...,value->...,result conv.u1 (0xD2) Converts value to uint8, pushing resulting int32 ...,value->...,result conv.u2 (0xD1) Converts value to uint16, pushing resulting int32 ...,value->...,result conv.u4 (0x6D) Converts value to uint32, pushing int32 ...,value->...,result conv.u8 (0x6E) Converts value to uint32, pushing int32 ...,value->...,result cpblk (0xFE 0x17) Copies size bytes from srcAddr to destAddr in memory ...,destAddr,srcAddr, size->... cpobj type (0x70) Copies a value type ...,destAddr, srcAddr->... div (0x5B) Divides value1 by value2, pushing result ...,val1,val2->...,result div.un (0x5C) Divides (unsigned) integer values, pushing result ...,val1,val2->...,result dup (0x25) Duplicates the value at top of stack ...,value-> ...,value,value endfilter (0xFE 0x11) Returns from the filter clause of an SEH exception ...,value->... endfinally (0xDC) Returns from a finally clause ...->... jmp method (0x27) Transfers control to the specified method ...->... initblk (0xFE 0x18) Initializes a block of size memory starting at addr with value ...,addr,value,size->... initobj classToken (0xFE 0x15) Initializes a value type ...,valueObjAddr->... isinst type (0x75)

Tests whether an object is an instance of a type or interface, pushing resulting cast if successful or null on failure.

(A null is defined by the CLI as zero (having a bit pattern of all bits zero).)

...,obj->...,result ldarg uint32 (0xFE 0x09) Pushes argument at specified index ...->...,value ldarg.0 (0x02) Pushes the first argument of a method ...->...,value ldarg.1 (0x03) Pushes the second argument of a method ...->...,value ldarg.2 (0x04) Pushes the third argument of a method ...->...,value ldarg.3 (0x05) Pushes the fourth argument of a method ...->...,value ldarg.s uint8 (0x0E) Pushes specified argument ...->...,value ldarga uint32 (0xFE 0x0A) Pushes address of argument at specified index ...->...,address ldarga.s uint8 (0x0F) Pushes address of specified argument ...->...,value ldc.i4.m1 (0x15) Pushes the literal value, -1 ...->...,-1 ldc.i4 int32 (0x20) Pushes specified 32-bit value ...-> ...,value ldc.i4.0 (0x16) Pushes the literal value, 0 ...->...,0 ldc.i4.1 (0x17) Pushes the literal value, 1 ...->...,1 ldc.i4.2 (0x18) Pushes the literal value, 2 ...->...,2 ldc.i4.3 (0x19) Pushes the literal value, 3 ...>...,3 ldc.i4.4 (0x1A) Pushes the literal value, 4 ...->...,4 ldc.i4.5 (0x1B) Pushes the literal value, 5 ...->...,5 ldc.i4.6 (0x1C) Pushes the literal value, 6 ...->...,6 ldc.i4.7 (0x1D) Pushes the literal value, 7 ...->...,7 ldc.i4.8 (0x1E) Pushes the literal value, 8 ...->...,8 ldc.i4.s int8 (0x1F) Pushes specified 8-bit value as 32-bit ...->...,value ldc.i8 int64 (0x21) Pushes specified 64-bit value ...->...,value ldc.r4 float32 (0x22) Pushes specified 32-bit floating point ...->...,value ldc.r8 float64 (0x23) Pushes specified 64-bit floating point ...->...,value ldelem.i (0x97) Pushes array element (native int) as native int ...array,index->..., value ldelem.i1 (0x90) Pushes array element (int8) as int32 ...array,index->..., value ldelem.i2 (0x92) Pushes array element (int16) as int32 ...array,index->..., value ldelem.i4 (0x94) Pushes array element (int32) as int32 ...array,index->..., value ldelem.i8 (0x96) Pushes array element (int64) as int64 ...array,index->..., value ldelem.r4 (0x98) Pushes array element (float32) as F ...array,index->..., value ldelem.r8 (0x99) Pushes array element (float64) as F ...array,index->..., value ldelem.ref (0x9A) Pushes array element (object) as object ...array,index->..., value ldelem.u1 (0x91) Pushes array element (uint8) as int32 ...array,index-> ...,value ldelem.u2 (0x93) Pushes array element (uint16) as int32 ...array,index-> ...,value ldelem.u4 (0x95) Pushes array element (uint32) as int32 ...array,index-> ...,value ldelema type (0x8F) Pushes the address of an array element ...,array,index-> ...,addr ldfld field (0x7B) Pushes field of an object ...,obj->...,value ldflda field (0x7C) Pushes field address of an object ...,obj->...,addr ldftn method (0xFE 0x06) Pushes the method pointer referenced by method ...->...,ftn ldind.i (0x4D) Pushes (indirect) value of type native int as native int ...,addr->...,value ldind.i1 (0x46) Pushes (indirect) value of type int8 as int32 ...,addr->...,value ldind.i2 (0x48) Pushes (indirect) value of type int16 as int32 ...,addr->...,value ldind.i4 (0x4A) Pushes (indirect) value of type int32 as int32 ...,addr->...,value ldind.i8 (0x4C) Pushes (indirect) value of type int64 as int64 ...,addr->...,value ldind.u1 (0x47) Pushes (indirect) value of type uint8 as int32 ...,addr->...,value ldind.u2 (0x49) Pushes (indirect) value of type uint16 as int32 ...,addr->...,value ldind.u4 (0x4B) Pushes (indirect) value of type uint32 as int32 ...,addr->...,value ldind.r4 (0x4E) Pushes (indirect) value of type float32 as F ...,addr->...,value ldind.r8 (0x4F) Pushes (indirect) value of type float64 as F ...,addr->...,value ldind.ref (0x50) Pushes (indirect) object ref as o ...,addr->...,value ldlen (0x8E) Pushes the length of an array ...,array->...,length ldloc uint32 (0xFE 0x0C) Pushes local variable at specified index ...->...,value ldloc.0 (0x06) Pushes the first local variable ...->...,value ldloc.1 (0x07) Pushes the second local variable ...->...,value ldloc.2 (0x08) Pushes the third local variable ...->...,value ldloc.3 (0x09) Pushes the fourth local variable ...->...,value ldloc.s uint8 (0x11) Pushes the specified local variable ...->...,value ldloca uint32 (0xFE 0x0D) Pushes address of local variable at specified index ...->...,address ldloca.s uint8 (0x12) Pushes address of specified local variable ...->...,value ldnull (0x14) Pushes null reference ...->...,null ldobj type (0x71) Pushes value ...,addrValueObj-> ...,valueObj ldsfld field (0x7E) Pushes static field of an object ...->...,value ldsflda field (0x7F) Pushes static field address of an object ...->...,value ldstr type (0x72) Pushes a literal string ...,->..,string ldtoken token (0xD0) Loads the common language runtime representation of a metadata token ...->...,runtimeHandle ldvirtftn method (0xFE 0x07) Pushes the method pointer referenced by method ...,obj->...,ftn leave int32 (0xDD) Branches out of a protected block of code (try, filter, catch) to target (int32 offset) ...->... leave.s int8 (0xDE) Branches out of a protected block of code (try, filter, catch) to target (int8 offset) ...->... localloc (0xFE 0x0F) Allocates size (native unsigned int) bytes from the local dynamic memory pool and pushes address ...,size->...,address mkrefany type (0xC6) Pushes a typed reference on the stack ...,ptr->...,typedRef mul (0x5A) Multiplies two values, pushing result ...,val1,val2->...,result mul.ovf (0xD8) Multiplies signed integer values, pushing result; throws exception on overflow ...,val1,val2->...,result mul.ovf.un (0xD9) Multiplies unsigned signed integer values, pushing result; throws exception on overflow ...,val1,val2->...,result neg (0x65) Negates the value on the stack ...,value->...,result newarr elementType (0x8D) Creates a one-dimensional array of elementType ...,numberOfElements->...,array newobj method (0x73) Creates a new object (calling its ctor) ...,arg1...argn->...,obj not (0x66) Computes bitwise complement of value on stack, pushing result ...,value->...,result nop (0x00) Null operation used only to fill in space if bytecodes are patched ...->... or (0x60) Bitwise OR of two integral values, pushing an integral value ...val1,val2->...,result pop (0x26) Pops top element of stack ...,value->... refanytype (0xFE 0x1D) Pushes the type token out of typed reference ...,typedRef->...,type refanyval type (0xC2) Loads the address out of a typed reference ...,typedRef->...,addr rem (0x5D) Computes remainder of dividing value1 by value2 ...,val1,val2->...,result rem.un (0x5E) Computes remainder of dividing value1 by value2 (both unsigned) ...,val1,val2->...,result ret (0x2A) Returns control from current method to caller

...,retVal->

(Not always pushed onto the stack for the ret instruction.)

...,retVal

(Not always pushed onto the stack for the ret instruction.)

rethrow (0xFE 0x1A) Only valid within a catch block; this instruction rethrows the current exception back up the call stack ...->... shl (0x62) Shift-left operation in which signed integer value and number of decimal places to shift are on stack ...,value,shiftAmount-> ...,result shr (0x63) Shift-right operation in which signed integer value and number of decimal places to shift are on stack ...,value,shiftAmount-> ...,result shr.un (0x64) Shift-right operation in which unsigned integer value and number of decimal places to shift are on stack ...,value,shiftAmount-> ...,result sizeof valueType (0xFE 0x1C) Pushes the size (in bytes) of the specified valueType ...->...,size starg uint32 (0xFE 0x0B) Stores a value to argument at specified index ...,value->... starg.s uint8 (0x10) Pops value to specified method argument ...,value ->... stelem.i (0x9B) Overwrites array element at index with value on the stack ...,array,index,value-> ... stelem.i1 (0x9C) Overwrites array element at index with int8 value on the stack ...,array,index,value-> ... stelem.i2 (0x9D) Overwrites array element at index with int16 value on the stack ...,array,index,value-> ... stelem.i4 (0x9E) Overwrites array element at index with int32 value on the stack ...,array,index,value-> ... stelem.i8 (0x9F) Overwrites array element at index with int64 value on the stack ...,array,index,value-> ... stelem.r4 (0xA0) Overwrites array element at index with float32 value on the stack ...,array,index,value-> ... stelem.r8 (0xA1) Overwrites array element at index with float64 value on the stack ...,array,index,value-> ... stelem.ref (0xA2) Overwrites array element at index with reference value on the stack ...array,index,value-> ... stfld field (0x7D) Stores into a field of an object ...,obj,value->... stind.ref (0x51) Stores object reference value into addr ...,addr,value->... stind.i (0xDF) Stores native int value into addr ...,addr,value->... stind.i1 (0x52) Stores int8 value at addr ...,addr,value->... stind.i2 (0x53) Stores int16 value at addr ...,addr,value->... stind.i4 (0x54) Stores int32 value at addr ...,addr,value->... stind.i8 (0x55) Stores int64 value at addr ...,addr,value->... stind.r4 (0x56) Stores float32 value at addr ...,addr,value->... stind.r8 (0x57) Stores float64 value at addr ...,addr,value->... stloc uint32 (0xFE 0x0E) Pops value to local variable at specified index ...,value->... stloc.0 (0x0A) Pops value to first local variable ...,value ->... stloc.1 (0x0B) Pops value to second local variable ...,value ->... stloc.2 (0x0C) Pops value to third local variable ...,value ->... stloc.3 (0x0D) Pops value to fourth local variable ...,value ->... stloc.s uint8 (0x13) Pops value to specified local variable ..,value ->... stobj type (0x81) Copies valObj into addr ...,addr,valObj->... stsfld field (0x80) Stores a static field ...,value->... sub (0x59) Subtracts value2 from value1, pushing result ...,val1,val2->...,result sub.ovf (0xDA) Subtracts integer values, pushing result; throws exception on overflow ...,val1,val2->...,result sub.ovf.un (0xDB) Subtracts unsigned integer values, pushing result; throws exception on overflow ...,val1,val2->...,result switch uint32 (N) + N(int32) (0x45) Implements a jump table in which first argument (uint32) is number of targets (specified as offsets); remaining arguments (int32) are the target offsets ...,value->... tail (0xFE 0x14) This prefix indicates termination of current method via subsequent call, calli, or callvirt instructions ...->... throw (0x7A) Throws an exception of type o ...,object->... unaligned uint8 (0xFE 0x12) Specifies that addr shouldn't be aligned ...,addr->...,addr unbox type (0x79) Converts boxed value type to raw form ...,obj->..., valueTypePtr volatile (0xFE 0x13) This prefix specifies that the pointer reference (on the stack) is volatile ...,addr->...,addr xor (0x61) Bitwise exclusive OR of two integral values, pushing an integral value ...val1,val2->...,result

作者:蒋金楠
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
14天前
|
存储 弹性计算 人工智能
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
2025年9月24日,阿里云弹性计算团队多位产品、技术专家及服务器团队技术专家共同在【2025云栖大会】现场带来了《通用计算产品发布与行业实践》的专场论坛,本论坛聚焦弹性计算多款通用算力产品发布。同时,ECS云服务器安全能力、资源售卖模式、计算AI助手等用户体验关键环节也宣布升级,让用云更简单、更智能。海尔三翼鸟云服务负责人刘建锋先生作为特邀嘉宾,莅临现场分享了关于阿里云ECS g9i推动AIoT平台的场景落地实践。
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
|
6天前
|
云安全 人工智能 安全
Dify平台集成阿里云AI安全护栏,构建AI Runtime安全防线
阿里云 AI 安全护栏加入Dify平台,打造可信赖的 AI
|
9天前
|
人工智能 运维 Java
Spring AI Alibaba Admin 开源!以数据为中心的 Agent 开发平台
Spring AI Alibaba Admin 正式发布!一站式实现 Prompt 管理、动态热更新、评测集构建、自动化评估与全链路可观测,助力企业高效构建可信赖的 AI Agent 应用。开源共建,现已上线!
848 25
|
8天前
|
机器学习/深度学习 人工智能 搜索推荐
万字长文深度解析最新Deep Research技术:前沿架构、核心技术与未来展望
近期发生了什么自 2025 年 2 月 OpenAI 正式发布Deep Research以来,深度研究/深度搜索(Deep Research / Deep Search)正在成为信息检索与知识工作的全新范式:系统以多步推理驱动大规模联网检索、跨源证据。
583 46
|
2天前
|
监控 BI 数据库
打工人救星!来看看这两家企业如何用Quick BI让业务更高效
Quick BI专业版监控告警助力企业高效运作,通过灵活配置规则与多渠道推送,让数据异常早发现、快响应,推动业务敏捷决策与持续增长。
打工人救星!来看看这两家企业如何用Quick BI让业务更高效
|
8天前
|
人工智能 Java Nacos
基于 Spring AI Alibaba + Nacos 的分布式 Multi-Agent 构建指南
本文将针对 Spring AI Alibaba + Nacos 的分布式多智能体构建方案展开介绍,同时结合 Demo 说明快速开发方法与实际效果。
565 42