整合資料

本指南將引導您完成使用 Spring Integration 建立一個簡單應用程式的過程,該應用程式從 RSS Feed (Spring Blog) 中檢索資料,對資料進行處理,然後將其寫入檔案。本指南使用傳統的 Spring Integration XML 配置。其他指南將介紹如何使用 Java 配置和 DSL(帶或不帶 Lambda 表示式)進行配置。

您將構建什麼

您將使用傳統的 XML 配置透過 Spring Integration 建立一個流程。

你需要什麼

如何完成本指南

與大多數 Spring 入門指南一樣,您可以從頭開始並完成每個步驟,也可以跳過您已熟悉的基本設定步驟。無論哪種方式,您最終都會得到可工作的程式碼。

從頭開始,請轉到從 Spring Initializr 開始

跳過基礎知識,請執行以下操作

完成後,您可以將您的結果與 gs-integration/complete 中的程式碼進行對照。

從 Spring Initializr 開始

您可以使用這個 預初始化專案 並點選生成下載 ZIP 檔案。此專案已配置為符合本教程中的示例。

手動初始化專案

  1. 導航到 https://start.spring.io。此服務會為您拉取應用程式所需的所有依賴項,併為您完成大部分設定。

  2. 選擇 Gradle 或 Maven 以及您想要使用的語言。本指南假設您選擇了 Java。

  3. 點選 Dependencies 並選擇 Spring Integration

  4. 單擊生成

  5. 下載生成的 ZIP 檔案,這是一個已根據您的選擇配置好的 Web 應用程式存檔。

如果您的 IDE 集成了 Spring Initializr,您可以從 IDE 中完成此過程。
您還可以從 Github fork 該專案並在您的 IDE 或其他編輯器中開啟它。

新增到構建檔案

對於此示例,您需要新增兩個依賴項。

  • spring-integration-feed

  • spring-integration-file

以下列表顯示了最終的 pom.xml 檔案

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.0</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>integration-complete</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>integration-complete</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>17</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-integration</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.integration</groupId>
			<artifactId>spring-integration-feed</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.integration</groupId>
			<artifactId>spring-integration-file</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.integration</groupId>
			<artifactId>spring-integration-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

以下列表顯示了最終的 build.gradle 檔案

plugins {
	id 'org.springframework.boot' version '3.3.0'
	id 'java'
}

apply plugin: 'io.spring.dependency-management'

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-integration'
	implementation 'org.springframework.integration:spring-integration-feed'
	implementation 'org.springframework.integration:spring-integration-file'
	testImplementation('org.springframework.boot:spring-boot-starter-test')
	testImplementation 'org.springframework.integration:spring-integration-test'
}

test {
	useJUnitPlatform()
}

定義整合流程

對於本指南的示例應用程式,您將定義一個 Spring Integration 流程,該流程將:

  • 從 spring.io 的 RSS feed 讀取部落格文章。

  • 將它們轉換為易於閱讀的 String,其中包含文章標題和文章的 URL。

  • 將該 String 追加到檔案末尾(/tmp/si/SpringBlog)。

要定義整合流程,您可以建立一個 Spring XML 配置檔案,其中包含 Spring Integration 的 XML 名稱空間中的一些元素。具體來說,對於所需的整合流程,您將使用以下 Spring Integration 名稱空間中的元素:core、feed 和 file。(獲取後兩個是我們需要修改 Spring Initializr 提供的構建檔案的原因。)

