SOAP Web サービスの使用

このガイドでは、Spring で SOAP ベースの Web サービスを利用するプロセスを順を追って説明します。

構築するもの

SOAP [Wikipedia] を使用して、WSDL ベースの Web サービスである リモートから国データをフェッチするクライアントを構築します。このガイドに従って、国別サービスの詳細を確認し、自分でサービスを実行できます。

このサービスは国データを提供します。名前に基づいて国に関するデータを照会できます。

必要なもの

本ガイドの完成までの流れ

ほとんどの Spring 入門ガイドと同様に、最初から始めて各ステップを完了するか、すでに慣れている場合は基本的なセットアップステップをバイパスできます。いずれにしても、最終的に動作するコードになります。

最初から始めるには、Spring Initializr から開始に進みます。

基本スキップするには、次の手順を実行します。

完了したときは、gs-consuming-web-service/complete のコードに対して結果を確認できます。

ターゲット Web サービスをローカルで実行する

関連ガイドの手順に従うか、リポジトリ [GitHub] (英語) のクローンを作成し、その complete ディレクトリからサービスを (たとえば、mvn spring-boot:run を使用して) 実行します。ブラウザーで http://localhost:8080/ws/countries.wsdl にアクセスすると、機能することを確認できます。そうしないと、後で JAXB ツールからビルドに紛らわしい例外が表示されることになります。

Spring Initializr から開始

すべての Spring アプリケーションは、Spring Initializr (Eclipse の場合は Spring スタータープロジェクト) から始める必要があります。Initializr は、アプリケーションに必要なすべての依存関係をすばやく取り込む方法を提供し、多くの設定を行います。この例では、Spring Web Services 依存関係のみが必要です。

IDE を使用する場合はプロジェクト作成ウィザードを使用します。IDE を使用せずにコマンドラインなどで開発する場合は、この事前に初期化されたプロジェクトからプロジェクトを ZIP ファイルとしてダウンロードできます。このプロジェクトは、このチュートリアルの例に合うように構成されています。

プロジェクトを初期化するには:

  1. IDE のメニューまたはブラウザーから Spring Initializr を開きます。アプリケーションに必要なすべての依存関係を取り込み、ほとんどのセットアップを行います。

  2. Gradle または Maven のいずれかと、使用する言語を選択します。このガイドは、Java を選択したことを前提としています。

  3. 依存関係をクリックして、Spring Web Services を選択します。

  4. 生成をクリックします。

  5. 結果の ZIP ファイルをダウンロードします。これは、選択して構成された Web アプリケーションのアーカイブです。

EclipseIntelliJ のような IDE は新規プロジェクト作成ウィザードから Spring Initializr の機能が使用できるため、手動での ZIP ファイルのダウンロードやインポートは不要です。
プロジェクトを Github からフォークして、IDE または他のエディターで開くこともできます。

ビルドファイルを変更する

Spring Initializr によって作成されたビルドファイルは、このガイドのためにかなりの作業が必要です。また、pom.xml (Maven の場合)と build.gradle (Gradle の場合)への変更は大幅に異なります。

Maven

Maven の場合、依存関係、プロファイル、WSDL 生成プラグインを追加する必要があります。

次のリストは、Maven に追加する必要がある依存関係を示しています。

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

WSDL に基づいてドメインオブジェクトを生成するセクションでは、WSDL 生成プラグインについて説明します。

次のリストは、最終的な 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>consuming-web-service-complete</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>consuming-web-service-complete</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>17</java.version>
	</properties>

	<dependencies>
		<!-- tag::dependency[] -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web-services</artifactId>
			<exclusions>
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-tomcat</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<!-- end::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>
			<!-- tag::wsdl[] -->
			<plugin>
				<groupId>com.sun.xml.ws</groupId>
				<artifactId>jaxws-maven-plugin</artifactId>
				<version>3.0.0</version>
				<executions>
					<execution>
						<goals>
							<goal>wsimport</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<packageName>com.example.consumingwebservice.wsdl</packageName>
					<wsdlUrls>
						<wsdlUrl>http://localhost:8080/ws/countries.wsdl</wsdlUrl>
					</wsdlUrls>
					<sourceDestDir>${sourcesDir}</sourceDestDir>
					<destDir>${classesDir}</destDir>
					<extension>true</extension>
				</configuration>
			</plugin>
			<!-- end::wsdl[] -->
		</plugins>
	</build>

</project>

Gradle

Gradle の場合、依存関係、構成、bootJar セクション、WSDL 生成プラグインを追加する必要があります。

次のリストは、Gradle に追加する必要がある依存関係を示しています。

implementation ('org.springframework.boot:spring-boot-starter-web-services') {
	exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
}
jaxws 'com.sun.xml.ws:jaxws-tools:3.0.0',
		'jakarta.xml.ws:jakarta.xml.ws-api:3.0.0',
		'jakarta.xml.bind:jakarta.xml.bind-api:3.0.0',
		'jakarta.activation:jakarta.activation-api:2.0.0',
		'com.sun.xml.ws:jaxws-rt:3.0.0'

Tomcat が除外されていることに注意してください。このビルドで Tomcat の実行が許可されている場合、国データを提供する Tomcat インスタンスとポートの衝突が発生します。

このポートの衝突により、最初のプロジェクトは開始できません。この問題は、server.port=8081 の単一プロパティを持つ application.properties ファイルを追加することで修正できます。初期プロジェクトは出発点として存在するため、それを実行する作業はスキップできます。

WSDL に基づいてドメインオブジェクトを生成するセクションでは、WSDL 生成プラグインについて説明します。

次のリストは、最終的な build.gradle ファイルを示しています。

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.3.0'
	id 'io.spring.dependency-management' version '1.1.5'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
	sourceCompatibility = '17'
}
ext.jaxwsSourceDir = "${buildDir}/generated/sources/jaxws"

// tag::configurations[]
configurations {
	jaxws
}
// end::configurations[]

repositories {
	mavenCentral()
}

// tag::wsdl[]
task wsimport {
	description = 'Generate classes from wsdl using wsimport'

	doLast {
		project.mkdir(jaxwsSourceDir)
		ant {
			taskdef(name: 'wsimport',
					classname: 'com.sun.tools.ws.ant.WsImport',
					classpath: configurations.jaxws.asPath
			)
			wsimport(
					keep: true,
					destdir: jaxwsSourceDir,
					extension: "true",
					verbose: true,
					wsdl: "http://localhost:8080/ws/countries.wsdl",
					xnocompile: true,
					package: "com.example.consumingwebservice.wsdl") {
				xjcarg(value: "-XautoNameResolution")
			}
		}
	}
}

sourceSets {
	main {
		java.srcDirs += jaxwsSourceDir
	}
}

compileJava {
	dependsOn wsimport
}
// end::wsdl[]

dependencies {
// tag::dependency[]
	implementation ('org.springframework.boot:spring-boot-starter-web-services') {
		exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
	}
	jaxws 'com.sun.xml.ws:jaxws-tools:3.0.0',
			'jakarta.xml.ws:jakarta.xml.ws-api:3.0.0',
			'jakarta.xml.bind:jakarta.xml.bind-api:3.0.0',
			'jakarta.activation:jakarta.activation-api:2.0.0',
			'com.sun.xml.ws:jaxws-rt:3.0.0'
// end::dependency[]
	testImplementation('org.springframework.boot:spring-boot-starter-test')
}

tasks.named('test') {
	useJUnitPlatform()
}

WSDL に基づいてドメインオブジェクトを生成する

SOAP Web サービスへのインターフェースは、WSDL [Wikipedia] (英語) でキャプチャーされます。JAXB は、WSDL(または、WSDL の <Types/> セクションに含まれる XSD)から Java クラスを生成する方法を提供します。http://localhost:8080/ws/countries.wsdl で、国別サービスの WSDL を見つけることができます。

Maven の WSDL から Java クラスを生成するには、次のプラグインのセットアップが必要です。

<plugin>
	<groupId>com.sun.xml.ws</groupId>
	<artifactId>jaxws-maven-plugin</artifactId>
	<version>3.0.0</version>
	<executions>
		<execution>
			<goals>
				<goal>wsimport</goal>
			</goals>
		</execution>
	</executions>
	<configuration>
		<packageName>com.example.consumingwebservice.wsdl</packageName>
		<wsdlUrls>
			<wsdlUrl>http://localhost:8080/ws/countries.wsdl</wsdlUrl>
		</wsdlUrls>
		<sourceDestDir>${sourcesDir}</sourceDestDir>
		<destDir>${classesDir}</destDir>
		<extension>true</extension>
	</configuration>
