欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Olingo分析和實(shí)踐之EDM 輔助序列化器詳解(最佳實(shí)踐)

 更新時(shí)間:2025年07月21日 14:43:46   作者:breaksoftware  
EDM輔助序列化器是Apache Olingo OData框架中無(wú)需完整EDM模型的智能序列化工具,通過(guò)運(yùn)行時(shí)類型推斷實(shí)現(xiàn)靈活數(shù)據(jù)轉(zhuǎn)換,適用于動(dòng)態(tài)數(shù)據(jù)源、快速原型開(kāi)發(fā)及多格式數(shù)據(jù)集成,兼顧開(kāi)發(fā)效率與協(xié)議兼容性,本文給大家介紹Olingo分析和實(shí)踐之EDM 輔助序列化器,感興趣的朋友一起看看

概念與定義

什么是 EDM 輔助序列化器?

EDM 輔助序列化器(EdmAssistedSerializer)是 Apache Olingo OData 框架中的一種特殊序列化器,專門(mén)設(shè)計(jì)用于在缺少完整 EDM(實(shí)體數(shù)據(jù)模型)信息的情況下進(jìn)行數(shù)據(jù)序列化。

核心概念

  • EDM(Entity Data Model): OData 服務(wù)的元數(shù)據(jù)模型,定義了實(shí)體類型、屬性、關(guān)系等
  • 輔助(Assisted): 表示該序列化器可以在沒(méi)有完整 EDM 信息的情況下工作
  • 智能推斷: 能夠根據(jù)數(shù)據(jù)本身推斷出類型和結(jié)構(gòu)信息

設(shè)計(jì)目標(biāo)

核心特點(diǎn)

1. EDM 信息可選

// 可以在沒(méi)有 EDM 信息的情況下工作
EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer(ContentType.APPLICATION_JSON);
// 如果有 EDM 信息,會(huì)利用它進(jìn)行驗(yàn)證和優(yōu)化
SerializerResult result = serializer.entityCollection(
    metadata,           // 可選的 ServiceMetadata
    null,              // 可選的 EdmEntityType
    entityCollection,  // 必需的數(shù)據(jù)
    options           // 序列化選項(xiàng)
);

2. 智能類型推斷

// 自動(dòng)推斷數(shù)據(jù)類型
Entity entity = new Entity();
entity.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "John"));
entity.addProperty(new Property(null, "Age", ValueType.PRIMITIVE, 25));
entity.addProperty(new Property(null, "Salary", ValueType.PRIMITIVE, 50000.50));
entity.addProperty(new Property(null, "IsActive", ValueType.PRIMITIVE, true));
// 序列化器會(huì)自動(dòng)推斷:
// Name -> String
// Age -> Integer  
// Salary -> Double
// IsActive -> Boolean

3. 版本感知

// 支持不同 OData 版本
List<String> versions = Arrays.asList("4.01", "4.0");
EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer(ContentType.APPLICATION_JSON, versions);
// 根據(jù)版本自動(dòng)選擇合適的常量和行為
// v4.0: 使用 Constantsv00
// v4.01+: 使用 Constantsv01

4. 元數(shù)據(jù)級(jí)別控制

// 不同的元數(shù)據(jù)級(jí)別
EdmAssistedSerializer noMetadata = odata.createEdmAssistedSerializer(ContentType.JSON_NO_METADATA);
EdmAssistedSerializer minimalMetadata = odata.createEdmAssistedSerializer(ContentType.JSON);
EdmAssistedSerializer fullMetadata = odata.createEdmAssistedSerializer(ContentType.JSON_FULL_METADATA);

與標(biāo)準(zhǔn)序列化器的區(qū)別

對(duì)比表格

特性標(biāo)準(zhǔn)序列化器 (ODataSerializer)EDM 輔助序列化器 (EdmAssistedSerializer)
EDM 依賴必須有完整的 EDM 信息EDM 信息可選,可以沒(méi)有
類型安全編譯時(shí)類型檢查運(yùn)行時(shí)類型推斷
性能更高(有完整類型信息)略低(需要推斷類型)
靈活性較低,結(jié)構(gòu)固定更高,支持動(dòng)態(tài)結(jié)構(gòu)
使用場(chǎng)景完整的 OData 服務(wù)輕量級(jí)或動(dòng)態(tài)數(shù)據(jù)序列化
支持格式JSON, XML僅 JSON
開(kāi)發(fā)速度需要先定義 EDM可以直接開(kāi)始開(kāi)發(fā)
適用階段生產(chǎn)環(huán)境開(kāi)發(fā)、原型、集成階段

使用決策流程

工作原理

序列化流程

類型推斷機(jī)制

// 類型推斷示例
public class TypeInferenceExample {
    public void demonstrateTypeInference() {
        Entity entity = new Entity();
        // 字符串類型推斷
        entity.addProperty(new Property(null, "stringProp", ValueType.PRIMITIVE, "Hello"));
        // 輸出: "stringProp": "Hello"
        // 數(shù)值類型推斷
        entity.addProperty(new Property(null, "intProp", ValueType.PRIMITIVE, 42));
        // 輸出: "intProp@odata.type": "#Int32", "intProp": 42
        entity.addProperty(new Property(null, "doubleProp", ValueType.PRIMITIVE, 3.14));
        // 輸出: "doubleProp@odata.type": "#Double", "doubleProp": 3.14
        // 布爾類型推斷
        entity.addProperty(new Property(null, "boolProp", ValueType.PRIMITIVE, true));
        // 輸出: "boolProp": true
        // 日期類型推斷
        entity.addProperty(new Property(null, "dateProp", ValueType.PRIMITIVE, 
            Calendar.getInstance()));
        // 輸出: "dateProp@odata.type": "#DateTimeOffset", "dateProp": "2025-01-15T10:30:00Z"
        // 復(fù)雜類型推斷
        ComplexValue address = new ComplexValue();
        address.getValue().add(new Property(null, "Street", ValueType.PRIMITIVE, "Main St"));
        address.getValue().add(new Property(null, "City", ValueType.PRIMITIVE, "Seattle"));
        entity.addProperty(new Property(null, "Address", ValueType.COMPLEX, address));
        // 輸出: "Address": { "Street": "Main St", "City": "Seattle" }
    }
}