以下 XML 配置檔案(來自 src/main/resources/integration/integration.xml)定義了整合流程:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:int="http://www.springframework.org/schema/integration"
	xmlns:file="http://www.springframework.org/schema/integration/file"
	xmlns:feed="http://www.springframework.org/schema/integration/feed"
	xsi:schemaLocation="http://www.springframework.org/schema/integration/feed https://www.springframework.org/schema/integration/feed/spring-integration-feed.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/integration/file https://www.springframework.org/schema/integration/file/spring-integration-file.xsd
		http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd">

    <feed:inbound-channel-adapter id="news" url="https://springframework.tw/blog.atom" auto-startup="${auto.startup:true}">
        <int:poller fixed-rate="5000"/>
    </feed:inbound-channel-adapter>

    <int:transformer
            input-channel="news"
            expression="payload.title + ' @ ' + payload.link + '#{systemProperties['line.separator']}'"
            output-channel="file"/>

    <file:outbound-channel-adapter id="file"
            mode="APPEND"
            charset="UTF-8"
            directory="/tmp/si"
            filename-generator-expression="'${feed.file.name:SpringBlog}'"/>

</beans>

這裡有三個整合元素在起作用:

  • <feed:inbound-channel-adapter>:一個入站介面卡,每隔輪詢一次檢索帖子。在此配置中,它每五秒輪詢一次。帖子被放入一個名為 news 的通道(對應於介面卡的 ID)。

  • <int:transformer>:轉換 news 通道中的條目(com.rometools.rome.feed.synd.SyndEntry),提取條目的標題(payload.title)和連結(payload.link),並將它們連線成一個易於閱讀的 String(並新增換行符)。然後將 String 傳送到名為 file 的輸出通道。

  • <file:outbound-channel-adapter>:一個出站通道介面卡,它將內容從其通道(名為 file)寫入檔案。具體來說,在此配置中,它將 file 通道中的任何內容追加到位於 /tmp/si/SpringBlog 的檔案中。

下圖顯示了這個簡單的流程:

A flow that reads RSS feed entries

暫時忽略 auto-startup 屬性。我們稍後在討論測試時會重新討論它。目前,請注意它預設設定為 true,這意味著在應用程式啟動時就會獲取帖子。還要注意 filename-generator-expression 中的屬性佔位符。這意味著預設值是 SpringBlog,但可以透過屬性覆蓋。

使應用程式可執行

雖然通常在一個更大的應用程式(甚至可能是 Web 應用程式)中配置 Spring Integration 流程,但沒有理由不能在一個更簡單的獨立應用程式中定義它。接下來您將要做的是:建立一個主類來啟動整合流程,並宣告一些 bean 來支援整合流程。您還將把應用程式構建成一個獨立的、可執行的 JAR 檔案。我們使用 Spring Boot 的 @SpringBootApplication 註解來建立應用程式上下文。由於本指南使用 XML 名稱空間來定義整合流程,因此您必須使用 @ImportResource 註解將其載入到應用程式上下文中。以下列表(來自 src/main/java/com/example/integration/IntegrationApplication.java)顯示了應用程式檔案:

package com.example.integration;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ImportResource;

@SpringBootApplication
@ImportResource("/integration/integration.xml")
public class IntegrationApplication {
  public static void main(String[] args) throws Exception {
    ConfigurableApplicationContext ctx = new SpringApplication(IntegrationApplication.class).run(args);
    System.out.println("Hit Enter to terminate");
    System.in.read();
    ctx.close();
  }

}

構建可執行 JAR

您可以使用 Gradle 或 Maven 從命令列執行應用程式。您還可以構建一個包含所有必要依賴項、類和資源並執行的單個可執行 JAR 檔案。構建可執行 JAR 使在整個開發生命週期中,跨不同環境等,輕鬆交付、版本化和部署服務作為應用程式。

如果您使用 Gradle,您可以透過使用 ./gradlew bootRun 執行應用程式。或者,您可以透過使用 ./gradlew build 構建 JAR 檔案,然後按如下方式執行 JAR 檔案

java -jar build/libs/gs-integration-0.1.0.jar

如果您使用 Maven,您可以透過使用 ./mvnw spring-boot:run 執行應用程式。或者,您可以使用 ./mvnw clean package 構建 JAR 檔案,然後按如下方式執行 JAR 檔案