</plugin>

このセットアップでは、指定された URL で見つかった WSDL のクラスを生成し、それらのクラスを com.example.consumingwebservice.wsdl パッケージに入れます。そのコードを生成するには、./mvnw compile を実行し、それが機能することを確認したい場合は target/generated-sources を調べます。

Gradle で同じことを行うには、ビルドファイルに次のものが必要です。

task wsimport {
  description = 'Generate classes from wsdl using wsimport'

  doLast {
    project.mkdir(jaxwsSourceDir)
    ant {
      taskdef(name: 'wsimport',
          classname: 'com.sun.tools.ws.ant.WsImport',
          classpath: configurations.jaxws.asPath
      )
      wsimport(
          keep: true,
          destdir: jaxwsSourceDir,
          extension: "true",
          verbose: true,
          wsdl: "http://localhost:8080/ws/countries.wsdl",
          xnocompile: true,
          package: "com.example.consumingwebservice.wsdl") {
        xjcarg(value: "-XautoNameResolution")
      }
    }
  }
}

sourceSets {
  main {
    java.srcDirs += jaxwsSourceDir
  }
}

compileJava {
  dependsOn wsimport
}

Gradle には(まだ)JAXB プラグインがないため、Ant タスクが関係するため、Maven よりも少し複雑になります。そのコードを生成するには、./gradlew compileJava を実行し、それが機能したことを確認したい場合は build/generated-sources を調べます。

Maven と Gradle の両方で、JAXB ドメインオブジェクト生成プロセスがビルドツールのライフサイクルに組み込まれているため、ビルドが成功したら追加の手順を実行する必要はありません。

カントリーサービスクライアントを作成する

Web サービスクライアントを作成するには、次の例(src/main/java/com/example/consumingwebservice/CountryClient.java から)が示すように、WebServiceGatewaySupport (Javadoc) クラスを継承し、操作をコーディングする必要があります。

package com.example.consumingwebservice;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
import org.springframework.ws.soap.client.core.SoapActionCallback;

import com.example.consumingwebservice.wsdl.GetCountryRequest;
import com.example.consumingwebservice.wsdl.GetCountryResponse;

public class CountryClient extends WebServiceGatewaySupport {

  private static final Logger log = LoggerFactory.getLogger(CountryClient.class);

  public GetCountryResponse getCountry(String country) {

    GetCountryRequest request = new GetCountryRequest();
    request.setName(country);

    log.info("Requesting location for " + country);

    GetCountryResponse response = (GetCountryResponse) getWebServiceTemplate()
        .marshalSendAndReceive("http://localhost:8080/ws/countries", request,
            new SoapActionCallback(
                "http://spring.io/guides/gs-producing-web-service/GetCountryRequest"));

    return response;
  }

}

クライアントには、実際の SOAP 交換を行う 1 つのメソッド(getCountry)が含まれています。

この方法では、GetCountryRequest クラスと GetCountryResponse クラスの両方が WSDL から派生し、JAXB 生成プロセス (WSDL に基づいてドメインオブジェクトを生成するで説明) で生成されます。GetCountryRequest リクエストオブジェクトを作成し、country パラメーター (国名) を使用して設定します。国名を出力した後は、WebServiceGatewaySupport 基本クラスによって提供される WebServiceTemplate (Javadoc) を使用して、実際の SOAP 交換を行います。WSDL が <soap:operation/> 要素にこのヘッダーが必要であると記述しているように、GetCountryRequest リクエストオブジェクト (リクエストとともに SOAPAction [W3C] (英語) ヘッダーを渡すための SoapActionCallback ) を渡します。レスポンスを GetCountryResponse オブジェクトにキャストし、そのオブジェクトを返します。

Web サービスコンポーネントの構成

Spring WS は Spring Framework の OXM モジュールを使用します。このモジュールには、次の例(src/main/java/com/example/consumingwebservice/CountryConfiguration.java から)が示すように、XML リクエストをシリアライズおよびデシリアライズする Jaxb2Marshaller があります

