領先一步
VMware 提供培訓和認證,助您加速進步。
瞭解更多親愛的Spring社群!
最近我們釋出了 Spring Integration Java DSL: 逐行教程,其中廣泛使用了 Java 8 Lambda。我們收到一些反饋,認為這是 DSL 的一個很好的介紹,但對於那些無法遷移到 Java 8 或尚未熟悉 Lambdas 但希望利用它的人來說,需要一個類似的教程。
因此,為了幫助那些希望從 XML 配置遷移到 Java 和註解配置的 Spring Integration 使用者,我們提供了這個 逐行教程 來演示,即使沒有 Lambdas,我們也能從 Spring Integration Java DSL 的使用中獲益良多。當然,大多數人會同意 Lambda 語法提供了更簡潔的定義。
我們在這裡分析的是同一個 Cafe Demo 示例,但使用了 Java 8 之前的配置版本。許多選項是相同的,所以我們只是將它們的描述複製/貼上到這裡,以獲得完整的圖景。由於這個 Spring Integration Java DSL 配置與 Java 8 Lambda 風格非常不同,所有使用者都可以從中學習如何透過 Spring Integration Java DSL 提供的豐富選項來實現相同的結果。
我們的應用程式的原始碼放在一個類中,這是一個 Boot 應用程式;重要行都標有相應的數字,對應下面的註釋。
@SpringBootApplication // 1
@IntegrationComponentScan // 2
public class Application {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext ctx =
SpringApplication.run(Application.class, args); // 3
Cafe cafe = ctx.getBean(Cafe.class); // 4
for (int i = 1; i <= 100; i++) { // 5
Order order = new Order(i);
order.addItem(DrinkType.LATTE, 2, false);
order.addItem(DrinkType.MOCHA, 3, true);
cafe.placeOrder(order);
}
System.out.println("Hit 'Enter' to terminate"); // 6
System.in.read();
ctx.close();
}
@MessagingGateway // 7
public interface Cafe {
@Gateway(requestChannel = "orders.input") // 8
void placeOrder(Order order); // 9
}
private final AtomicInteger hotDrinkCounter = new AtomicInteger();
private final AtomicInteger coldDrinkCounter = new AtomicInteger(); // 10
@Autowired
private CafeAggregator cafeAggregator; // 11
@Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata poller() { // 12
return Pollers.fixedDelay(1000).get();
}
@Bean
@SuppressWarnings("unchecked")
public IntegrationFlow orders() { // 13
return IntegrationFlows.from("orders.input") // 14
.split("payload.items", (Consumer) null) // 15
.channel(MessageChannels.executor(Executors.newCachedThreadPool()))// 16
.route("payload.iced", // 17
new Consumer<RouterSpec<ExpressionEvaluatingRouter>>() { // 18
@Override
public void accept(RouterSpec<ExpressionEvaluatingRouter> spec) {
spec.channelMapping("true", "iced")
.channelMapping("false", "hot"); // 19
}
})
.get(); // 20
}
@Bean
public IntegrationFlow icedFlow() { // 21
return IntegrationFlows.from(MessageChannels.queue("iced", 10)) // 22
.handle(new GenericHandler<OrderItem>() { // 23
@Override
public Object handle(OrderItem payload, Map<String, Object> headers) {
Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);
System.out.println(Thread.currentThread().getName()
+ " prepared cold drink #" + coldDrinkCounter.incrementAndGet()
+ " for order #" + payload.getOrderNumber() + ": " + payload);
return payload; // 24
}
})
.channel("output") // 25
.get();
}
@Bean
public IntegrationFlow hotFlow() { // 26
return IntegrationFlows.from(MessageChannels.queue("hot", 10))
.handle(new GenericHandler<OrderItem>() {
@Override
public Object handle(OrderItem payload, Map<String, Object> headers) {
Uninterruptibles.sleepUninterruptibly(5, TimeUnit.SECONDS); // 27
System.out.println(Thread.currentThread().getName()
+ " prepared hot drink #" + hotDrinkCounter.incrementAndGet()
+ " for order #" + payload.getOrderNumber() + ": " + payload);
return payload;
}
})
.channel("output")
.get();
}
@Bean
public IntegrationFlow resultFlow() { // 28
return IntegrationFlows.from("output") // 29
.transform(new GenericTransformer<OrderItem, Drink>() { // 30
@Override
public Drink transform(OrderItem orderItem) {
return new Drink(orderItem.getOrderNumber(),
orderItem.getDrinkType(),
orderItem.isIced(),
orderItem.getShots()); // 31
}
})
.aggregate(new Consumer<AggregatorSpec>() { // 32
@Override
public void accept(AggregatorSpec aggregatorSpec) {
aggregatorSpec.processor(cafeAggregator, null); // 33
}
}, null)
.handle(CharacterStreamWritingMessageHandler.stdout()) // 34
.get();
}
@Component
public static class CafeAggregator { // 35
@Aggregator // 36
public Delivery output(List<Drink> drinks) {
return new Delivery(drinks);
}
@CorrelationStrategy // 37
public Integer correlation(Drink drink) {
return drink.getOrderNumber();
}
}
}
逐行檢查程式碼...
1. ````java @SpringBootApplication ```` 這是 Spring Boot 1.2 中的一個新元註解。它包含 `@Configuration` 和 `@EnableAutoConfiguration`。由於我們處於 Spring Integration 應用程式中,並且 Spring Boot 為其提供了自動配置,因此 `@EnableIntegration` 會被自動應用,以初始化 Spring Integration 基礎設施,包括一個用於 Java DSL 的環境 - `DslIntegrationConfigurationInitializer`,它由 `/META-INF/spring.factories` 中的 `IntegrationConfigurationBeanFactoryPostProcessor` 拾取。 2. ````java @IntegrationComponentScan ```` 這是 Spring Integration 中 `@ComponentScan` 的類似物,用於掃描基於介面的元件(Spring Framework 的 `@ComponentScan` 只檢視類)。Spring Integration 支援發現用 `@MessagingGateway` 註解的介面(見下面的 #7)。 3. ````java ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args); ```` 我們類的 `main` 方法旨在啟動 Spring Boot 應用程式,使用此類的配置,並透過 Spring Boot 啟動一個 `ApplicationContext`。此外,它還將命令列引數委託給 Spring Boot。例如,您可以指定 `--debug` 來檢視有關啟動自動配置報告的日誌。 4. ````java Cafe cafe = ctx.getBean(Cafe.class); ```` 由於我們已經有了一個 `ApplicationContext`,我們可以開始與應用程式互動。`Cafe` 是入口點 - 在 EIP 術語中是一個 `gateway`。閘道器只是介面,應用程式不直接與訊息傳遞 API 互動;它只處理域(見下面的 #7)。 5. ````java for (int i = 1; i <= 100; i++) { ```` 為了演示咖啡館的“工作”,我們發起了 100 個訂單,包含兩種飲品 - 一種熱飲和一種冷飲。並將 `Order` 傳送給 `Cafe` 閘道器。 6. ````java System.out.println("Hit 'Enter' to terminate"); ```` 通常 Spring Integration 應用程式是非同步的,因此為了避免從 `main` 執行緒過早退出,我們在使用者透過命令列進行某些互動之前,會阻塞 `main` 方法。非守護執行緒會保持應用程式開啟,但 `System.read()` 為我們提供了一個乾淨關閉應用程式的機制。 7. ````java @MessagingGateway ```` 這個註解用於標記一個業務介面,表明它是應用程式與整合層之間的 `gateway`。它是 Spring Integration XML 配置中 ``.split()` EIP 方法的第二個引數是 `endpointConfigurer`,用於自定義 `autoStartup`、`requiresReply`、`adviceChain` 等選項。我們在這裡使用 `null` 來表明我們依賴於端點的預設選項。許多 EIP 方法都提供了帶和不帶 `endpointConfigurer` 的過載版本。目前,沒有提供不帶 `endpointConfigurer` 引數的 `.split(String expression)` EIP 方法;這將在未來的版本中解決。
在我們的示例中,我們這裡使用了一個受限的 QueueChannel 來反映現實生活中咖啡館廚房繁忙的狀態。而在這裡,我們需要為監聽此通道的下一個端點設定 全域性 poller。
好了,我們已經描述了基於 Spring Integration Java DSL 的 Cafe Demo 示例,其中不包含 Java Lambda 支援。將其與 XML 示例 進行比較,並查閱 Lambda 支援教程 以獲取更多關於 Spring Integration 的資訊。
正如您所見,在沒有 Lambda 的情況下使用 DSL 會稍微冗長一些,因為您需要為函式式介面的內聯匿名實現提供樣板程式碼。然而,我們相信支援 DSL 對於那些還無法遷移到 Java 8 的使用者來說很重要。DSL 的許多優勢(流暢的 API、編譯時驗證等)對所有使用者都是可用的。
Lambda 的使用延續了 Spring Framework 減少或消除樣板程式碼的傳統,因此我們鼓勵使用者嘗試 Java 8 和 Lambda,並鼓勵他們的組織考慮允許在 Spring Integration 應用程式中使用 Java 8。
此外,請參閱 參考手冊 以獲取更多資訊。
一如既往,我們期待您的評論和反饋(StackOverflow (spring-integration 標籤)、Spring JIRA、GitHub),我們非常歡迎貢獻!
感謝您的時間和耐心閱讀本文!