整合資料

本指南將引導您使用 Spring Integration 建立一個簡單的應用程式,該應用程式從 RSS Feed(Spring 部落格)檢索資料,處理資料,然後將其寫入檔案。本指南使用傳統的 Spring Integration XML 配置。其他指南將展示如何使用帶 Lambda 表示式和不帶 Lambda 表示式的 Java 配置和 DSL。

您將構建什麼

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

您需要什麼

如何完成本指南

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

從頭開始,請繼續閱讀 使用 Spring Initializr 入門

跳過基礎部分,請執行以下操作

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

使用 Spring Initializr 入門

您可以使用此預配置專案,然後單擊 Generate 下載 ZIP 檔案。此專案已配置為適合本教程中的示例。

手動初始化專案

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

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

  3. 單擊 Dependencies(依賴項)並選擇 Spring Integration

  4. 單擊 Generate(生成)。

  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 中讀取部落格文章。

  • 將它們轉換為包含文章標題和文章 URL 的易讀 String

  • 將該 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 許可證釋出,文字內容採用 署名-禁止演繹創作共用許可證 釋出。

獲取程式碼