Consuming a SOAP web service
This guide walks you through the process of consuming a SOAP-based web service with Spring.
What You Will Build
You will build a client that fetches country data from a remote, WSDL-based web service by using SOAP . You can find out more about the country service and run the service yourself by following this guide .
The service provides country data. You will be able to query data about a country based on its name.
Java 17 or later
You can also import the code straight into your IDE:
Like most Spring Getting Started guides , you can start from scratch and complete each step or you can bypass basic setup steps that are already familiar to you. Either way, you end up with working code.
To start from scratch , move on to Starting with Spring Initializr .
To skip the basics , do the following:
Download
and unzip the source repository for this guide, or clone it using
Git
:
git clone
https://github.com/spring-guides/gs-consuming-web-service.git
cd into
gs-consuming-web-service/initial
Jump ahead to Generate Domain Objects Based on a WSDL .
When you finish
, you can check your results against the code in
gs-consuming-web-service/complete
.
Follow the steps in the
companion guide
or clone the
repository
and run the service (for example, by using
mvn spring-boot:run
) from its
complete
directory. You can verify that it works by visiting
http://localhost:8080/ws/countries.wsdl
in your browser. If you do not do so, you will see a confusing exception in your build later from the JAXB tooling.
For all Spring applications, you should start with the Spring Initializr . The Initializr offers a fast way to pull in all the dependencies you need for an application and does a lot of the setup for you. This example needs only the Spring Web Services dependency.
You can use this pre-initialized project and click Generate to download a ZIP file. This project is configured to fit the examples in this tutorial.
To initialize the project:
Navigate to https://start.spring.io . This service pulls in all the dependencies you need for an application and does most of the setup for you.
Choose either Gradle or Maven and the language you want to use. This guide assumes that you chose Java.
Click Dependencies and select Spring Web Services .
Click Generate .
Download the resulting ZIP file, which is an archive of a web application that is configured with your choices.
The build files created by the Spring Initializr need quite a bit of work for this guide. Also, the modifications to
pom.xml
(for Maven) and
build.gradle
(for Gradle) differ substantially.
Maven
For Maven, you need to add a dependency, a profile, and a WSDL generation plugin.
The following listing shows the dependency you need to add in 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>
The Generate Domain Objects Based on a WSDL section describes the WSDL generation plugin.
The following listing shows the final
pom.xml
file:
<?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.2.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
For Gradle, you need to add a dependency, a configuration, a
bootJar
section, and a WSDL generation plugin.
The following listing shows the dependency you need to add in 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'
The Generate Domain Objects Based on a WSDL section describes the WSDL generation plugin.
The following listing shows the final
build.gradle
file:
The interface to a SOAP web service is captured in
WSDL
. JAXB provides a way to generate Java classes from WSDL (or rather, the XSD contained in the
<Types/>
section of the WSDL). You can find the WSDL for the country service at
http://localhost:8080/ws/countries.wsdl
.
To generate Java classes from the WSDL in Maven, you need the following plugin setup:
<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>
This setup generates classes for the WSDL found at the specified URL, putting those classes in the
com.example.consumingwebservice.wsdl
package. To generate that code run,
./mvnw compile
and then look in
target/generated-sources
if you want to check that it worked.
To do the same with Gradle, you need the following in your build file:
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
As Gradle does not (yet) have a JAXB plugin, it involves an Ant task, which makes it a bit more complex than in Maven. To generate that code run
./gradlew compileJava
and then look in
build/generated-sources
if you want to check that it worked.
In both Maven and Gradle, the JAXB domain object generation process has been wired into the build tool’s lifecycle, so you need not run any extra steps once you have a successful build.
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;
In this method, both the
GetCountryRequest
and the
GetCountryResponse
classes are derived from the WSDL and were generated in the JAXB generation process (described in
Generate Domain Objects Based on a WSDL
). It creates the
GetCountryRequest
request object and sets it up with the
country
parameter (the name of the country). After printing out the country name, it uses the
WebServiceTemplate
supplied by the
WebServiceGatewaySupport
base class to do the actual SOAP exchange. It passes the
GetCountryRequest
request object (as well as a
SoapActionCallback
to pass on a
SOAPAction
header with the request) as the WSDL described that it needed this header in the
<soap:operation/>
elements. It casts the response into a
GetCountryResponse
object, which is then returned.
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;
The marshaller
is pointed at the collection of generated domain objects and will use them to both serialize and deserialize between XML and POJOs.
The countryClient
is created and configured with the URI of the country service shown earlier. It is also configured to use the JAXB marshaller.
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());
Build an executable JAR
You can run the application from the command line with Gradle or Maven. You can also build a single executable JAR file that contains all the necessary dependencies, classes, and resources and run that. Building an executable jar makes it easy to ship, version, and deploy the service as an application throughout the development lifecycle, across different environments, and so forth.
If you use Gradle, you can run the application by using ./gradlew bootRun
. Alternatively, you can build the JAR file by using ./gradlew build
and then run the JAR file, as follows: