集成数据

本指南将引导您使用 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://springjava.cn/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://springjava.cn/blog/2014/11/24/spring-integration-java-dsl-1-0-ga-released
This Week in Spring - November 25th, 2014 @ https://springjava.cn/blog/2014/11/25/this-week-in-spring-november-25th-2014
Spring Integration Java DSL: Line by line tutorial @ https://springjava.cn/blog/2014/11/25/spring-integration-java-dsl-line-by-line-tutorial
Spring for Apache Hadoop 2.1.0.M2 Released @ https://springjava.cn/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 许可证发布,文本内容采用 署名-禁止演绎创作共用许可证 发布。