本指南将引导您完成使用Spring Integration创建一个简单应用程序的过程,该应用程序将从RSS Feed(Spring Blog)中检索数据,处理数据,然后将其写入文件。本指南使用传统的Spring Integration XML配置。其他指南也显示了在有和没有JDK 8 Lambda表达式的情况下如何使用JavaConfig / DSL。

你会建立什么

您将使用传统的XML配置通过Spring Integration创建一个流程。

你需要什么

如何完成本指南

像大多数Spring 入门指南一样,您可以从头开始并完成每个步骤,也可以绕过您已经熟悉的基本设置步骤。无论哪种方式,您最终都可以使用工作代码。

从头开始 ,请继续使用Gradle构建

跳过基础知识 ,请执行以下操作:

完成后 ,您可以根据中的代码检查结果gs-integration/complete

用Gradle构建

用Gradle构建

首先,您设置一个基本的构建脚本。在使用Spring构建应用程序时,可以使用任何喜欢的构建系统,但是此处包含使用GradleMaven所需的代码。如果您都不熟悉,请参阅使用Gradle 构建Java项目使用Maven构建Java项目

创建目录结构

在您选择的项目目录中,创建以下子目录结构;例如, mkdir -p src/main/java/hello在* nix系统上:

└── src
    └── main
        └── java
            └── hello

创建一个Gradle构建文件

build.gradle

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:2.1.6.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

bootJar {
    baseName = 'gs-integration'
    version =  '0.1.0'
}

repositories {
    mavenCentral()
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
    compile("org.springframework.boot:spring-boot-starter-integration")
    compile("org.springframework.integration:spring-integration-feed")
    compile("org.springframework.integration:spring-integration-file")
    testCompile("org.springframework.boot:spring-boot-starter-test")
    testCompile("junit:junit")
}

tasks.withType(JavaExec) {
    standardInput = System.in
}


eclipse {
    project {
        natures += 'org.springframework.ide.eclipse.core.springnature'
    }
}

Spring Boot gradle插件提供了许多方便的功能:

  • 它收集类路径上的所有jar,并构建一个可运行的单个“über-jar”,这使执行和传输服务更加方便。

  • 它搜索public static void main()标记为可运行类的方法。

  • 它提供了一个内置的依赖项解析器,用于设置版本号以匹配Spring Boot依赖项 。您可以覆盖所需的任何版本,但是它将默认为Boot选择的一组版本。

用Maven构建

用Maven构建

首先,您设置一个基本的构建脚本。使用Spring构建应用程序时,可以使用任何喜欢的构建系统,但是此处包含了使用Maven所需的代码。如果您不熟悉Maven,请参阅使用Maven 构建Java项目

创建目录结构

在您选择的项目目录中,创建以下子目录结构;例如, mkdir -p src/main/java/hello在* nix系统上:

└── src
    └── main
        └── java
            └── hello

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>

    <groupId>org.springframework</groupId>
    <artifactId>gs-integration</artifactId>
    <version>0.1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <properties>
        <java.version>1.8</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>
    </dependencies>


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

</project>

Spring Boot Maven插件提供了许多方便的功能:

  • 它收集类路径上的所有jar,并构建一个可运行的单个“über-jar”,这使执行和传输服务更加方便。

  • 它搜索public static void main()标记为可运行类的方法。

  • 它提供了一个内置的依赖项解析器,用于设置版本号以匹配Spring Boot依赖项 。您可以覆盖所需的任何版本,但是它将默认为Boot选择的一组版本。

使用您的IDE进行构建

使用您的IDE进行构建

定义整合流程

对于本指南的示例应用程序,您将定义一个Spring Integration流,该流从Spring IO的RSS feed中读取博客文章,并将其转换为易于阅读的内容。 String由文章标题和文章网址组成,并附加String到文件末尾/tmp/si/SpringBlog

要定义集成流程,您只需使用Spring Integration的XML名称空间中的少量元素创建一个Spring XML配置。具体来说,对于所需的集成流程,您可以使用这些Spring Integration名称空间中的元素:核心,提要和文件。