package com.example.consumingwebservice;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;

@Configuration
public class CountryConfiguration {

  @Bean
  public Jaxb2Marshaller marshaller() {
    Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
    // this package must match the package in the <generatePackage> specified in
    // pom.xml
    marshaller.setContextPath("com.example.consumingwebservice.wsdl");
    return marshaller;
  }

  @Bean
  public CountryClient countryClient(Jaxb2Marshaller marshaller) {
    CountryClient client = new CountryClient();
    client.setDefaultUri("http://localhost:8080/ws");
    client.setMarshaller(marshaller);
    client.setUnmarshaller(marshaller);
    return client;
  }

}

marshaller は、生成されたドメインオブジェクトのコレクションを指しており、使用して XML と POJO の間で直列化と非直列化の両方を行います。

countryClient は、前に示した国別サービスの URI を使用して作成および構成されます。また、JAXB マーシャラーを使用するように構成されています。

アプリケーションの実行

このアプリケーションは、次のリスト(src/main/java/com/example/consumingwebservice/ConsumingWebServiceApplication.java から)に示すように、コンソールから実行して特定の国名のデータを取得するようにパッケージ化されています。

package com.example.consumingwebservice;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import com.example.consumingwebservice.wsdl.GetCountryResponse;

@SpringBootApplication
public class ConsumingWebServiceApplication {

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

  @Bean
  CommandLineRunner lookup(CountryClient countryClient) {
    return args -> {
      String country = "Spain";

      if (args.length > 0) {
        country = args[0];
      }
      GetCountryResponse response = countryClient.getCountry(country);
      System.err.println(response.getCountry().getCurrency());
    };
  }

}

main() メソッドは SpringApplication (Javadoc) ヘルパークラスに従い、run() メソッドの引数として CountryConfiguration.class を提供します。これにより、Spring は CountryConfiguration からアノテーションメタデータを読み取り、Spring アプリケーションコンテキストのコンポーネントとして管理するように指示されます。

このアプリケーションは、「スペイン」を検索するためにハードコードされています。このガイドの後半では、コードを編集せずに別のシンボルを入力する方法について説明します。

実行可能 JAR を構築する

コマンドラインから Gradle または Maven を使用してアプリケーションを実行できます。必要なすべての依存関係、クラス、リソースを含む単一の実行可能 JAR ファイルを構築して実行することもできます。実行可能な jar を構築すると、開発ライフサイクル全体、さまざまな環境などで、アプリケーションとしてサービスを簡単に提供、バージョン管理、デプロイできます。

Gradle を使用する場合、./gradlew bootRun を使用してアプリケーションを実行できます。または、次のように、./gradlew build を使用して JAR ファイルをビルドしてから、JAR ファイルを実行できます。

java -jar build/libs/gs-consuming-web-service-0.1.0.jar

Maven を使用する場合、./mvnw spring-boot:run を使用してアプリケーションを実行できます。または、次のように、./mvnw clean package で JAR ファイルをビルドしてから、JAR ファイルを実行できます。

java -jar target/gs-consuming-web-service-0.1.0.jar
ここで説明する手順は、実行可能な JAR を作成します。クラシック WAR ファイルを作成することもできます。

ロギング出力が表示されます。サービスは数秒以内に起動して実行されるはずです。

次のリストは、初期レスポンスを示しています。

Requesting country data for Spain

<getCountryRequest><name>Spain</name>...</getCountryRequest>

次のコマンドを実行して、別の国に接続できます。

java -jar build/libs/gs-consuming-web-service-0.1.0.jar Poland

その後、レスポンスは次のように変わります。

Requesting location for Poland

<getCountryRequest><name>Poland</name>...</getCountryRequest>

要約

おめでとう! Spring で SOAP ベースの Web サービスを使用するクライアントを開発しました。

関連事項

次のガイドも役立ちます。

新しいガイドを作成したり、既存のガイドに貢献したいですか? 投稿ガイドラインを参照してください [GitHub] (英語)

すべてのガイドは、コード用の ASLv2 ライセンス、およびドキュメント用の Attribution、NoDerivatives creative commons ライセンス (英語) でリリースされています。

コードを入手する