java -jar target/gs-integration-0.1.0.jar
這裡描述的步驟建立了一個可執行的 JAR。您還可以構建一個經典的 WAR 檔案

執行應用程式

現在,您可以執行該應用程式的 jar 檔案,執行以下命令:

java -jar build/libs/{project_id}-0.1.0.jar

... app starts up ...

應用程式啟動後,它會連線到 RSS feed 並開始獲取部落格文章。應用程式透過您定義的整合流程處理這些文章,最終將文章資訊追加到 /tmp/si/SpringBlog 的檔案中。

應用程式執行一段時間後,您應該能夠檢視 /tmp/si/SpringBlog 中的檔案,檢視一些帖子的資料。在類 UNIX 作業系統上,您也可以使用 tail 命令即時檢視寫入的資料,執行以下命令:

tail -f /tmp/si/SpringBlog

您應該會看到類似以下的示例輸出(但實際新聞會有所不同):

Spring Integration Java DSL 1.0 GA Released @ https://springframework.tw/blog/2014/11/24/spring-integration-java-dsl-1-0-ga-released
This Week in Spring - November 25th, 2014 @ https://springframework.tw/blog/2014/11/25/this-week-in-spring-november-25th-2014
Spring Integration Java DSL: Line by line tutorial @ https://springframework.tw/blog/2014/11/25/spring-integration-java-dsl-line-by-line-tutorial
Spring for Apache Hadoop 2.1.0.M2 Released @ https://springframework.tw/blog/2014/11/14/spring-for-apache-hadoop-2-1-0-m2-released

測試

檢查 complete 專案,您會在 src/test/java/com/example/integration/FlowTests.java 中看到一個測試用例。

package com.example.integration;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.MessageChannel;

import com.rometools.rome.feed.synd.SyndEntryImpl;

@SpringBootTest({ "auto.startup=false",   // we don't want to start the real feed
          "feed.file.name=Test" })   // use a different file
public class FlowTests {

  @Autowired
  private SourcePollingChannelAdapter newsAdapter;

  @Autowired
  private MessageChannel news;

  @Test
  public void test() throws Exception {
    assertThat(this.newsAdapter.isRunning()).isFalse();
    SyndEntryImpl syndEntry = new SyndEntryImpl();
    syndEntry.setTitle("Test Title");
    syndEntry.setLink("http://characters/frodo");
    File out = new File("/tmp/si/Test");
    out.delete();
    assertThat(out.exists()).isFalse();
    this.news.send(MessageBuilder.withPayload(syndEntry).build());
    assertThat(out.exists()).isTrue();
    BufferedReader br = new BufferedReader(new FileReader(out));
    String line = br.readLine();
    assertThat(line).isEqualTo("Test Title @ http://characters/frodo");
    br.close();
    out.delete();
  }

}

此測試使用 Spring Boot 的測試支援將名為 auto.startup 的屬性設定為 false。依賴網路連線進行測試通常不是一個好主意,尤其是在 CI 環境中。相反,我們阻止 feed 介面卡啟動,並將一個 SyndEntry 注入到 news 通道中,以便其餘流程處理。該測試還設定了 feed.file.name,以便測試寫入不同的檔案。然後它:

  • 驗證介面卡已停止。

  • 建立測試 SyndEntry

  • 刪除測試輸出檔案(如果存在)。

  • 傳送訊息。

  • 驗證檔案是否存在。

  • 讀取檔案並驗證資料是否符合預期。

總結

恭喜!您已經開發了一個簡單的應用程式,該應用程式使用 Spring Integration 從 spring.io 獲取部落格文章,進行處理,然後將其寫入檔案。

另請參閱

以下指南也可能有所幫助:

想寫新指南或為現有指南做貢獻嗎?請檢視我們的貢獻指南

所有指南的程式碼均採用 ASLv2 許可,文字內容採用署名-禁止演繹知識共享許可

獲取程式碼