詳細(xì)API分析

核心接口

public interface EdmAssistedSerializer {
    /**
     * 序列化實(shí)體集合
     * @param metadata 服務(wù)元數(shù)據(jù)(可選)
     * @param referencedEntityType 引用的實(shí)體類型(可選)
     * @param entityCollection 要序列化的實(shí)體集合
     * @param options 序列化選項(xiàng)
     * @return 序列化結(jié)果
     */
    SerializerResult entityCollection(
        ServiceMetadata metadata, 
        EdmEntityType referencedEntityType,
        AbstractEntityCollection entityCollection, 
        EdmAssistedSerializerOptions options
    ) throws SerializerException;
}

實(shí)現(xiàn)類分析

public class EdmAssistedJsonSerializer implements EdmAssistedSerializer {
    // 關(guān)鍵字段
    private final boolean isIEEE754Compatible;    // IEEE754 兼容性
    private final boolean isODataMetadataNone;    // 無(wú)元數(shù)據(jù)模式
    private final boolean isODataMetadataFull;    // 完整元數(shù)據(jù)模式
    private final IConstants constants;           // 版本常量
    // 構(gòu)造函數(shù)
    public EdmAssistedJsonSerializer(final ContentType contentType) {
        this.isIEEE754Compatible = ContentTypeHelper.isODataIEEE754Compatible(contentType);
        this.isODataMetadataNone = ContentTypeHelper.isODataMetadataNone(contentType);
        this.isODataMetadataFull = ContentTypeHelper.isODataMetadataFull(contentType);
        this.constants = new Constantsv00();
    }
    // 版本感知構(gòu)造函數(shù)
    public EdmAssistedJsonSerializer(final ContentType contentType, final IConstants constants) {
        this.isIEEE754Compatible = ContentTypeHelper.isODataIEEE754Compatible(contentType);
        this.isODataMetadataNone = ContentTypeHelper.isODataMetadataNone(contentType);
        this.isODataMetadataFull = ContentTypeHelper.isODataMetadataFull(contentType);
        this.constants = constants;
    }
}

序列化選項(xiàng)

public class EdmAssistedSerializerOptions {
    private ContextURL contextURL;
    public static Builder with() {
        return new Builder();
    }
    public static final class Builder {
        private final EdmAssistedSerializerOptions options;
        private Builder() {
            options = new EdmAssistedSerializerOptions();
        }
        public Builder contextURL(final ContextURL contextURL) {
            options.contextURL = contextURL;
            return this;
        }
        public EdmAssistedSerializerOptions build() {
            return options;
        }
    }
}

使用場(chǎng)景

1. 快速原型開(kāi)發(fā)

場(chǎng)景描述: 在項(xiàng)目初期,需要快速驗(yàn)證 OData 接口設(shè)計(jì),但還沒(méi)有完整的 EDM 模型。

@RestController
@RequestMapping("/api/prototype")
public class PrototypeController {
    private final OData odata = OData.newInstance();
    @GetMapping("/users")
    public ResponseEntity<String> getUsers() throws SerializerException, IOException {
        // 快速創(chuàng)建測(cè)試數(shù)據(jù),無(wú)需預(yù)定義 EDM
        EntityCollection users = new EntityCollection();
        // 用戶1
        Entity user1 = new Entity();
        user1.addProperty(new Property(null, "Id", ValueType.PRIMITIVE, 1));
        user1.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Alice"));
        user1.addProperty(new Property(null, "Email", ValueType.PRIMITIVE, "alice@example.com"));
        user1.addProperty(new Property(null, "Age", ValueType.PRIMITIVE, 28));
        user1.addProperty(new Property(null, "IsActive", ValueType.PRIMITIVE, true));
        users.getEntities().add(user1);
        // 用戶2
        Entity user2 = new Entity();
        user2.addProperty(new Property(null, "Id", ValueType.PRIMITIVE, 2));
        user2.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Bob"));
        user2.addProperty(new Property(null, "Email", ValueType.PRIMITIVE, "bob@example.com"));
        user2.addProperty(new Property(null, "Age", ValueType.PRIMITIVE, 35));
        user2.addProperty(new Property(null, "IsActive", ValueType.PRIMITIVE, false));
        users.getEntities().add(user2);
        // 使用 EDM 輔助序列化器
        EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer(
            ContentType.JSON_FULL_METADATA);
        ContextURL contextURL = ContextURL.with()
            .entitySet("Users")
            .selectList("Id,Name,Email,Age,IsActive")
            .build();
        EdmAssistedSerializerOptions options = EdmAssistedSerializerOptions.with()
            .contextURL(contextURL)
            .build();
        SerializerResult result = serializer.entityCollection(
            null, null, users, options);
        return ResponseEntity.ok()
            .contentType(MediaType.parseMediaType("application/json"))
            .body(IOUtils.toString(result.getContent(), StandardCharsets.UTF_8));
    }
}

輸出結(jié)果:

{
  "@odata.context": "$metadata#Users(Id,Name,Email,Age,IsActive)",
  "value": [
    {
      "@odata.id": null,
      "Id@odata.type": "#Int32",
      "Id": 1,
      "Name": "Alice",
      "Email": "alice@example.com",
      "Age@odata.type": "#Int32",
      "Age": 28,
      "IsActive": true
    },
    {
      "@odata.id": null,
      "Id@odata.type": "#Int32",
      "Id": 2,
      "Name": "Bob",
      "Email": "bob@example.com",
      "Age@odata.type": "#Int32",
      "Age": 35,
      "IsActive": false
    }
  ]
}

2. 動(dòng)態(tài)數(shù)據(jù)源集成

場(chǎng)景描述: 從外部數(shù)據(jù)庫(kù)或 API 動(dòng)態(tài)獲取數(shù)據(jù),數(shù)據(jù)結(jié)構(gòu)可能會(huì)變化。

