Springは、 Spring Integration と呼ばれる拡張機能を使用して、エンタープライズフレームワーク間でのアプリケーション統合をサポートします。 。主な目標は、多様なビジネスドメインを持つアプリケーションを容易にすることです。テクノロジーは、企業全体の水平方向の相互運用性に向けて機能します。軽量のメッセージングと、アダプタフレームワークを使用した外部システムおよびサービスとの統合を追加します。この記事は、Spring Integrationの基本的な理解と、Springプログラミングモデルにどのように拡張されるかを説明することを目的としています。
概要
商用アプリケーションは、ビジネスユニットが提起する問題の解決策に他なりません。問題の規模と複雑さによって、ソリューションがエンタープライズ規模であるか、数行のコードであるかが決まります。エンタープライズアプリケーションの問題は、新しいハードウェアであれソフトウェアであれ、新しいテクノロジーに合わせてゼロから再構築するのは費用効果が高くない可能性がある古いテクノロジーを使用して、ソリューションの一部がすでに利用可能である場合があることです。これは、レガシーアプリケーションの典型的な問題です。したがって、私たちにできることは、既存のシステムと相互運用する新しいコンポーネントを作成することです。これはアプリケーション統合です 。しかし、問題はそれだけではありません。互いの効率に不必要な制限を課すことなく、既存のコンポーネントとシームレスに連携するコンポーネントを作成することは非常に簡単ではありません。複数のコンポーネントをシームレスに統合するには、対処しなければならない重大な考慮事項があります。 Spring Integrationはこれらの問題を考慮し、開発者が簡単に統合できるようにコーディングするための環境を提供します。 Springは、関連する一般的なパターンを特定し、開発者の介入をほとんど必要とせずに機能します。
緩い結合
ソリューションは複数のパーツを使用する必要があるため、各パーツには、可能な限り緩く結合された方法で個別の懸念事項が必要です。あるコンポーネントの進化が、別のコンポーネントに深刻な設計と保守の影響を与えてはなりません。完全に分離された状況は統合に適さず、密結合は許容されません。したがって、ゲームは、可能な限り緩く結合された方法でコンポーネントを設計することです。 Springアプリケーションで結合を減らすには、依存性注入またはイベント駆動型アーキテクチャの2つの方法があります。 。イベント駆動型アーキテクチャは広義の用語であり、メッセージ駆動型アーキテクチャ(MOM)とは非常に細かい違いがあります。技術的には同じではありませんが、この目的のために、ここではこれらの用語を同じ意味で使用する場合があります。純粋主義者のために、イベント駆動型とメッセージ駆動型の基本的な違いは、メッセージは受信者に向けられているのに対し、イベントは向けられていないということです。そうでなければ、それらの実装には明確な境界がほとんどありません。ここでは入りません。代わりに、SpringIntegrationでイベント駆動型アーキテクチャがどのように利用されているかに焦点を当てましょう。
イベント駆動型のSpring統合
イベント駆動型アーキテクチャでは、複雑なアプリケーションがいくつかのサービスコンポーネントに分割されます。これらのコンポーネントは、パブリッシャーと呼ばれる他のコンポーネントによって生成されたイベントを介して相互作用します イベントの。その特定のイベントに関心のある他のコンポーネントは、それをサブスクライブし、それに応じて適切なアクションを実行します。これは、本質的に、イベントに取り組むパブリッシャー/サブスクライバーモデルです。
状態の特定の変化はイベントです。述べたように、イベント は利害関係者に送信され、イベントサブスクライバーは、ビジネスプロセスの実行、別のイベントの公開、または場合によってはそれを完全に無視するなどのサービスを提供することによって応答することを選択できます。つまり、一度公開されたイベントは、サブスクライバーの応答に対して一切の責任を負いません。これは分離されたシナリオであり、イベント駆動型アーキテクチャはこのような緩い結合シナリオを活用します。緩い結合は、SpringIntegrationが必要とするリアルタイムのワークフローを実行するのに特に適しています。
春の統合コンポーネント
Springフレームワークの拡張として、Spring Integrationは基本的に、メッセージ、メッセージチャネル、エンドポイントの3つのコンポーネントを追加します。 Spring Integrationの開発者は、エンタープライズ分野のさまざまなアーキテクチャ、ドメイン、テクノロジー間で相互運用するための類似点の一般的なパターンを認識していました。したがって、パイプとフィルターを使用してコンポーネントを介してメッセージングを導入することにより、このモデルはアプリケーション統合の基礎になりました。フィルタコンポーネントは、パイプ(チャネルと呼ばれる)の間にメッセージを消費または生成します Spring Integrationで、フィルター間のメッセージフローについて説明しています。
関係する多くの複雑さがあります。詳細については、「参照」セクションのリンクを参照してください。ここでは、代わりに DirectChannelを使用した簡単な実装に焦点を当てましょう。 。
簡単な例
リスト1の例は、 DirectChannelとのSpring統合を実装するSpringBOOTアプリケーションです。 。ここで、メッセージチャネルは、パブリッシャーエンドポイントとサブスクライバーエンドポイントを分離するために使用されます。メッセージチャネルは、フィルタおよびアダプタコンポーネントとの接続を確立するために使用されます。この例は非常に単純で、 DirectChannelを使用しています。 パブリッシャーサブスクライバーおよびポイントツーポイント通信モデルを使用します。このコードは基本的であり、SpringIntegrationの概念を説明するための単純な実装であることに注意してください。
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns_xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi_schemaLocation="https://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mano.example</groupId>
<artifactId>spring-integration</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-integration</name>
<description>Demo project for Spring BOOT</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath/> <!-- look up parent from
repository -->
</parent>
<properties>
<project.build.sourceEncoding>
UTF-8
</project.build.sourceEncoding>
<project.reporting.outputEncoding>
UTF-8
</project.reporting.outputEncoding>
<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.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>
リスト1: pom.xml、Spring統合ソリューションのSpringBOOT依存関係
package com.mano.example.springintegration.model;
import java.util.Date;
public class Tweet {
private long tid;
private String text;
private Date time;
private String hashTag;
@Override
public String toString() {
return "Tweet{" +
"tid=" + tid +
", text='" + text + ''' +
", time=" + time +
", hashTag='" + hashTag + ''' +
'}';
}
public long getTid() {
return tid;
}
public void setTid(long tid) {
this.tid = tid;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
public String getUser() {
return hashTag;
}
public void setUser(String hashTag) {
this.hashTag = hashTag;
}
}
リスト2: Tweet.java、モデルクラス
package com.mano.example.springintegration.repo;
import com.mano.example.springintegration.model.Tweet;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Component
public class TweetPublisher {
private static long id;
public List<Tweet> getTweets(){
List<Tweet> tweets = new ArrayList<>();
tweets.add(createTweet("Storms in Pacific","#weather"));
tweets.add(createTweet("what's up developers?","#dev"));
tweets.add(createTweet("Chinese delicacy in Amazon",
"#traveller"));
tweets.add(createTweet("inflation down by 2%","#stock"));
tweets.add(createTweet("save river","#environment"));
tweets.add(createTweet("New star found","#astronaut"));
tweets.add(createTweet("Learn math quickly","#tutor"));
tweets.add(createTweet("Save animals","#bovine"));
tweets.add(createTweet("stars are favorable now",
"#astro"));
tweets.add(createTweet("social unrest in the world",
"#concern"));
return tweets;
}
Tweet createTweet(String text, String hashTag){
Tweet tweet = new Tweet();
tweet.setTid(id++);
tweet.setUser(hashTag);
tweet.setText(text);
tweet.setTime(new Date());
return tweet;
}
}
リスト3: TweetPublisher.java、ツイートデータを入力します
package com.mano.example.springintegration.pub;
import com.mano.example.springintegration.model.Tweet;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.support
.MessageBuilder;
import org.springframework.stereotype.Component;
@Component
public class Tweeter {
private DirectChannel channel;
@Value("#{tweetChannel}")
public void setChannel(DirectChannel channel) {
this.channel = channel;
}
public void sendTweetReaders(Tweet tweet) {
System.out.println("New Tweet - " + tweet.toString());
channel.send(MessageBuilder.withPayload(tweet)
.build());
}
}
リスト4: Tweeter.java、パブリッシャークラス
package com.mano.example.springintegration.sub;
import com.mano.example.springintegration.model.Tweet;
import org.springframework.integration
.MessageRejectedException;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.stereotype.Component;
@Component
public class TweetReader implements MessageHandler {
@Override
public void handleMessage(Message<?> message)
throws MessagingException {
Object payload = message.getPayload();
if (payload instanceof Tweet) {
receiveAndAcknowledge((Tweet) payload);
} else {
throw new MessageRejectedException(message,
"Unknown data type has been received.");
}
}
void receiveAndAcknowledge(Tweet tweet) {
System.out.println("Hi Tweeter, this is Reader #"
+ System.identityHashCode(this) +
"." + "Received tweet - " + tweet.toString()
+ "n");
}
}
リスト5: TweetReader.java、サブスクライバークラス
package com.mano.example.springintegration;
import com.mano.example.springintegration.incoming
.TweetPublisher;
import com.mano.example.springintegration.model.Tweet;
import com.mano.example.springintegration.pub.Tweeter;
import com.mano.example.springintegration.sub.TweetReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure
.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.messaging.MessageChannel;
import java.util.List;
@SpringBootApplication
@ComponentScan(basePackages = "com.mano.example.springintegration")
public class SpringIntegrationApplication {
@Autowired
private TweetPublisher tweetPublisher;
@Autowired
private Tweeter tweeter;
@Autowired
private DirectChannel channel;
@Bean
public MessageChannel tweetChannel(){
return new DirectChannel();
}
@Bean
public CommandLineRunner commandLineRunner
(ApplicationContext context){
return args -> {
channel.subscribe(new TweetReader());
channel.subscribe(new TweetReader());
channel.subscribe(new TweetReader());
List<Tweet> tweets = tweetPublisher.getTweets();
for (Tweet tweet: tweets){
tweeter.sendTweetReaders(tweet);
}
};
}
public static void main(String[] args) {
SpringApplication.run(SpringIntegrationApplication
.class, args);
}
}
リスト6: SpringIntegrationApplication.java、メインアプリケーションクラス
結論
ここに示されているよりも多くのSpringIntegrationがあることに注意してください。氷山の一角に過ぎません。重要な詳細は省略されています。詳細については、Springのドキュメントを参照してください。リンクを以下に示します。 Spring Integrationには、制御の反転(IoC)、横断的関心事に対処するためのアスペクト指向プログラミング、およびSpringFrameworkのその他のコアの利点を自由に使用できるなどの利点があります。 SpringIntegrationはそれをさらに進めます。 Spring Integrationの開発者は、データの方法、時期、および所在について知る必要はありません。フレームワークは、適切なコンポーネントを介してメッセージを操作することにより、ビジネスロジックの実行方法を処理します。通常のXML構成スキームとは別に、Spring BOOTスターターは、依存関係についてほとんど心配することなく、コードを最初から開始するために必要な依存関係を提供します。
参照
- 春の統合の概要
- 春の統合