SBE(simple-binary-encoding)-Demo

  1. 第一步:先配置一个sbe的xml(trade.sbe.xml)

    <?xml version="1.0" encoding="UTF-8"?>

    <sbe:messageSchema xmlns:sbe="http://fixprotocol.io/2016/sbe"
    package="com.baeldung.sbe.stub" id="1" version="0" semanticVersion="5.2"
    description="A schema represents stock market data.">
    <types>
    <composite name="messageHeader" description="消息标识符和消息根的长度">
    <type name="blockLength" primitiveType="uint16"/>
    <type name="templateId" primitiveType="uint16"/>
    <type name="schemaId" primitiveType="uint16"/>
    <type name="version" primitiveType="uint16"/>
    <type name="timestamp" primitiveType="uint64"/>
    <type name="sequenceNumber" primitiveType="uint64"/>
    <type name="messageType" primitiveType="uint8"/>
    <type name="messageLength" primitiveType="uint32"/>
    <type name="messageId" primitiveType="uint64"/>
    <type name="messageStatus" primitiveType="uint8"/>
    <type name="messageStatusReason" primitiveType="uint8"/>
    </composite>
    <enum name="Market" encodingType="uint8">
    <validValue name="NYSE" description="纽约证券交易所">0</validValue>
    <validValue name="NASDAQ" description="全国证券交易商协会自动报价系统">1</validValue>
    </enum>
    <type name="Symbol" primitiveType="char" length="4" characterEncoding="ASCII" description="股票代码"/>
    <composite name="Decimal" >
    <type name="mantissa" primitiveType="int64" description="尾数" minValue="0"/>
    <type name="exponent" primitiveType="int8" description="指数" minValue="-128" maxValue="127"/>
    </composite>
    <enum name="Currency" encodingType="uint8" description="" >
    <validValue name="USD" description="美元">0</validValue>
    <validValue name="CNY" description="人民币">1</validValue>
    </enum>

    复制代码
     <composite name="Quote" description="报价代表股票在市场上的价格。">
       <ref name="market" type="Market"/>
       <ref name="symbol" type="Symbol"/>
       <ref name="price" type="Decimal"/>
       <ref name="currency" type="Currency"/>
     </composite>
    </types>

    <sbe:message name="TradeData" id="1" description="代表报价和交易量">
    <field name="quote" id="1" type="Quote" description="报价"/>
    <field name="tradeVolume" id="2" type="uint16" description="交易量"/>
    </sbe:message>
    </sbe:messageSchema>