@Service
public class DynamicDataService {
    private final OData odata = OData.newInstance();
    private final JdbcTemplate jdbcTemplate;
    public DynamicDataService(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    /**
     * 動(dòng)態(tài)查詢?nèi)我獗砀駭?shù)據(jù)并序列化為 OData 格式
     */
    public String queryTableAsOData(String tableName, List<String> columns) 
            throws SerializerException, IOException {
        // 構(gòu)建動(dòng)態(tài) SQL
        String sql = "SELECT " + String.join(", ", columns) + " FROM " + tableName;
        // 執(zhí)行查詢
        List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql);
        // 轉(zhuǎn)換為 OData 實(shí)體集合
        EntityCollection entities = new EntityCollection();
        for (Map<String, Object> row : rows) {
            Entity entity = new Entity();
            for (Map.Entry<String, Object> entry : row.entrySet()) {
                String columnName = entry.getKey();
                Object value = entry.getValue();
                // 動(dòng)態(tài)確定值類型
                ValueType valueType = determineValueType(value);
                entity.addProperty(new Property(null, columnName, valueType, value));
            }
            entities.getEntities().add(entity);
        }
        // 使用 EDM 輔助序列化器
        EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer(
            ContentType.APPLICATION_JSON);
        ContextURL contextURL = ContextURL.with()
            .entitySet(tableName)
            .selectList(String.join(",", columns))
            .build();
        EdmAssistedSerializerOptions options = EdmAssistedSerializerOptions.with()
            .contextURL(contextURL)
            .build();
        SerializerResult result = serializer.entityCollection(
            null, null, entities, options);
        return IOUtils.toString(result.getContent(), StandardCharsets.UTF_8);
    }
    private ValueType determineValueType(Object value) {
        if (value == null) return ValueType.PRIMITIVE;
        if (value instanceof String) return ValueType.PRIMITIVE;
        if (value instanceof Number) return ValueType.PRIMITIVE;
        if (value instanceof Boolean) return ValueType.PRIMITIVE;
        if (value instanceof Date || value instanceof Calendar) return ValueType.PRIMITIVE;
        if (value instanceof Map) return ValueType.COMPLEX;
        if (value instanceof Collection) return ValueType.COLLECTION_PRIMITIVE;
        return ValueType.PRIMITIVE;
    }
}

3. 數(shù)據(jù)轉(zhuǎn)換管道

場(chǎng)景描述: 在數(shù)據(jù)集成管道中,需要將不同格式的數(shù)據(jù)統(tǒng)一轉(zhuǎn)換為 OData 格式。

@Component
public class DataTransformationPipeline {
    private final OData odata = OData.newInstance();
    /**
     * 將 CSV 數(shù)據(jù)轉(zhuǎn)換為 OData JSON 格式
     */
    public String transformCsvToOData(String csvContent, String entitySetName) 
            throws SerializerException, IOException {
        String[] lines = csvContent.split("\n");
        if (lines.length < 2) {
            throw new IllegalArgumentException("CSV must have at least header and one data row");
        }
        // 解析表頭
        String[] headers = lines[0].split(",");
        EntityCollection entities = new EntityCollection();
        // 解析數(shù)據(jù)行
        for (int i = 1; i < lines.length; i++) {
            String[] values = lines[i].split(",");
            Entity entity = new Entity();
            for (int j = 0; j < headers.length && j < values.length; j++) {
                String header = headers[j].trim();
                String value = values[j].trim();
                // 嘗試推斷類型并轉(zhuǎn)換
                Object typedValue = parseValue(value);
                entity.addProperty(new Property(null, header, ValueType.PRIMITIVE, typedValue));
            }
            entities.getEntities().add(entity);
        }
        return serializeToOData(entities, entitySetName);
    }
    /**
     * 將 JSON 數(shù)組轉(zhuǎn)換為 OData 格式
     */
    public String transformJsonArrayToOData(String jsonArray, String entitySetName) 
            throws SerializerException, IOException {
        ObjectMapper mapper = new ObjectMapper();
        try {
            List<Map<String, Object>> dataList = mapper.readValue(jsonArray, 
                new TypeReference<List<Map<String, Object>>>() {});
            EntityCollection entities = new EntityCollection();
            for (Map<String, Object> dataMap : dataList) {
                Entity entity = new Entity();
                for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
                    String key = entry.getKey();
                    Object value = entry.getValue();
                    ValueType valueType = determineValueType(value);
                    entity.addProperty(new Property(null, key, valueType, value));
                }
                entities.getEntities().add(entity);
            }
            return serializeToOData(entities, entitySetName);
        } catch (Exception e) {
            throw new RuntimeException("Failed to parse JSON array", e);
        }
    }
    private String serializeToOData(EntityCollection entities, String entitySetName) 
            throws SerializerException, IOException {
        // 支持多版本
        List<String> versions = Arrays.asList("4.01", "4.0");
        EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer(
            ContentType.APPLICATION_JSON, versions);
        ContextURL contextURL = ContextURL.with()
            .entitySet(entitySetName)
            .build();
        EdmAssistedSerializerOptions options = EdmAssistedSerializerOptions.with()
            .contextURL(contextURL)
            .build();
        SerializerResult result = serializer.entityCollection(
            null, null, entities, options);
        return IOUtils.toString(result.getContent(), StandardCharsets.UTF_8);
    }
    private Object parseValue(String value) {
        // 嘗試解析為不同類型
        if (value.isEmpty() || "null".equalsIgnoreCase(value)) {
            return null;
        }
        // 布爾值
        if ("true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value)) {
            return Boolean.parseBoolean(value);
        }
        // 整數(shù)
        try {
            return Integer.parseInt(value);
        } catch (NumberFormatException e) {
            // 不是整數(shù),繼續(xù)嘗試其他類型
        }
        // 浮點(diǎn)數(shù)
        try {
            return Double.parseDouble(value);
        } catch (NumberFormatException e) {
            // 不是浮點(diǎn)數(shù),繼續(xù)嘗試其他類型
        }
        // 日期(簡(jiǎn)單格式)
        try {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
            return dateFormat.parse(value);
        } catch (ParseException e) {
            // 不是日期格式
        }
        // 默認(rèn)作為字符串
        return value;
    }
    private ValueType determineValueType(Object value) {
        if (value == null) return ValueType.PRIMITIVE;
        if (value instanceof String) return ValueType.PRIMITIVE;
        if (value instanceof Number) return ValueType.PRIMITIVE;
        if (value instanceof Boolean) return ValueType.PRIMITIVE;
        if (value instanceof Date) return ValueType.PRIMITIVE;
        if (value instanceof Map) return ValueType.COMPLEX;
        if (value instanceof List) return ValueType.COLLECTION_PRIMITIVE;
        return ValueType.PRIMITIVE;
    }
}

