1概要

場合によっては、システムをいくつかのプロセスに分解し、それぞれがアプリケーションの異なる側面に責任を負う必要があります。これらのシナリオでは、プロセスの1つが他のプロセスから同期的にデータを取得する必要があることは珍しくありません。

Spring Frameworkは

Spring Remoting

と呼ばれる包括的なツールを提供しており、少なくともある程度はローカルで利用可能であるかのようにリモートサービスを呼び出すことができます。

この記事では、Springの

HTTP invoker

をベースにしたアプリケーションをセットアップします。このアプリケーションは、ネイティブのJavaシリアル化とHTTPを利用して、クライアントとサーバー・アプリケーション間でリモート・メソッド呼び出しを提供します。


2サービス定義

ユーザーがタクシーで乗車を予約できるシステムを実装する必要があるとしましょう。

この目標を達成するために、

2つの異なるアプリケーション

を構築することを選択したとします。

  • タクシーの要求がすることができるかどうかを確認するための予約エンジンアプリケーション

役立った、そして
** 顧客が自分の乗り物を予約することを可能にするフロントエンドWebアプリケーション

タクシーの空室状況を確認する


2.1. サービスインターフェース


HTTP呼び出し側で

Spring Remoting

を使用するときは、

リモート呼び出しの専門性をカプセル化するプロキシをクライアント側とサーバー側の両方でSpringに作成させるために__リモート呼び出し可能サービスをインタフェースを通じて定義する必要があります。それでは、タクシーを予約できるサービスのインターフェースから始めましょう。

public interface CabBookingService {
    Booking bookRide(String pickUpLocation) throws BookingException;
}

サービスがキャブを割り当てることができるとき、それは予約コードと共に

Booking

オブジェクトを返します。 SpringのHTTP呼び出し側はそのインスタンスをサーバーからクライアントに転送する必要があるため、

Booking

は直列化可能である必要があります。

public class Booking implements Serializable {
    private String bookingCode;

    @Override public String toString() {
        return format("Ride confirmed: code '%s'.", bookingCode);
    }

   //standard getters/setters and a constructor
}

サービスがタクシーを予約できない場合は、

BookingException

がスローされます。この場合、

Exception

がすでにそれを実装しているので、クラスを

Serializable

としてマークする必要はありません。

public class BookingException extends Exception {
    public BookingException(String message) {
        super(message);
    }
}


2.2. サービスの包装

引数、戻り値の型、および例外として使用されるすべてのカスタムクラスと共にサービスインターフェイスは、クライアントとサーバーの両方のクラスパスで使用できる必要があります。そのための最も効果的な方法の1つは、これらすべてを

.jar

ファイルにまとめて、後でサーバーとクライアントの__pom.xmlに依存関係として含めることができるようにすることです。

それでは、すべてのコードを「api」という専用のMavenモジュールに入れてみましょう。この例では、次のMaven座標を使用します。

<groupId>com.baeldung</groupId>
<artifactId>api</artifactId>
<version>1.0-SNAPSHOT</version>


3サーバーアプリケーション

Spring Bootを使ってサービスを公開する予約エンジンアプリケーションを作成しましょう。


3.1. Mavenの依存関係

まず、プロジェクトがSpring Bootを使用していることを確認する必要があります。

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

最新のSpring Bootバージョンhttps://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22org.springframework.boot%22%20AND%20a%3A%22spring-boot-parentを見つけることができます。 %22[ここ]

それならWebスターターモジュールが必要です。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

そして、前のステップで組み立てたサービス定義モジュールが必要です。

<dependency>
    <groupId>com.baeldung</groupId>
    <artifactId>api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>


3.2. サービス実施

まず、サービスのインターフェースを実装するクラスを定義します。

public class CabBookingServiceImpl implements CabBookingService {

    @Override public Booking bookPickUp(String pickUpLocation) throws BookingException {
        if (random() < 0.3) throw new BookingException("Cab unavailable");
        return new Booking(randomUUID().toString());
    }
}

これがありそうな実装であるとしましょう。ランダムな値のテストを使用して、利用可能なタクシーが見つかって予約コードが返された場合と、失敗した場合 – 利用可能なタクシーがないことを示すBookingExceptionがスローされる場合


3.3. サービスを公開する

次にコンテキスト内で

HttpInvokerServiceExporter

型のBeanを持つアプリケーションを定義する必要があります。これは、後でクライアントによって呼び出されるWebアプリケーション内のHTTPエントリポイントを公開するようにします。

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Server {

    @Bean(name = "/booking") HttpInvokerServiceExporter accountService() {
        HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
        exporter.setService( new CabBookingServiceImpl() );
        exporter.setServiceInterface( CabBookingService.class );
        return exporter;
    }

    public static void main(String[]args) {
        SpringApplication.run(Server.class, args);
    }
}

Springの

HTTP invoker

