Skip to content

10.2.5 Bộ chia tách (Splitters)

Đôi khi trong một luồng tích hợp, việc chia một message thành nhiều message riêng lẻ để xử lý độc lập là điều hữu ích. Splitters, như minh họa trong hình 10.6, sẽ giúp bạn thực hiện việc chia và xử lý các message đó.

Hình 10.6

Hình 10.6 Splitters phân tách một message thành hai hoặc nhiều message riêng biệt để xử lý bởi các luồng con khác nhau.

Splitters hữu ích trong nhiều tình huống, nhưng bạn có thể sử dụng splitter trong hai trường hợp sử dụng cơ bản sau:

  • Payload của một message chứa một tập hợp các phần tử cùng loại mà bạn muốn xử lý như các payload message riêng lẻ. Ví dụ, một message mang danh sách các sản phẩm có thể được chia thành nhiều message với mỗi payload là một sản phẩm.
  • Payload của một message chứa thông tin có liên quan, nhưng có thể được chia thành hai hoặc nhiều message với các kiểu khác nhau. Ví dụ, một đơn hàng mua hàng có thể chứa thông tin giao hàng, thanh toán và chi tiết mặt hàng. Thông tin giao hàng có thể được xử lý bởi một luồng con, thanh toán bởi luồng khác và chi tiết mặt hàng bởi một luồng khác nữa. Trong trường hợp này, splitter thường được theo sau bởi một router để định tuyến các message theo kiểu payload nhằm đảm bảo dữ liệu được xử lý bởi đúng luồng con.

Khi chia payload của một message thành hai hoặc nhiều message với các kiểu khác nhau, thường thì bạn chỉ cần định nghĩa một POJO để trích xuất các phần riêng lẻ của payload đầu vào và trả về chúng dưới dạng một tập hợp.

Ví dụ, giả sử bạn muốn chia một message mang đơn hàng thành hai message: một chứa thông tin thanh toán và một chứa danh sách các mặt hàng. OrderSplitter sau đây sẽ thực hiện việc đó:

java
public class OrderSplitter {
  public Collection<Object> splitOrderIntoParts(PurchaseOrder po) {
    ArrayList<Object> parts = new ArrayList<>();
    parts.add(po.getBillingInfo());
    parts.add(po.getLineItems());
    return parts;
  }
}

Bạn có thể khai báo một bean OrderSplitter như một phần của luồng tích hợp bằng cách chú thích nó với @Splitter như sau:

java
@Bean
@Splitter(inputChannel="poChannel",
      outputChannel="splitOrderChannel")
public OrderSplitter orderSplitter() {
  return new OrderSplitter();
}

Tại đây, các đơn hàng đến kênh tên là poChannel và được chia tách bởi OrderSplitter. Sau đó, mỗi phần tử trong tập hợp được trả về sẽ được xuất bản dưới dạng một message riêng biệt trong luồng tích hợp đến kênh có tên là splitOrderChannel. Tại thời điểm này trong luồng, bạn có thể khai báo một PayloadTypeRouter để định tuyến thông tin thanh toán và các mặt hàng đến các luồng con riêng biệt như sau:

java
@Bean
@Router(inputChannel="splitOrderChannel")
public MessageRouter splitOrderRouter() {
  PayloadTypeRouter router = new PayloadTypeRouter();
  router.setChannelMapping(
      BillingInfo.class.getName(), "billingInfoChannel");
  router.setChannelMapping(
      List.class.getName(), "lineItemsChannel");
  return router;
}

Đúng như tên gọi, PayloadTypeRouter định tuyến các message đến các kênh khác nhau dựa trên kiểu payload của chúng. Như được cấu hình ở đây, các message có payload là kiểu BillingInfo sẽ được định tuyến đến kênh billingInfoChannel để xử lý thêm. Còn các mặt hàng thì nằm trong một tập hợp java.util.List; vì vậy, bạn ánh xạ các payload kiểu List để định tuyến đến kênh lineItemsChannel.

Tại thời điểm này, luồng được chia thành hai luồng con: một nơi các đối tượng BillingInfo đi qua và một nơi chứa List<LineItem>. Nhưng nếu bạn muốn chia nhỏ hơn nữa để thay vì xử lý một List các đối tượng LineItem, bạn xử lý từng LineItem riêng biệt thì sao? Tất cả những gì bạn cần làm là viết một phương thức (không phải bean) được chú thích với @Splitter và trả về một tập hợp các đối tượng LineItem, ví dụ như sau:

java
@Splitter(inputChannel="lineItemsChannel", outputChannel="lineItemChannel")
public List<LineItem> lineItemSplitter(List<LineItem> lineItems) {
  return lineItems;
}

Khi một message có payload là List<LineItem> đến kênh tên là lineItemsChannel, nó sẽ được truyền vào phương thức lineItemSplitter(). Theo quy tắc của splitter, phương thức này phải trả về một tập hợp các phần tử cần chia tách. Trong trường hợp này, bạn đã có sẵn một tập hợp các đối tượng LineItem, nên chỉ cần trả về tập hợp đó trực tiếp. Kết quả là, mỗi LineItem trong tập hợp sẽ được phát hành thành một message riêng biệt đến kênh tên lineItemChannel.

Nếu bạn muốn sử dụng Java DSL để khai báo cấu hình splitter/router tương tự, bạn có thể làm điều đó bằng các lời gọi split()route() như sau:

java
return IntegrationFlows
  ...
    .split(orderSplitter())
    .<Object, String> route(
      p -> {
        if (p.getClass().isAssignableFrom(BillingInfo.class)) {
          return "BILLING_INFO";
        } else {
          return "LINE_ITEMS";
        }
      }, mapping -> mapping
        .subFlowMapping("BILLING_INFO", sf -> sf
          .<BillingInfo> handle((billingInfo, h) -> {
          ...
        }))
        .subFlowMapping("LINE_ITEMS", sf -> sf
          .split()
          .<LineItem> handle((lineItem, h) -> {
           ...
        }))
      )
    .get();

Dạng DSL của định nghĩa luồng rõ ràng là ngắn gọn hơn, mặc dù có thể hơi khó theo dõi hơn. Chúng ta có thể làm cho nó dễ đọc hơn bằng cách tách các lambda ra thành các phương thức riêng. Ví dụ, chúng ta có thể sử dụng ba phương thức sau để thay thế các lambda trong định nghĩa luồng:

java
private String route(Object p) {
  return p.getClass().isAssignableFrom(BillingInfo.class)
    ? "BILLING_INFO"
    : "LINE_ITEMS";
}
private BillingInfo handleBillingInfo(
    BillingInfo billingInfo, MessageHeaders h) {
  // ...
}
private LineItem handleLineItems(
    LineItem lineItem, MessageHeaders h) {
  // ...
}

Sau đó, chúng ta có thể viết lại luồng tích hợp với các tham chiếu phương thức như sau:

java
return IntegrationFlows
  ...
    .split()
    .route(
      this::route,
      mapping -> mapping
        .subFlowMapping("BILLING_INFO", sf -> sf
          .<BillingInfo> handle(this::handleBillingInfo))
        .subFlowMapping("LINE_ITEMS", sf -> sf
          .split()
          .<LineItem> handle(this::handleLineItems)));

Dù là cách nào, chúng ta vẫn sử dụng cùng một OrderSplitter để chia đơn hàng như trong ví dụ cấu hình Java. Sau khi đơn hàng được chia, nó sẽ được định tuyến theo kiểu dữ liệu đến hai luồng con riêng biệt.

Released under the MIT License.