4. 微服務(wù)數(shù)據(jù)聚合

場(chǎng)景描述: 從多個(gè)微服務(wù)聚合數(shù)據(jù),各服務(wù)的數(shù)據(jù)格式可能不同。

@RestController
@RequestMapping("/api/aggregation")
public class DataAggregationController {
    private final OData odata = OData.newInstance();
    @Autowired
    private UserService userService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private ProductService productService;
    /**
     * 聚合用戶、訂單和產(chǎn)品數(shù)據(jù)
     */
    @GetMapping("/dashboard")
    public ResponseEntity<String> getDashboardData() throws SerializerException, IOException {
        EntityCollection dashboardData = new EntityCollection();
        // 聚合用戶數(shù)據(jù)
        List<User> users = userService.getActiveUsers();
        for (User user : users) {
            Entity userEntity = new Entity();
            userEntity.addProperty(new Property(null, "Type", ValueType.PRIMITIVE, "User"));
            userEntity.addProperty(new Property(null, "Id", ValueType.PRIMITIVE, user.getId()));
            userEntity.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, user.getName()));
            userEntity.addProperty(new Property(null, "Email", ValueType.PRIMITIVE, user.getEmail()));
            userEntity.addProperty(new Property(null, "LastLogin", ValueType.PRIMITIVE, user.getLastLogin()));
            // 動(dòng)態(tài)添加用戶統(tǒng)計(jì)信息
            Map<String, Object> stats = userService.getUserStats(user.getId());
            for (Map.Entry<String, Object> stat : stats.entrySet()) {
                userEntity.addProperty(new Property(null, 
                    "Stats_" + stat.getKey(), ValueType.PRIMITIVE, stat.getValue()));
            }
            dashboardData.getEntities().add(userEntity);
        }
        // 聚合訂單數(shù)據(jù)
        List<Order> recentOrders = orderService.getRecentOrders(30);
        for (Order order : recentOrders) {
            Entity orderEntity = new Entity();
            orderEntity.addProperty(new Property(null, "Type", ValueType.PRIMITIVE, "Order"));
            orderEntity.addProperty(new Property(null, "Id", ValueType.PRIMITIVE, order.getId()));
            orderEntity.addProperty(new Property(null, "UserId", ValueType.PRIMITIVE, order.getUserId()));
            orderEntity.addProperty(new Property(null, "Amount", ValueType.PRIMITIVE, order.getAmount()));
            orderEntity.addProperty(new Property(null, "Status", ValueType.PRIMITIVE, order.getStatus()));
            orderEntity.addProperty(new Property(null, "CreatedAt", ValueType.PRIMITIVE, order.getCreatedAt()));
            dashboardData.getEntities().add(orderEntity);
        }
        // 聚合產(chǎn)品數(shù)據(jù)(動(dòng)態(tài)屬性)
        List<Map<String, Object>> productData = productService.getProductAnalytics();
        for (Map<String, Object> product : productData) {
            Entity productEntity = new Entity();
            productEntity.addProperty(new Property(null, "Type", ValueType.PRIMITIVE, "Product"));
            // 動(dòng)態(tài)添加所有產(chǎn)品屬性
            for (Map.Entry<String, Object> entry : product.entrySet()) {
                ValueType valueType = determineValueType(entry.getValue());
                productEntity.addProperty(new Property(null, 
                    entry.getKey(), valueType, entry.getValue()));
            }
            dashboardData.getEntities().add(productEntity);
        }
        // 使用 EDM 輔助序列化器序列化混合數(shù)據(jù)
        EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer(
            ContentType.APPLICATION_JSON);
        ContextURL contextURL = ContextURL.with()
            .entitySet("DashboardData")
            .build();
        EdmAssistedSerializerOptions options = EdmAssistedSerializerOptions.with()
            .contextURL(contextURL)
            .build();
        SerializerResult result = serializer.entityCollection(
            null, null, dashboardData, options);
        String jsonOutput = IOUtils.toString(result.getContent(), StandardCharsets.UTF_8);
        return ResponseEntity.ok()
            .contentType(MediaType.APPLICATION_JSON)
            .body(jsonOutput);
    }
    private ValueType determineValueType(Object value) {
        if (value == null) return ValueType.PRIMITIVE;
        if (value instanceof String) return ValueType.PRIMITIVE;
        if (value instanceof Number) return ValueType.PRIMITIVE;
        if (value instanceof Boolean) return ValueType.PRIMITIVE;
        if (value instanceof Date || value instanceof Calendar) return ValueType.PRIMITIVE;
        if (value instanceof Map) return ValueType.COMPLEX;
        if (value instanceof Collection) return ValueType.COLLECTION_PRIMITIVE;
        return ValueType.PRIMITIVE;
    }
}

代碼案例

案例1: 基礎(chǔ)使用

public class BasicUsageExample {
    public void basicExample() throws SerializerException, IOException {
        OData odata = OData.newInstance();
        // 1. 創(chuàng)建 EDM 輔助序列化器
        EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer(
            ContentType.APPLICATION_JSON);
        // 2. 創(chuàng)建數(shù)據(jù)
        Entity person = new Entity();
        person.addProperty(new Property(null, "FirstName", ValueType.PRIMITIVE, "John"));
        person.addProperty(new Property(null, "LastName", ValueType.PRIMITIVE, "Doe"));
        person.addProperty(new Property(null, "Age", ValueType.PRIMITIVE, 30));
        EntityCollection people = new EntityCollection();
        people.getEntities().add(person);
        // 3. 序列化
        SerializerResult result = serializer.entityCollection(
            null, null, people, null);
        // 4. 獲取結(jié)果
        String json = IOUtils.toString(result.getContent(), StandardCharsets.UTF_8);
        System.out.println(json);
    }
}