がHTTPエンドポイントURLの相対パスとして

HttpInvokerServiceExporter

Beanの名前を使用していることは注目に値する。

これで、サーバーアプリケーションを起動し、クライアントアプリケーションをセットアップしている間もサーバーアプリケーションを実行し続けることができます。


4クライアントアプリケーション

それでは、クライアントアプリケーションを書きましょう。


4.1. Mavenの依存関係

サーバー側で使用したのと同じサービス定義と同じSpring Bootバージョンを使用します。まだWebスターターの依存関係が必要ですが、組み込みコンテナーを自動的に開始する必要はないので、依存関係からTomcatスターターを除外することができます。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>


4.2. クライアント実装

クライアントを実装しましょう:

@Configuration
public class Client {

    @Bean
    public HttpInvokerProxyFactoryBean invoker() {
        HttpInvokerProxyFactoryBean invoker = new HttpInvokerProxyFactoryBean();
        invoker.setServiceUrl("https://localhost:8080/booking");
        invoker.setServiceInterface(CabBookingService.class);
        return invoker;
    }

    public static void main(String[]args) throws BookingException {
        CabBookingService service = SpringApplication
          .run(Client.class, args)
          .getBean(CabBookingService.class);
        out.println(service.bookRide("13 Seagate Blvd, Key Largo, FL 33037"));
    }
}


@ Bean

アノテーション付き呼び出し元の

__()


メソッドは、

HttpInvokerProxyFactoryBean

のインスタンスを作成します。リモートサーバーが応答するURLを

setServiceUrl()__メソッドで提供する必要があります。

サーバーに対して行ったのと同様に、

setServiceInterface()

メソッドを通じてリモートで呼び出したいサービスのインターフェースも提供する必要があります。


HttpInvokerProxyFactoryBean

はSpringの

FactoryBean

を実装しています。

FactoryBean

はBeanとして定義されていますが、Spring IoCコンテナはファクトリ自体ではなく、作成するオブジェクトをインジェクトします。

FactoryBean

の詳細については、/spring-factorybean[ファクトリビーンの記事]を参照してください。


main()

メソッドはスタンドアロンアプリケーションをブートストラップし、コンテキストから

CabBookingService

のインスタンスを取得します。内部的には、このオブジェクトは

HttpInvokerProxyFactoryBean

によって作成されたプロキシであり、リモート呼び出しの実行に関連するすべての技術を処理します。そのおかげで、サービス実装がローカルで利用可能だった場合と同じように、プロキシを簡単に使用できるようになりました。

タクシーが利用可能なときとそうでないときのクライアントの動作を確認するために、複数のリモート呼び出しを実行するためにアプリケーションを複数回実行しましょう。


5買い手責任負担

リモート呼び出しを可能にするテクノロジを扱うときには、注意すべき落とし穴がいくつかあります。


5.1. ネットワーク関連の例外に注意する

ネットワークとして信頼できないリソースを扱うときは、常に予期しないことを予想する必要があります。

ネットワークに問題があるか、サーバーがダウンしているために、クライアントがサーバーにアクセスできない状態で呼び出しを行っているとします。その場合、Spring Remotingは

RuntimeException.

である

RemoteAccessException

を発生させます。

そうすれば、コンパイラーはその呼び出しをtry-catchブロックに含めることを強制しませんが、ネットワークの問題を適切に管理するために、常にそれを行うことを検討する必要があります。


5.2. オブジェクトは参照によってではなく値によって転送されます


Spring Remoting HTTP

は、メソッドの引数と戻り値を整列化して、ネットワーク上に転送します。つまり、サーバーは提供された引数のコピーに基づいて動作し、クライアントはサーバーによって作成された結果のコピーに基づいて動作します。

したがって、たとえば、クライアントとサーバーの間に共有オブジェクトがないため、結果のオブジェクトでメソッドを呼び出すと、サーバー側の同じオブジェクトのステータスが変わることはありません。


5.3. きめ細かいインターフェースに注意する

ネットワーク境界を越えてメソッドを呼び出すのは、同じプロセス内のオブジェクトでメソッドを呼び出すよりもかなり時間がかかります。

このため、より面倒なインターフェースを犠牲にしても、より少ない対話でビジネストランザクションを完了できる粗いインターフェースを使用してリモートで呼び出すサービスを定義するのが通常は良い方法です。


6. 結論

この例では、Spring Remotingを使用してリモートプロセスを呼び出すのが簡単であることを確認しました。

このソリューションは、RESTやWebサービスのような他の広範なメカニズムよりもオープン性が少し劣りますが、Springですべてのコンポーネントが開発されているシナリオでは、それは実行可能ではるかに速い代替手段を表します。

いつものように、あなたはソースhttps://github.com/eugenp/tutorials/tree/master/spring-remoting/remoting-http[GitHubに乗って]を見つけるでしょう。