第二步:配置pom, 用maven命令去生成java文件

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.baeldung</groupId>
    <artifactId>sbe-demo</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <name>SBE Demo</name>
    <description>Simple Binary Encoding Demo based on Baeldung tutorial</description>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        
        <!-- 使用最新稳定版本 -->
        <sbe.version>1.32.0</sbe.version>
        <agrona.version>1.23.1</agrona.version>
        <junit.version>5.10.1</junit.version>
    </properties>

    <dependencies>
        <!-- Agrona - 高性能数据结构库,SBE 依赖 -->
        <dependency>
            <groupId>org.agrona</groupId>
            <artifactId>agrona</artifactId>
            <version>${agrona.version}</version>
        </dependency>

        <!-- SBE Tool - 用于代码生成 -->
        <dependency>
            <groupId>uk.co.real-logic</groupId>
            <artifactId>sbe-tool</artifactId>
            <version>${sbe.version}</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>
        <!-- 测试依赖 -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <repositories>
        <!-- 阿里云 Maven 镜像(国内推荐,速度快) -->
        <repository>
            <id>aliyunmaven</id>
            <name>Aliyun Maven Repository</name>
            <url>https://maven.aliyun.com/repository/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <!-- Maven 中央仓库(备用) -->
        <repository>
            <id>central</id>
            <name>Maven Central Repository</name>
            <url>https://repo1.maven.org/maven2</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <pluginRepositories>
        <!-- Maven 插件仓库 -->
        <pluginRepository>
            <id>central</id>
            <name>Maven Central Repository</name>
            <url>https://repo1.maven.org/maven2</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

    <build>
        <plugins>
            <!-- Java 编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>

            <!-- SBE 代码生成插件 -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>3.2.0</version>
                <dependencies>
                    <dependency>
                        <groupId>uk.co.real-logic</groupId>
                        <artifactId>sbe-tool</artifactId>
                        <version>${sbe.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>org.agrona</groupId>
                        <artifactId>agrona</artifactId>
                        <version>${agrona.version}</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <id>generate-sbe-codecs-car</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>java</goal>
                        </goals>
                        <configuration>
                            <mainClass>uk.co.real_logic.sbe.SbeTool</mainClass>
                            <includePluginDependencies>true</includePluginDependencies>
                            <classpathScope>compile</classpathScope>
                            <arguments>
                                <argument>${project.basedir}/src/main/resources/sbe/car.sbe.xml</argument>
                            </arguments>
                            <systemProperties>
                                <systemProperty>
                                    <key>sbe.target.language</key>
                                    <value>java</value>
                                </systemProperty>
                                <systemProperty>
                                    <key>sbe.output.dir</key>
                                    <value>${project.build.directory}/generated-sources/sbe</value>
                                </systemProperty>
                                <systemProperty>
                                    <key>sbe.java.package</key>
                                    <value>com.baeldung.sbe.generated</value>
                                </systemProperty>
                                <systemProperty>
                                    <key>sbe.validation.stop.on.error</key>
                                    <value>true</value>
                                </systemProperty>
                            </systemProperties>
                        </configuration>
                    </execution>
                    <execution>
                        <id>generate-sbe-codecs-trade</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>java</goal>
                        </goals>
                        <configuration>
                            <mainClass>uk.co.real_logic.sbe.SbeTool</mainClass>
                            <includePluginDependencies>true</includePluginDependencies>
                            <classpathScope>compile</classpathScope>
                            <arguments>
                                <argument>${project.basedir}/src/main/resources/sbe/trade.sbe.xml</argument>
                            </arguments>
                            <systemProperties>
                                <systemProperty>
                                    <key>sbe.target.language</key>
                                    <value>java</value>
                                </systemProperty>
                                <systemProperty>
                                    <key>sbe.output.dir</key>
                                    <value>${project.build.directory}/generated-sources/sbe</value>
                                </systemProperty>
                                <systemProperty>
                                    <key>sbe.validation.stop.on.error</key>
                                    <value>true</value>
                                </systemProperty>
                            </systemProperties>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <!-- 添加生成的源码目录 -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <version>3.4.0</version>
                <executions>
                    <execution>
                        <id>add-sbe-generated-sources</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>${project.build.directory}/generated-sources/sbe</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <!-- 测试插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.2.2</version>
            </plugin>
        </plugins>
    </build>
</project>
  1. 第三部, 执行maven install命令生成代码后, 尝试一下数据的编码和解码

    package com.baeldung.sbe;

    import java.math.BigDecimal;
    import java.nio.ByteBuffer;

    import org.agrona.concurrent.UnsafeBuffer;

    import com.baeldung.sbe.stub.Currency;
    import com.baeldung.sbe.stub.Market;
    import com.baeldung.sbe.stub.MessageHeaderDecoder;
    import com.baeldung.sbe.stub.MessageHeaderEncoder;
    import com.baeldung.sbe.stub.QuoteEncoder;
    import com.baeldung.sbe.stub.TradeDataDecoder;
    import com.baeldung.sbe.stub.TradeDataEncoder;

    public class TradeDemo {

    public static void main(String[] args) {
    System.out.println("=== TradeDemo ===");

    复制代码
     /**
      * =========第一步 创建数据========
      */
     // 绑定数据
     MarketData marketData = new MarketData(2, 128.99, Market.NYSE, Currency.USD, "IBM");
     // 转换价格
     BigDecimal priceDecimal = BigDecimal.valueOf(marketData.getPrice());
     int priceMantissa = priceDecimal.scaleByPowerOfTen(priceDecimal.scale()).intValue();
     int priceExponent = priceDecimal.scale() * -1;
    
     /**
      * =========第二步 set数据========
      */
     // 创建缓冲区
     UnsafeBuffer buffer = new UnsafeBuffer(ByteBuffer.allocateDirect(128));
    
     // 创建消息头编码器
     MessageHeaderEncoder headerEncoder = new MessageHeaderEncoder();
    
     // 创建数据编码器
     TradeDataEncoder tradeDataEncoder = new TradeDataEncoder();
     // 绑定数据
     TradeDataEncoder dataEncoder = tradeDataEncoder.wrapAndApplyHeader(buffer, 0, headerEncoder);
     dataEncoder.tradeVolume(marketData.getTradeVolume());
     dataEncoder.quote()
         .market(marketData.getMarket())
         .symbol(marketData.getSymbol())
         .currency(marketData.getCurrency())
         .price()
           .mantissa(priceMantissa)
           .exponent((byte) priceExponent);
    
     /**
      * =========第三步 get数据========
      */
     // 创建消息头解码器
     MessageHeaderDecoder headerDecoder = new MessageHeaderDecoder();
     TradeDataDecoder dataDecoder = new TradeDataDecoder();
     // 解码数据
     System.out.println("============打印结果===========");
     dataDecoder.wrapAndApplyHeader(buffer, 0, headerDecoder);
     System.out.println("quoteId: " + dataDecoder.quoteId());
     System.out.println("quote: " + dataDecoder.quote());
     System.out.println("tradeVolume: " + dataDecoder.tradeVolume());
     System.out.println("price: " + dataDecoder.quote().price());
     System.out.println("priceMantissa: " + dataDecoder.quote().price().mantissa());
     System.out.println("priceExponent: " + dataDecoder.quote().price().exponent());
     System.out.println("market: " + dataDecoder.quote().market());
     System.out.println("symbol: " + dataDecoder.quote().symbol());
     System.out.println("currency: " + dataDecoder.quote().currency());
     System.out.println("encodedLength: " + dataDecoder.encodedLength());

    }
    }

参考:https:// baeldung. xiaocaicai. com/ java-sbe/

=============第二部分 ============

细心的朋友会发现, maven中还配置了个car.sbe.xml

那我们继续实现car.sbe.xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<sbe:messageSchema xmlns:sbe="http://www.fixprotocol.org/ns/sbe"
                   package="com.baeldung.sbe.generated"
                   id="1"
                   version="1"
                   semanticVersion="1.0"
                   description="Car example for SBE"
                   byteOrder="littleEndian">

    <types>
        <composite name="messageHeader" description="Message identifiers and length of message root">
            <type name="blockLength" primitiveType="uint16"/>
            <type name="templateId" primitiveType="uint16"/>
            <type name="schemaId" primitiveType="uint16"/>
            <type name="version" primitiveType="uint16"/>
        </composite>

        <enum name="BooleanType" encodingType="uint8">
            <validValue name="FALSE">0</validValue>
            <validValue name="TRUE">1</validValue>
        </enum>

        <enum name="Model" encodingType="uint8">
            <validValue name="A">0</validValue>
            <validValue name="B">1</validValue>
            <validValue name="C">2</validValue>
        </enum>

        <enum name="Code" encodingType="uint8">
            <validValue name="A">65</validValue>
            <validValue name="B">66</validValue>
            <validValue name="C">67</validValue>
        </enum>

        <composite name="groupSizeEncoding">
            <type name="blockLength" primitiveType="uint16"/>
            <type name="numInGroup" primitiveType="uint8"/>
        </composite>

        <composite name="varStringEncoding">
            <type name="length" primitiveType="uint8"/>
            <type name="varData" primitiveType="uint8" length="0" characterEncoding="UTF-8"/>
        </composite>

        <composite name="Engine">
            <type name="capacity" primitiveType="uint16"/>
            <type name="numCylinders" primitiveType="uint8"/>
            <type name="maxRpm" primitiveType="uint16" presence="optional"/>
            <type name="manufacturerCode" primitiveType="char" length="3"/>
            <type name="efficiency" primitiveType="uint8"/>
            <type name="boosterEnabled" primitiveType="uint8"/>
        </composite>
    </types>

    <sbe:message name="Car" id="1" description="Represents a car">
        <field name="serialNumber" id="1" type="uint64" description="Serial number"/>
        <field name="modelYear" id="2" type="uint16" description="Model year"/>
        <field name="available" id="3" type="BooleanType" description="Is available"/>
        <field name="code" id="4" type="Code" description="Code"/>
        <field name="someNumbers" id="5" type="uint8" description="Some numbers" presence="optional"/>
        <field name="vehicleCode" id="6" type="char" length="6" description="Vehicle code"/>
        <field name="extras" id="7" type="uint8" description="Extras"/>
        <field name="discountedModel" id="8" type="Model" description="Discounted model"/>
        <field name="engine" id="9" type="Engine" description="Engine information"/>
        <group name="fuelFigures" id="10" dimensionType="groupSizeEncoding">
            <field name="speed" id="11" type="uint16" description="Speed"/>
            <field name="mpg" id="12" type="float" description="Miles per gallon"/>
            <data name="usageDescription" id="13" type="varStringEncoding"/>
        </group>
        <group name="performanceFigures" id="14" dimensionType="groupSizeEncoding">
            <field name="octaneRating" id="15" type="uint8" description="Octane rating"/>
            <group name="acceleration" id="16" dimensionType="groupSizeEncoding">
                <field name="mph" id="17" type="uint16" description="Miles per hour"/>
                <field name="seconds" id="18" type="float" description="Seconds"/>
            </group>
        </group>
        <data name="manufacturer" id="19" type="varStringEncoding"/>
        <data name="model" id="20" type="varStringEncoding"/>
        <data name="activationCode" id="21" type="varStringEncoding"/>
    </sbe:message>

</sbe:messageSchema>

然后新增一个测试类:

SbeBasicTest

复制代码
package com.baeldung.sbe;

import com.baeldung.sbe.generated.*;
import org.agrona.concurrent.UnsafeBuffer;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

import java.nio.ByteBuffer;

/**
 * SBE 基本用法测试
 * 演示如何使用生成的 Encoder 和 Decoder
 */
public class SbeBasicTest {

    @Test
    public void testBasicEncodeDecode() {
        // 1. 创建缓冲区
        UnsafeBuffer buffer = new UnsafeBuffer(ByteBuffer.allocateDirect(4096));
        
        // 2. 编码消息头
        MessageHeaderEncoder headerEncoder = new MessageHeaderEncoder();
        CarEncoder carEncoder = new CarEncoder();
        
        int offset = 0;
        headerEncoder.wrap(buffer, offset)
            .blockLength(carEncoder.sbeBlockLength())
            .templateId(carEncoder.sbeTemplateId())
            .schemaId(carEncoder.sbeSchemaId())
            .version(carEncoder.sbeSchemaVersion());
        
        offset += headerEncoder.encodedLength();
        
        // 3. 编码 Car 消息 - 基本字段
        carEncoder.wrap(buffer, offset)
            .serialNumber(12345L)
            .modelYear(2024)
            .available(BooleanType.TRUE)
            .code(Code.C)
            .extras((byte) 0)
            .discountedModel(Model.A);
        
        // 4. 编码 vehicleCode (固定长度字符数组,需要逐个设置)
        String vehicleCode = "123456";
        for (int i = 0; i < Math.min(6, vehicleCode.length()); i++) {
            carEncoder.vehicleCode((byte) vehicleCode.charAt(i));
        }
        
        // 5. 编码 Engine
        EngineEncoder engineEncoder = carEncoder.engine();
        engineEncoder.capacity(4200)
            .numCylinders((short) 8)
            .maxRpm(5000)
            .manufacturerCode("123")
            .efficiency((short) 35)
            .boosterEnabled((byte) 1);
        
        // 6. 编码 fuelFigures 组
        CarEncoder.FuelFiguresEncoder fuelFigures = carEncoder.fuelFiguresCount(2);
        fuelFigures.next()
            .speed(30)
            .mpg(35.9f)
            .usageDescription("Urban cycle");
        fuelFigures.next()
            .speed(55)
            .mpg(49.0f)
            .usageDescription("Highway cycle");
        
        // 7. 编码 performanceFigures 组(包含嵌套组)
        CarEncoder.PerformanceFiguresEncoder performanceFigures = carEncoder.performanceFiguresCount(1);
        performanceFigures.next()
            .octaneRating((short) 95);
        performanceFigures.accelerationCount(2)
            .next().mph(30).seconds(4.0f)
            .next().mph(60).seconds(7.5f);
        
        // 8. 编码变长字符串
        carEncoder.manufacturer("Tesla");
        carEncoder.model("Model S");
        carEncoder.activationCode("ACT123");
        
        // 9. 获取编码后的数据
        int totalLength = carEncoder.encodedLength() + offset;
        byte[] encodedData = new byte[totalLength];
        buffer.getBytes(0, encodedData, 0, totalLength);
        
        // 验证编码成功
        assertNotNull(encodedData);
        assertTrue(encodedData.length > 0);
        System.out.println("编码成功,数据大小: " + encodedData.length + " bytes");
        
        // ========== 解码部分 ==========
        
        // 10. 创建解码缓冲区
        UnsafeBuffer decodeBuffer = new UnsafeBuffer(encodedData);
        
        // 11. 解码消息头
        MessageHeaderDecoder headerDecoder = new MessageHeaderDecoder();
        headerDecoder.wrap(decodeBuffer, 0);
        
        int decodeOffset = headerDecoder.encodedLength();
        
        // 12. 验证消息头
        assertEquals(carEncoder.sbeTemplateId(), headerDecoder.templateId());
        
        // 13. 解码 Car 消息
        CarDecoder carDecoder = new CarDecoder();
        carDecoder.wrap(decodeBuffer, decodeOffset, 
                       headerDecoder.blockLength(),
                       headerDecoder.version());
        
        // 14. 读取基本字段
        assertEquals(12345L, carDecoder.serialNumber());
        assertEquals(2024, carDecoder.modelYear());
        assertEquals(BooleanType.TRUE, carDecoder.available());
        assertEquals(Code.C, carDecoder.code());
        assertEquals(Model.A, carDecoder.discountedModel());
        
        // 15. 读取 Engine
        EngineDecoder engine = carDecoder.engine();
        assertEquals(4200, engine.capacity());
        assertEquals(8, engine.numCylinders());
        assertEquals(5000, engine.maxRpm());
        assertEquals("123", engine.manufacturerCode());
        assertEquals(35, engine.efficiency());
        assertEquals(1, engine.boosterEnabled());
        
        // 16. 读取 fuelFigures 组
        CarDecoder.FuelFiguresDecoder fuelFiguresDecoder = carDecoder.fuelFigures();
        assertTrue(fuelFiguresDecoder.hasNext());
        fuelFiguresDecoder.next();
        assertEquals(30, fuelFiguresDecoder.speed());
        assertEquals(35.9f, fuelFiguresDecoder.mpg(), 0.01f);
        assertEquals("Urban cycle", fuelFiguresDecoder.usageDescription());
        
        assertTrue(fuelFiguresDecoder.hasNext());
        fuelFiguresDecoder.next();
        assertEquals(55, fuelFiguresDecoder.speed());
        assertEquals(49.0f, fuelFiguresDecoder.mpg(), 0.01f);
        assertEquals("Highway cycle", fuelFiguresDecoder.usageDescription());
        
        // 17. 读取 performanceFigures 组
        CarDecoder.PerformanceFiguresDecoder perfFiguresDecoder = carDecoder.performanceFigures();
        assertTrue(perfFiguresDecoder.hasNext());
        perfFiguresDecoder.next();
        assertEquals(95, perfFiguresDecoder.octaneRating());
        
        // 读取嵌套的 acceleration 组
        CarDecoder.PerformanceFiguresDecoder.AccelerationDecoder acceleration = 
            perfFiguresDecoder.acceleration();
        assertTrue(acceleration.hasNext());
        acceleration.next();
        assertEquals(30, acceleration.mph());
        assertEquals(4.0f, acceleration.seconds(), 0.01f);
        
        assertTrue(acceleration.hasNext());
        acceleration.next();
        assertEquals(60, acceleration.mph());
        assertEquals(7.5f, acceleration.seconds(), 0.01f);
        
        // 18. 读取变长字符串
        assertEquals("Tesla", carDecoder.manufacturer());
        assertEquals("Model S", carDecoder.model());
        assertEquals("ACT123", carDecoder.activationCode());
        
        System.out.println("解码成功!");
        System.out.println("序列号: " + carDecoder.serialNumber());
        System.out.println("年份: " + carDecoder.modelYear());
        System.out.println("制造商: " + carDecoder.manufacturer());
        System.out.println("型号: " + carDecoder.model());
    }

    @Test
    public void testSimpleEncodeDecode() {
        // 简化版本:只编码基本字段和字符串
        UnsafeBuffer buffer = new UnsafeBuffer(ByteBuffer.allocateDirect(1024));
        
        MessageHeaderEncoder headerEncoder = new MessageHeaderEncoder();
        CarEncoder carEncoder = new CarEncoder();
        
        int offset = 0;
        headerEncoder.wrap(buffer, offset)
            .blockLength(carEncoder.sbeBlockLength())
            .templateId(carEncoder.sbeTemplateId())
            .schemaId(carEncoder.sbeSchemaId())
            .version(carEncoder.sbeSchemaVersion());
        
        offset += headerEncoder.encodedLength();
        
        // 只编码基本字段
        carEncoder.wrap(buffer, offset)
            .serialNumber(99999L)
            .modelYear(2023)
            .available(BooleanType.FALSE)
            .code(Code.A)
            .extras((short) 0)
            .discountedModel(Model.B);
        
        // 设置 vehicleCode
        String vCode = "ABC123";
        for (int i = 0; i < Math.min(6, vCode.length()); i++) {
            carEncoder.vehicleCode((byte) vCode.charAt(i));
        }
        
        // 编码 Engine(简化)
        carEncoder.engine()
            .capacity(3000)
            .numCylinders((short) 6)
            .manufacturerCode("XYZ")
            .efficiency((short) 30)
            .boosterEnabled((byte) 0);
        
        // 编码字符串
        carEncoder.manufacturer("BMW");
        carEncoder.model("X5");
        carEncoder.activationCode("CODE999");
        
        // 提取数据
        int totalLength = carEncoder.encodedLength() + offset;
        byte[] data = new byte[totalLength];
        buffer.getBytes(0, data, 0, totalLength);
        
        // 解码
        UnsafeBuffer decodeBuffer = new UnsafeBuffer(data);
        MessageHeaderDecoder headerDecoder = new MessageHeaderDecoder();
        headerDecoder.wrap(decodeBuffer, 0);
        
        CarDecoder decoder = new CarDecoder();
        decoder.wrap(decodeBuffer, headerDecoder.encodedLength(),
                    headerDecoder.blockLength(), headerDecoder.version());
        
        // 验证
        assertEquals(99999L, decoder.serialNumber());
        assertEquals(2023, decoder.modelYear());
        assertEquals(BooleanType.FALSE, decoder.available());
        assertEquals(Code.A, decoder.code());
        assertEquals("BMW", decoder.manufacturer());
        assertEquals("X5", decoder.model());
        assertEquals("CODE999", decoder.activationCode());
        
        EngineDecoder engine = decoder.engine();
        assertEquals(3000, engine.capacity());
        assertEquals(6, engine.numCylinders());
        assertEquals("XYZ", engine.manufacturerCode());
    }

    @Test
    public void testRoundTrip() {
        // 往返测试:编码后解码,验证数据一致性
        UnsafeBuffer buffer = new UnsafeBuffer(ByteBuffer.allocateDirect(2048));
        
        // 编码
        MessageHeaderEncoder headerEncoder = new MessageHeaderEncoder();
        CarEncoder carEncoder = new CarEncoder();
        
        int offset = 0;
        headerEncoder.wrap(buffer, offset)
            .blockLength(carEncoder.sbeBlockLength())
            .templateId(carEncoder.sbeTemplateId())
            .schemaId(carEncoder.sbeSchemaId())
            .version(carEncoder.sbeSchemaVersion());
        offset += headerEncoder.encodedLength();
        
        long serialNumber = 88888L;
        int modelYear = 2024;
        String manufacturer = "Mercedes";
        String model = "S-Class";
        
        carEncoder.wrap(buffer, offset)
            .serialNumber(serialNumber)
            .modelYear(modelYear)
            .available(BooleanType.TRUE)
            .code(Code.B)
            .extras((short) 0)
            .discountedModel(Model.C);
        
        String vCode = "MERC01";
        for (int i = 0; i < 6; i++) {
            carEncoder.vehicleCode((byte) vCode.charAt(i));
        }
        
        carEncoder.engine()
            .capacity(4500)
            .numCylinders((short) 12)
            .maxRpm(6000)
            .manufacturerCode("MBZ")
            .efficiency((short) 40)
            .boosterEnabled((byte) 1);
        
        carEncoder.manufacturer(manufacturer);
        carEncoder.model(model);
        carEncoder.activationCode("MERC123");
        
        int totalLength = carEncoder.encodedLength() + offset;
        byte[] encoded = new byte[totalLength];
        buffer.getBytes(0, encoded, 0, totalLength);
        
        // 解码
        UnsafeBuffer decodeBuffer = new UnsafeBuffer(encoded);
        MessageHeaderDecoder headerDecoder = new MessageHeaderDecoder();
        headerDecoder.wrap(decodeBuffer, 0);
        
        CarDecoder decoder = new CarDecoder();
        decoder.wrap(decodeBuffer, headerDecoder.encodedLength(),
                    headerDecoder.blockLength(), headerDecoder.version());
        
        // 验证往返一致性
        assertEquals(serialNumber, decoder.serialNumber());
        assertEquals(modelYear, decoder.modelYear());
        assertEquals(manufacturer, decoder.manufacturer());
        assertEquals(model, decoder.model());
        
        EngineDecoder engine = decoder.engine();
        assertEquals(4500, engine.capacity());
        assertEquals(12, engine.numCylinders());
        assertEquals(6000, engine.maxRpm());
        assertEquals("MBZ", engine.manufacturerCode());
        
        System.out.println("往返测试通过!");
    }
}

最后贴一下目录结构:

相关推荐
-凌凌漆-2 小时前
【Java】java中throws与try catch区别
java
代码的奴隶(艾伦·耶格尔)2 小时前
Nginx
java·服务器·nginx
zzcufo2 小时前
多邻国第五阶段第13部分
java·开发语言·数据库
漂洋过海的鱼儿2 小时前
设计模式——EIT构型(三)
java·网络·设计模式
曹轲恒2 小时前
@PropertySource、@ImportResource、@Bean
java·spring boot·mybatis
Anastasiozzzz3 小时前
Redis的键过期是如何删除的?【面试高频】
java·数据库·redis·缓存·面试
老神在在0013 小时前
Token身份验证完整流程
java·前端·后端·学习·java-ee
王九思4 小时前
Java 内存分析工具 MAT
java·开发语言·安全
浅水壁虎4 小时前
任务调度——XXLJOB2(调度中心)
java·spring boot·spring