案例2: 復(fù)雜類型處理

public class ComplexTypeExample {
    public void complexTypeExample() throws SerializerException, IOException {
        OData odata = OData.newInstance();
        EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer(
            ContentType.JSON_FULL_METADATA);
        // 創(chuàng)建包含復(fù)雜類型的實(shí)體
        Entity employee = new Entity();
        employee.addProperty(new Property(null, "EmployeeId", ValueType.PRIMITIVE, 1001));
        employee.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Alice Johnson"));
        // 創(chuàng)建地址復(fù)雜類型
        ComplexValue address = new ComplexValue();
        address.getValue().add(new Property(null, "Street", ValueType.PRIMITIVE, "123 Main St"));
        address.getValue().add(new Property(null, "City", ValueType.PRIMITIVE, "Seattle"));
        address.getValue().add(new Property(null, "State", ValueType.PRIMITIVE, "WA"));
        address.getValue().add(new Property(null, "ZipCode", ValueType.PRIMITIVE, "98101"));
        employee.addProperty(new Property(null, "Address", ValueType.COMPLEX, address));
        // 創(chuàng)建聯(lián)系方式復(fù)雜類型
        ComplexValue contact = new ComplexValue();
        contact.getValue().add(new Property(null, "Email", ValueType.PRIMITIVE, "alice@company.com"));
        contact.getValue().add(new Property(null, "Phone", ValueType.PRIMITIVE, "+1-555-0123"));
        employee.addProperty(new Property(null, "Contact", ValueType.COMPLEX, contact));
        // 創(chuàng)建技能集合
        List<String> skills = Arrays.asList("Java", "Spring", "OData", "SQL");
        employee.addProperty(new Property(null, "Skills", ValueType.COLLECTION_PRIMITIVE, skills));
        EntityCollection employees = new EntityCollection();
        employees.getEntities().add(employee);
        // 設(shè)置上下文 URL
        ContextURL contextURL = ContextURL.with()
            .entitySet("Employees")
            .build();
        EdmAssistedSerializerOptions options = EdmAssistedSerializerOptions.with()
            .contextURL(contextURL)
            .build();
        SerializerResult result = serializer.entityCollection(
            null, null, employees, options);
        String json = IOUtils.toString(result.getContent(), StandardCharsets.UTF_8);
        System.out.println("Complex Type Example Output:");
        System.out.println(json);
    }
}

輸出結(jié)果:

{
  "@odata.context": "$metadata#Employees",
  "value": [
    {
      "@odata.id": null,
      "EmployeeId@odata.type": "#Int32",
      "EmployeeId": 1001,
      "Name": "Alice Johnson",
      "Address": {
        "Street": "123 Main St",
        "City": "Seattle",
        "State": "WA",
        "ZipCode": "98101"
      },
      "Contact": {
        "Email": "alice@company.com",
        "Phone": "+1-555-0123"
      },
      "Skills@odata.type": "#Collection(String)",
      "Skills": ["Java", "Spring", "OData", "SQL"]
    }
  ]
}

案例3: 版本差異處理

public class VersionHandlingExample {
    public void compareVersions() throws SerializerException, IOException {
        OData odata = OData.newInstance();
        // 創(chuàng)建測(cè)試數(shù)據(jù)
        Entity product = new Entity();
        product.addProperty(new Property(null, "ProductId", ValueType.PRIMITIVE, 1));
        product.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Laptop"));
        product.addProperty(new Property(null, "Price", ValueType.PRIMITIVE, 999.99));
        product.addProperty(new Property(null, "InStock", ValueType.PRIMITIVE, true));
        EntityCollection products = new EntityCollection();
        products.getEntities().add(product);
        // v4.0 序列化
        List<String> versionsV40 = Arrays.asList("4.0");
        EdmAssistedSerializer serializerV40 = odata.createEdmAssistedSerializer(
            ContentType.APPLICATION_JSON, versionsV40);
        SerializerResult resultV40 = serializerV40.entityCollection(
            null, null, products, null);
        String jsonV40 = IOUtils.toString(resultV40.getContent(), StandardCharsets.UTF_8);
        System.out.println("OData v4.0 Output:");
        System.out.println(jsonV40);
        // v4.01 序列化
        List<String> versionsV401 = Arrays.asList("4.01");
        EdmAssistedSerializer serializerV401 = odata.createEdmAssistedSerializer(
            ContentType.APPLICATION_JSON, versionsV401);
        SerializerResult resultV401 = serializerV401.entityCollection(
            null, null, products, null);
        String jsonV401 = IOUtils.toString(resultV401.getContent(), StandardCharsets.UTF_8);
        System.out.println("\nOData v4.01 Output:");
        System.out.println(jsonV401);
    }
}

案例4: 元數(shù)據(jù)級(jí)別對(duì)比

public class MetadataLevelExample {
    public void compareMetadataLevels() throws SerializerException, IOException {
        OData odata = OData.newInstance();
        // 創(chuàng)建測(cè)試數(shù)據(jù)
        Entity order = new Entity();
        order.addProperty(new Property(null, "OrderId", ValueType.PRIMITIVE, 12345));
        order.addProperty(new Property(null, "CustomerName", ValueType.PRIMITIVE, "John Smith"));
        order.addProperty(new Property(null, "OrderDate", ValueType.PRIMITIVE, 
            Calendar.getInstance()));
        order.addProperty(new Property(null, "TotalAmount", ValueType.PRIMITIVE, 129.99));
        EntityCollection orders = new EntityCollection();
        orders.getEntities().add(order);
        ContextURL contextURL = ContextURL.with()
            .entitySet("Orders")
            .build();
        EdmAssistedSerializerOptions options = EdmAssistedSerializerOptions.with()
            .contextURL(contextURL)
            .build();
        // 1. 無(wú)元數(shù)據(jù)
        EdmAssistedSerializer noMetadata = odata.createEdmAssistedSerializer(
            ContentType.JSON_NO_METADATA);
        SerializerResult resultNoMeta = noMetadata.entityCollection(
            null, null, orders, options);
        String jsonNoMeta = IOUtils.toString(resultNoMeta.getContent(), StandardCharsets.UTF_8);
        System.out.println("No Metadata:");
        System.out.println(jsonNoMeta);
        // 2. 最小元數(shù)據(jù)
        EdmAssistedSerializer minimalMetadata = odata.createEdmAssistedSerializer(
            ContentType.JSON);
        SerializerResult resultMinimal = minimalMetadata.entityCollection(
            null, null, orders, options);
        String jsonMinimal = IOUtils.toString(resultMinimal.getContent(), StandardCharsets.UTF_8);
        System.out.println("\nMinimal Metadata:");
        System.out.println(jsonMinimal);
        // 3. 完整元數(shù)據(jù)
        EdmAssistedSerializer fullMetadata = odata.createEdmAssistedSerializer(
            ContentType.JSON_FULL_METADATA);
        SerializerResult resultFull = fullMetadata.entityCollection(
            null, null, orders, options);
        String jsonFull = IOUtils.toString(resultFull.getContent(), StandardCharsets.UTF_8);
        System.out.println("\nFull Metadata:");
        System.out.println(jsonFull);
    }
}

案例5: 錯(cuò)誤處理和邊界情況

public class ErrorHandlingExample {
    public void demonstrateErrorHandling() {
        OData odata = OData.newInstance();
        // 1. 不支持的內(nèi)容類型
        try {
            EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer(
                ContentType.APPLICATION_XML); // 不支持 XML
        } catch (SerializerException e) {
            System.out.println("Expected error - Unsupported format: " + e.getMessage());
        }
        // 2. 空數(shù)據(jù)處理
        try {
            EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer(
                ContentType.APPLICATION_JSON);
            EntityCollection emptyCollection = new EntityCollection();
            SerializerResult result = serializer.entityCollection(
                null, null, emptyCollection, null);
            String json = IOUtils.toString(result.getContent(), StandardCharsets.UTF_8);
            System.out.println("Empty collection result: " + json);
        } catch (Exception e) {
            System.out.println("Error handling empty collection: " + e.getMessage());
        }
        // 3. 空值屬性處理
        try {
            EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer(
                ContentType.APPLICATION_JSON);
            Entity entityWithNulls = new Entity();
            entityWithNulls.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Test"));
            entityWithNulls.addProperty(new Property(null, "NullValue", ValueType.PRIMITIVE, null));
            entityWithNulls.addProperty(new Property(null, "EmptyString", ValueType.PRIMITIVE, ""));
            EntityCollection collection = new EntityCollection();
            collection.getEntities().add(entityWithNulls);
            SerializerResult result = serializer.entityCollection(
                null, null, collection, null);
            String json = IOUtils.toString(result.getContent(), StandardCharsets.UTF_8);
            System.out.println("Null values handling: " + json);
        } catch (Exception e) {
            System.out.println("Error handling null values: " + e.getMessage());
        }
    }
}

最佳實(shí)踐

1. 選擇合適的元數(shù)據(jù)級(jí)別

public class MetadataBestPractices {
    // 生產(chǎn)環(huán)境 - 使用最小元數(shù)據(jù)以減少帶寬
    public EdmAssistedSerializer createProductionSerializer() throws SerializerException {
        OData odata = OData.newInstance();
        return odata.createEdmAssistedSerializer(ContentType.JSON);
    }
    // 開(kāi)發(fā)和調(diào)試 - 使用完整元數(shù)據(jù)便于調(diào)試
    public EdmAssistedSerializer createDevelopmentSerializer() throws SerializerException {
        OData odata = OData.newInstance();
        return odata.createEdmAssistedSerializer(ContentType.JSON_FULL_METADATA);
    }
    // 性能敏感場(chǎng)景 - 使用無(wú)元數(shù)據(jù)
    public EdmAssistedSerializer createPerformanceSerializer() throws SerializerException {
        OData odata = OData.newInstance();
        return odata.createEdmAssistedSerializer(ContentType.JSON_NO_METADATA);
    }
}

2. 版本管理策略

public class VersionManagementBestPractices {
    private final OData odata = OData.newInstance();
    // 支持多版本的通用方法
    public EdmAssistedSerializer createVersionAwareSerializer(
            ContentType contentType, String clientVersion) throws SerializerException {
        List<String> supportedVersions = determineSupportedVersions(clientVersion);
        return odata.createEdmAssistedSerializer(contentType, supportedVersions);
    }
    private List<String> determineSupportedVersions(String clientVersion) {
        List<String> versions = new ArrayList<>();
        if (clientVersion != null && clientVersion.startsWith("4.01")) {
            versions.add("4.01");
            versions.add("4.0");  // 向后兼容
        } else {
            versions.add("4.0");
        }
        return versions;
    }
}

3. 性能優(yōu)化

public class PerformanceOptimization {
    // 序列化器復(fù)用
    private final Map<String, EdmAssistedSerializer> serializerCache = new ConcurrentHashMap<>();
    private final OData odata = OData.newInstance();
    public EdmAssistedSerializer getCachedSerializer(ContentType contentType, List<String> versions) 
            throws SerializerException {
        String key = contentType.toContentTypeString() + "_" + 
            (versions != null ? String.join(",", versions) : "default");
        return serializerCache.computeIfAbsent(key, k -> {
            try {
                return versions != null && !versions.isEmpty() 
                    ? odata.createEdmAssistedSerializer(contentType, versions)
                    : odata.createEdmAssistedSerializer(contentType);
            } catch (SerializerException e) {
                throw new RuntimeException("Failed to create serializer", e);
            }
        });
    }
    // 批量序列化優(yōu)化
    public String serializeLargeDataset(List<Entity> entities, String entitySetName) 
            throws SerializerException, IOException {
        EdmAssistedSerializer serializer = getCachedSerializer(
            ContentType.JSON_NO_METADATA, null);
        // 分批處理大數(shù)據(jù)集
        int batchSize = 1000;
        StringBuilder result = new StringBuilder();
        result.append("{\"value\":[");
        for (int i = 0; i < entities.size(); i += batchSize) {
            int endIndex = Math.min(i + batchSize, entities.size());
            List<Entity> batch = entities.subList(i, endIndex);
            EntityCollection batchCollection = new EntityCollection();
            batchCollection.getEntities().addAll(batch);
            SerializerResult batchResult = serializer.entityCollection(
                null, null, batchCollection, null);
            String batchJson = IOUtils.toString(batchResult.getContent(), StandardCharsets.UTF_8);
            // 提取值數(shù)組部分
            if (i > 0) result.append(",");
            // 處理批次JSON...
        }
        result.append("]}");
        return result.toString();
    }
}