以下XML配置文件定义了集成流程:

src/main/resources/hello/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://spring.io/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>

如您所见,这里有三个集成元素:

  • 。一个入站适配器,用于检索帖子,每个轮询一个。按照此处的配置,它每5秒钟轮询一次。这些帖子放置在名为“新闻”(与适配器的ID对应)的频道中。

  • 。转换条目( com.rometools.rome.feed.synd.SyndEntry )在“新闻”频道中,提取条目的标题( payload.title )并链接( payload.link )并将它们连接成可读的String (添加换行符)。的String然后将其发送到名为“ file”的输出通道。

  • 。出站通道适配器,它将内容从其通道(在此称为“文件”)写入文件。具体来说,按照此处的配置,它将“文件”通道中的所有内容追加到位于/tmp/si/SpringBlog

这个简单的流程如下所示:

读取RSS feed条目的流

忽略auto-startup现在的属性;我们稍后将在讨论测试时再次进行讨论。只是注意到它将true默认情况下,这意味着将在应用程序启动时提取帖子。还要注意属性占位符在filename-generator-expression ;这意味着默认值为SpringBlog但可以被属性覆盖

使应用程序可执行

尽管通常在较大的应用程序(甚至是Web应用程序)中配置Spring Integration流,但没有理由不能在更简单的独立应用程序中定义它。这就是您接下来要做的,创建一个开始集成流程的主类,并声明一些bean以支持集成流程。您还可以将应用程序构建到独立的可执行JAR文件中。我们使用Spring Boot的SpringApplication创建应用程序上下文。由于本指南将XML名称空间用于集成流程,因此请注意,我们使用@ImportResource加载到应用程序上下文中。

src/main/java/hello/Application.java

package hello;

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

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

}

构建可执行的JAR

您可以使用Gradle或Maven从命令行运行该应用程序。您还可以构建一个包含所有必需的依赖项,类和资源的可执行JAR文件,然后运行该文件。构建可执行的jar使得在整个开发生命周期中,跨不同环境等等的情况下,可以轻松地将服务作为应用程序进行发布,版本化和部署。

如果您使用Gradle,则可以使用./gradlew bootRun 。或者,您可以通过使用以下命令构建JAR文件: ./gradlew build然后运行JAR文件,如下所示:

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

如果使用Maven,则可以通过使用以下命令运行应用程序./mvnw spring-boot:run 。或者,您可以使用以下命令构建JAR文件: ./mvnw clean package然后运行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 -f /tmp/si/SpringBlog

您应该看到类似以下内容(实际新闻会有所不同):

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

测试中

检查complete项目,您将看到一个测试案例。

src/test/java/hello/FlowTests.java

package hello;

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

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

import org.junit.Test;
import org.junit.runner.RunWith;

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 org.springframework.test.context.junit4.SpringRunner;

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

@RunWith(SpringRunner.class)
@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://foo/bar");
		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://foo/bar");
		br.close();
		out.delete();
	}

}

这使用Spring Boot的测试支持来设置属性auto.startupfalse 。依靠网络连接进行测试通常不是一个好主意,尤其是在CI环境中。因此,相反,我们阻止了Feed适配器启动并注入了SyndEntry进入news其余流程处理的通道。该测试还设置了feed.file.name因此测试将写入另一个文件;然后:

  • 验证适配器已停止

  • 创建一个测试SyndEntry

  • 删除测试输出文件(如果存在)

  • 发送消息

  • 验证文件是否存在

  • 读取文件并验证数据是否符合预期

摘要

恭喜你!您已经开发了一个简单的应用程序,该应用程序使用Spring Integration从spring.io中获取博客文章,对其进行处理并将其写入文件。

也可以看看

以下指南也可能会有所帮助:

是否要编写新指南或为现有指南做出贡献?查看我们的贡献准则

所有指南均以代码的ASLv2许可证和写作的Attribution,NoDerivatives创作共用许可证发布