4. 類型安全

public class TypeSafetyBestPractices {
    // 使用類型安全的屬性創(chuàng)建器
    public static class PropertyBuilder {
        public static Property createStringProperty(String name, String value) {
            return new Property(null, name, ValueType.PRIMITIVE, value);
        }
        public static Property createIntProperty(String name, Integer value) {
            return new Property(null, name, ValueType.PRIMITIVE, value);
        }
        public static Property createDoubleProperty(String name, Double value) {
            return new Property(null, name, ValueType.PRIMITIVE, value);
        }
        public static Property createBooleanProperty(String name, Boolean value) {
            return new Property(null, name, ValueType.PRIMITIVE, value);
        }
        public static Property createDateProperty(String name, Date value) {
            return new Property(null, name, ValueType.PRIMITIVE, value);
        }
        public static Property createComplexProperty(String name, ComplexValue value) {
            return new Property(null, name, ValueType.COMPLEX, value);
        }
        public static Property createCollectionProperty(String name, Collection<?> value) {
            return new Property(null, name, ValueType.COLLECTION_PRIMITIVE, value);
        }
    }
    // 使用示例
    public Entity createTypeSafeEntity() {
        Entity entity = new Entity();
        entity.addProperty(PropertyBuilder.createStringProperty("Name", "John Doe"));
        entity.addProperty(PropertyBuilder.createIntProperty("Age", 30));
        entity.addProperty(PropertyBuilder.createDoubleProperty("Salary", 75000.0));
        entity.addProperty(PropertyBuilder.createBooleanProperty("IsActive", true));
        entity.addProperty(PropertyBuilder.createDateProperty("HireDate", new Date()));
        return entity;
    }
}

常見(jiàn)問(wèn)題

Q1: EDM 輔助序列化器與標(biāo)準(zhǔn)序列化器的性能差異有多大?

A: 在大多數(shù)場(chǎng)景下,性能差異在 10-20% 之間。主要開(kāi)銷(xiāo)來(lái)自運(yùn)行時(shí)類型推斷。

public class PerformanceComparison {
    @Test
    public void comparePerformance() throws Exception {
        // 準(zhǔn)備測(cè)試數(shù)據(jù)
        EntityCollection testData = createLargeTestDataset(10000);
        // 測(cè)試標(biāo)準(zhǔn)序列化器
        long startTime = System.currentTimeMillis();
        ODataSerializer standardSerializer = odata.createSerializer(ContentType.APPLICATION_JSON);
        // ... 序列化邏輯
        long standardTime = System.currentTimeMillis() - startTime;
        // 測(cè)試 EDM 輔助序列化器
        startTime = System.currentTimeMillis();
        EdmAssistedSerializer assistedSerializer = odata.createEdmAssistedSerializer(
            ContentType.APPLICATION_JSON);
        // ... 序列化邏輯
        long assistedTime = System.currentTimeMillis() - startTime;
        System.out.println("Standard serializer: " + standardTime + "ms");
        System.out.println("Assisted serializer: " + assistedTime + "ms");
        System.out.println("Performance ratio: " + ((double)assistedTime / standardTime));
    }
}

Q2: 如何處理循環(huán)引用?

A: EDM 輔助序列化器不會(huì)自動(dòng)處理循環(huán)引用,需要在創(chuàng)建數(shù)據(jù)時(shí)避免。

public class CircularReferenceHandling {
    public Entity createEntityWithoutCircularRef(User user, Set<String> processedIds) {
        if (processedIds.contains(user.getId())) {
            // 創(chuàng)建引用實(shí)體,避免循環(huán)
            Entity refEntity = new Entity();
            refEntity.addProperty(PropertyBuilder.createStringProperty("Id", user.getId()));
            refEntity.addProperty(PropertyBuilder.createStringProperty("Name", user.getName()));
            return refEntity;
        }
        processedIds.add(user.getId());
        Entity entity = new Entity();
        entity.addProperty(PropertyBuilder.createStringProperty("Id", user.getId()));
        entity.addProperty(PropertyBuilder.createStringProperty("Name", user.getName()));
        // 安全地添加關(guān)聯(lián)實(shí)體
        if (user.getManager() != null) {
            Entity managerEntity = createEntityWithoutCircularRef(user.getManager(), processedIds);
            entity.addProperty(new Property(null, "Manager", ValueType.COMPLEX, 
                convertEntityToComplexValue(managerEntity)));
        }
        return entity;
    }
}

Q3: 如何優(yōu)化大數(shù)據(jù)集的序列化?

A: 使用流式處理和分批序列化:

public class LargeDatasetOptimization {
    public void streamLargeDataset(Iterator<Entity> entityIterator, OutputStream outputStream) 
            throws IOException, SerializerException {
        JsonGenerator jsonGenerator = new JsonFactory().createGenerator(outputStream);
        jsonGenerator.writeStartObject();
        jsonGenerator.writeArrayFieldStart("value");
        EdmAssistedSerializer serializer = odata.createEdmAssistedSerializer(
            ContentType.JSON_NO_METADATA);
        while (entityIterator.hasNext()) {
            Entity entity = entityIterator.next();
            EntityCollection singleEntityCollection = new EntityCollection();
            singleEntityCollection.getEntities().add(entity);
            SerializerResult result = serializer.entityCollection(
                null, null, singleEntityCollection, null);
            // 直接寫(xiě)入流,避免內(nèi)存積累
            IOUtils.copy(result.getContent(), outputStream);
            if (entityIterator.hasNext()) {
                jsonGenerator.writeRaw(",");
            }
        }
        jsonGenerator.writeEndArray();
        jsonGenerator.writeEndObject();
        jsonGenerator.close();
    }
}

總結(jié)

EDM 輔助序列化器的價(jià)值

  1. 開(kāi)發(fā)效率: 無(wú)需預(yù)先定義完整的 EDM 模型,可以快速開(kāi)始開(kāi)發(fā)
  2. 靈活性: 能夠處理動(dòng)態(tài)結(jié)構(gòu)的數(shù)據(jù),適應(yīng)數(shù)據(jù)模型的變化
  3. 集成友好: 便于與外部系統(tǒng)集成,處理格式不統(tǒng)一的數(shù)據(jù)
  4. 原型開(kāi)發(fā): 適合快速原型開(kāi)發(fā)和概念驗(yàn)證

適用場(chǎng)景總結(jié)

場(chǎng)景適用性推薦理由
快速原型開(kāi)發(fā)?????無(wú)需預(yù)定義 EDM,快速驗(yàn)證想法
動(dòng)態(tài)數(shù)據(jù)源?????能夠處理結(jié)構(gòu)變化的數(shù)據(jù)
數(shù)據(jù)集成????統(tǒng)一不同格式的數(shù)據(jù)輸出
微服務(wù)聚合????整合多個(gè)服務(wù)的異構(gòu)數(shù)據(jù)
生產(chǎn)環(huán)境???性能略低,但提供更大靈活性

最終建議

  1. 開(kāi)發(fā)階段: 優(yōu)先使用 EDM 輔助序列化器,加快開(kāi)發(fā)速度
  2. 生產(chǎn)環(huán)境: 如果數(shù)據(jù)結(jié)構(gòu)穩(wěn)定,考慮遷移到標(biāo)準(zhǔn)序列化器以獲得更好性能
  3. 混合使用: 對(duì)于不同的接口,可以根據(jù)需求選擇不同的序列化器
  4. 漸進(jìn)式采用: 從 EDM 輔助序列化器開(kāi)始,逐步完善 EDM 模型

EDM 輔助序列化器是 Apache Olingo OData 框架中的一個(gè)強(qiáng)大工具,它在保持 OData 協(xié)議兼容性的同時(shí),提供了極大的開(kāi)發(fā)靈活性。通過(guò)合理使用,可以顯著提高開(kāi)發(fā)效率并簡(jiǎn)化數(shù)據(jù)集成工作。

到此這篇關(guān)于Olingo分析和實(shí)踐之EDM 輔助序列化器詳解(最佳實(shí)踐)的文章就介紹到這了,更多相關(guān)Olingo EDM 輔助序列化器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java Swing中的文本區(qū)(JTextArea)實(shí)現(xiàn)換行保存到文件的幾個(gè)方法

    Java Swing中的文本區(qū)(JTextArea)實(shí)現(xiàn)換行保存到文件的幾個(gè)方法

    這篇文章主要介紹了Java Swing中的文本區(qū)(JTextArea)實(shí)現(xiàn)換行保存到文件的幾個(gè)方法,本文給出了4種方法,需要的朋友可以參考下
    2014-10-10
  • 詳解SpringBoot如何實(shí)現(xiàn)統(tǒng)一后端返回格式

    詳解SpringBoot如何實(shí)現(xiàn)統(tǒng)一后端返回格式

    在前后端分離的項(xiàng)目中后端返回的格式一定要友好,不然會(huì)對(duì)前端的開(kāi)發(fā)人員帶來(lái)很多的工作量。那么SpringBoot如何做到統(tǒng)一的后端返回格式呢?本文將為大家詳細(xì)講講
    2022-04-04
  • java8 List<Object>去掉重復(fù)對(duì)象的幾種方法

    java8 List<Object>去掉重復(fù)對(duì)象的幾種方法

    本文主要介紹了java8 List<Object>去掉重復(fù)對(duì)象的幾種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • Junit springboot打印測(cè)試方法信息

    Junit springboot打印測(cè)試方法信息

    這篇文章主要介紹了Junit springboot打印測(cè)試方法信息,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • SpringMVC用JsonSerialize日期轉(zhuǎn)換方法

    SpringMVC用JsonSerialize日期轉(zhuǎn)換方法

    下面小編就為大家?guī)?lái)一篇SpringMVC用JsonSerialize日期轉(zhuǎn)換方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起 小編過(guò)來(lái)看看吧
    2016-11-11
  • java集合中l(wèi)ist的用法代碼示例

    java集合中l(wèi)ist的用法代碼示例

    這篇文章主要介紹了java集合中l(wèi)ist的用法代碼示例,分享了相關(guān)代碼,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • maven私服配置全過(guò)程

    maven私服配置全過(guò)程

    這篇文章主要介紹了maven私服配置全過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-06-06
  • java查找文件夾下最新生成的文件的方法

    java查找文件夾下最新生成的文件的方法

    在本篇文章中我們給大家分享了關(guān)于java怎么查找文件夾下最新生成的文件的相關(guān)方法和知識(shí)點(diǎn),有需要的朋友們參考下。
    2019-07-07
  • Eureka注冊(cè)不上或注冊(cè)后IP不對(duì)(多網(wǎng)卡的坑及解決)

    Eureka注冊(cè)不上或注冊(cè)后IP不對(duì)(多網(wǎng)卡的坑及解決)

    這篇文章主要介紹了Eureka注冊(cè)不上或注冊(cè)后IP不對(duì)(多網(wǎng)卡的坑及解決),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • Java基礎(chǔ)知識(shí)之注解、元注解

    Java基礎(chǔ)知識(shí)之注解、元注解

    ava 注解,從名字上看是注釋,解釋。但功能卻不僅僅是注釋那么簡(jiǎn)單,下面這篇文章主要給大家介紹了關(guān)于Java基礎(chǔ)知識(shí)之注解、元注解的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-01-01

最新評(píng)論