原文鏈接: http://spring.io/guides/gs/consuming-web-service/
調用SOAP web service
本指南將指導你使用Spring調用一個基於SOAP的web service的整個過程。
指南內容
你將構建一個客戶端,使用SOAP用來從遠端的基於WSDL的web service獲取天氣數據。請訪問http://wiki.cdyne.com/index.php/CDYNE_Weather進一步獲取該天氣服務的信息。
該服務根據郵編返回天氣預測。你可以使用自己的郵編。
准備事項
如何完成該指南
如同大多數的示例教程一樣,你可以從頭開始並完成每個步驟,或者你也可以跳過已經熟悉的基礎章節。無論怎樣,最終你要得到可以工作的代碼。
想從頭開始,請移動到使用Gradle構建章節。
想跳過基礎部分,請做以下事情:
當完成后,你可以使用gs-consuming-web-service/complete
目錄中的代碼檢查你的結果。
首先你要設置一個基本的build腳本。當構建Spring應用程序時,你可以使用任何構建系統,但是這里只包括了使用Maven和Gradle的代碼。如果你兩者都不熟悉,請訪問使用Gradle構建Java項目或使用Maven構建Java項目。
創建目錄結構
在你選擇的存放項目的目錄中,創建如下的子目錄結構。例如,在*nix系統中使用mkdir -p src/main/java/hello
。
1
2
3
4
|
└── src └── main └── java └── hello
|
創建Gradle 構建文件
下面是一個初始的Gradle build文件。
build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
configurations { jaxb } buildscript { repositories { maven { url "http://repo.spring.io/libs-release" } mavenLocal() mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:1.1.6.RELEASE") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'idea' apply plugin: 'spring-boot' repositories { mavenLocal() mavenCentral() maven { url 'http://repo.spring.io/libs-release' } } // tag::wsdl[] task genJaxb { ext.sourcesDir = "${buildDir}/generated-sources/jaxb" ext.classesDir = "${buildDir}/classes/jaxb" ext.schema = "http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl" outputs.dir classesDir doLast() { project.ant { taskdef name: "xjc", classname: "com.sun.tools.xjc.XJCTask", classpath: configurations.jaxb.asPath mkdir(dir: sourcesDir) mkdir(dir: classesDir) xjc(destdir: sourcesDir, schema: schema, package: "hello.wsdl") { arg(value: "-wsdl") produces(dir: sourcesDir, includes: "**/*.java") } javac(destdir: classesDir, source: 1.6, target: 1.6, debug: true, debugLevel: "lines,vars,source", classpath: configurations.jaxb.asPath) { src(path: sourcesDir) include(name: "**/*.java") include(name: "*.java") } copy(todir: classesDir) { fileset(dir: sourcesDir, erroronmissingdir: false) { exclude(name: "**/*.java") } } } } } // end::wsdl[] dependencies { compile("org.springframework.boot:spring-boot-starter") compile("org.springframework.ws:spring-ws-core") compile(files(genJaxb.classesDir).builtBy(genJaxb)) jaxb "com.sun.xml.bind:jaxb-xjc:2.1.7" } jar { from genJaxb.classesDir } task wrapper(type: Wrapper) { gradleVersion = '1.11' } task afterEclipseImport { dependsOn genJaxb }
|
Spring Boot gradle插件提供了很多便利的特性:
將classpath中的所有jar包構建單個可運行的jar包,從而更容易執行和傳播服務。
搜索public static void main()
方法並標記為可運行的類。
提供了一個內置的依賴管理器,設置依賴版本以匹配Spring Boot依賴。你可以覆蓋為任何你希望的版本,但默認會使用Boot選擇的版本。
使用Maven構建
首先你需要設置一個基本的構建腳本。你可以使用任何構建系統來構建Spring應用程序,但這里包含了Maven的代碼。如果你對Maven不熟,請訪問使用Maven構建Java項目。
創建目錄結構
在你選擇的存放項目的目錄中,創建如下的子目錄結構。例如,在*nix系統中使用mkdir -p src/main/java/hello
。
1
2
3
4
|
└── src └── main └── java └── hello
|
pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.springframework</groupId> <artifactId>gs-consuming-web-service</artifactId> <version>0.1.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.1.6.RELEASE</version> </parent> <properties> <!-- use UTF-8 for everything --> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.ws</groupId> <artifactId>spring-ws-core</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <!-- tag::wsdl[] --> <plugin> <groupId>org.jvnet.jaxb2.maven2</groupId> <artifactId>maven-jaxb2-plugin</artifactId> <executions> <execution> <goals> <goal>generate</goal> </goals> </execution> </executions> <configuration> <schemaLanguage>WSDL</schemaLanguage> <generatePackage>hello.wsdl</generatePackage> <forceRegenerate>true</forceRegenerate> <schemas> <schema> <url>http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl</url> </schema> </schemas> </configuration> </plugin> <!-- end::wsdl[] --> </plugins> </build> <repositories> <repository> <id>spring-releases</id> <name>Spring Releases</name> <url>http://repo.spring.io/libs-release</url> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-releases</id> <url>http://repo.spring.io/libs-release</url> </pluginRepository> </pluginRepositories> </project>
|
注意:你可能注意到我們指定了maven-complier-plugin的版本。通常並不推薦這樣做。這里主要是為了解決我們的CI系統默認運行在該插件的早期版本(java5之前)的一個問題。
Spring Boot Maven插件提供了很多便利的特性:
將classpath中的所有jar包構建單個可運行的jar包,從而更容易執行和傳播服務。
搜索public static void main()
方法並標記為可運行的類。
提供了一個內置的依賴管理器,設置依賴版本以匹配Spring Boot依賴。你可以覆蓋為任何你希望的版本,但默認會使用Boot選擇的版本。
使用Spring工具集構建
如果你擁有Spring工具集,只需簡單的直接導入該指南。
注意:如果你閱讀過生成SOAP web service,你可能會疑惑為什么該指南沒有使用spring-boot-starter-ws?這是因為Spring Boot Starter只用於服務器端程序。Starter提供了諸如嵌入式Tomcat等功能,而服務調用則不需要這些。
基於WSDL生成領域對象
SOAP web service的接口描述在WSDL文件中。JAXB提供了一個簡單的方式來從WSDL(或者WSDL中包含在<Types/>
節點中的XSD)生成Java類。可以訪問http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl獲取該天氣服務的WSDL。
你需要下列插件來使用maven從WSDL生成Java類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<plugin> <groupId>org.jvnet.jaxb2.maven2</groupId> <artifactId>maven-jaxb2-plugin</artifactId> <executions> <execution> <goals> <goal>generate</goal> </goals> </execution> </executions> <configuration> <schemaLanguage>WSDL</schemaLanguage> <generatePackage>hello.wsdl</generatePackage> <forceRegenerate>true</forceRegenerate> <schemas> <schema> <url>http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl</url> </schema> </schemas> </configuration> </plugin>
|
該代碼將通過指定的WSDL的URL生成類,並放置在hello.wsdl
包中。
你也可以使用下列代碼在Gradle中完成同樣的事:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
task genJaxb { ext.sourcesDir = "${buildDir}/generated-sources/jaxb" ext.classesDir = "${buildDir}/classes/jaxb" ext.schema = "http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl" outputs.dir classesDir doLast() { project.ant { taskdef name: "xjc", classname: "com.sun.tools.xjc.XJCTask", classpath: configurations.jaxb.asPath mkdir(dir: sourcesDir) mkdir(dir: classesDir) xjc(destdir: sourcesDir, schema: schema, package: "hello.wsdl") { arg(value: "-wsdl") produces(dir: sourcesDir, includes: "**/*.java") } javac(destdir: classesDir, source: 1.6, target: 1.6, debug: true, debugLevel: "lines,vars,source", classpath: configurations.jaxb.asPath) { src(path: sourcesDir) include(name: "**/*.java") include(name: "*.java") } copy(todir: classesDir) { fileset(dir: sourcesDir, erroronmissingdir: false) { exclude(name: "**/*.java") } } } } }
|
由於gradle還沒有jaxb插件,所以它調用了一個ant任務,代碼看起來比maven稍顯復雜。 在maven和gradle兩個示例中,JAXB領域對象生成過程被包括在構建工具的生命周期中,所以無需額外步驟來運行。
創建天氣服務客戶端
創建一個web service客戶端,你只需要擴展WebServiceGatewaySupport
類並編寫操作代碼:
src/main/java/hello/WeatherClient.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
package hello; import java.text.SimpleDateFormat; import org.springframework.ws.client.core.support.WebServiceGatewaySupport; import org.springframework.ws.soap.client.core.SoapActionCallback; import hello.wsdl.Forecast; import hello.wsdl.ForecastReturn; import hello.wsdl.GetCityForecastByZIP; import hello.wsdl.GetCityForecastByZIPResponse; import hello.wsdl.Temp; public class WeatherClient extends WebServiceGatewaySupport { public GetCityForecastByZIPResponse getCityForecastByZip(String zipCode) { GetCityForecastByZIP request = new GetCityForecastByZIP(); request.setZIP(zipCode); System.out.println(); System.out.println("Requesting forecast for " + zipCode); GetCityForecastByZIPResponse response = (GetCityForecastByZIPResponse) getWebServiceTemplate().marshalSendAndReceive( request, new SoapActionCallback( "http://ws.cdyne.com/WeatherWS/GetCityForecastByZIP")); return response; } public void printResponse(GetCityForecastByZIPResponse response) { ForecastReturn forecastReturn = response.getGetCityForecastByZIPResult(); if (forecastReturn.isSuccess()) { System.out.println(); System.out.println("Forecast for " + forecastReturn.getCity() + ", " + forecastReturn.getState()); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); for (Forecast forecast : forecastReturn.getForecastResult().getForecast()) { System.out.print(format.format(forecast.getDate().toGregorianCalendar().getTime())); System.out.print(" "); System.out.print(forecast.getDesciption()); System.out.print(" "); Temp temperature = forecast.getTemperatures(); System.out.print(temperature.getMorningLow() + "\u00b0-" + temperature.getDaytimeHigh() + "\u00b0 "); System.out.println(); } } else { System.out.println("No forecast received"); } } }
|
該客戶端包含了兩個方法。getCityForecastByZip
用於實際的SOAP交換;printResponse
打印收到的響應結果。我們重點關注第一個方法。
在該方法中,GetCityForecastByZIP
及GetCityForecastByZIPResponse
類衍生於WSDL中,被前一個步驟描述過的JAXB生成。該方法創建了GetCityForecastByZIP
請求對象並設置了zipCode
參數。打印出郵編后,使用WebServiceGatewaySupport基類提供的WebServiceTemplate來進行實際的SOAP交換。它傳入GetCityForecastByZIP
請求對象,以及一個SoapActionCallback
來傳入SOAPAction頭,因為WSDL說明其需要在<soap:operation/>
元素中使用該頭。該方法將返回值轉換為GetCityForecastByZIPResponse
對象,然后返回該對象。
配置web service組件
Spring WS使用了Spring框架的OXM模塊。該模塊擁有Jaxb2Marshaller
類來序列化和反序列化XML請求。
src/main/java/hello/WeatherConfiguration.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package hello; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.oxm.jaxb.Jaxb2Marshaller; @Configuration public class WeatherConfiguration { @Bean public Jaxb2Marshaller marshaller() { Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); marshaller.setContextPath("hello.wsdl"); return marshaller; } @Bean public WeatherClient weatherClient(Jaxb2Marshaller marshaller) { WeatherClient client = new WeatherClient(); client.setDefaultUri("http://wsf.cdyne.com/WeatherWS/Weather.asmx"); client.setMarshaller(marshaller); client.setUnmarshaller(marshaller); return client; } }
|
marshaller
被指向了生成的領域對象集合,將使用這些對象來實現XML和POJO之間的序列化和反序列化。
我們使用了上面顯示的天氣服務URI創建和配置了weatherClient
。他也被配置使用JAXB marshaller。
生成可執行的應用程序
該應用程序被打包后可運行與命令行,傳入一個郵編則會得到一個天氣預報。
src/main/java/hello/Application.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package hello; import org.springframework.boot.SpringApplication; import org.springframework.context.ApplicationContext; import hello.wsdl.GetCityForecastByZIPResponse; public class Application { public static void main(String[] args) { ApplicationContext ctx = SpringApplication.run(WeatherConfiguration.class, args); WeatherClient weatherClient = ctx.getBean(WeatherClient.class); String zipCode = "94304"; if (args.length > 0) { zipCode = args[0]; } GetCityForecastByZIPResponse response = weatherClient.getCityForecastByZip(zipCode); weatherClient.printResponse(response); } }
|
main()
方法調用了SpringApplication
輔助方法,並向其run()
方法傳入了WeatherConfiguration.class
參數。這會使Spring從WeatherConfiguration
中讀取注解元數據,並作為Spring應用程序上下文中的一個組件進行管理。
注意:該應用程序硬編碼了郵編94304,Palo Alto, CA。在該指南的最后,你可以看到如何添加不同的郵編而不用修改代碼。
構建可執行的jar包
你可以創建一個包含所有必須的依賴,類,及資源的可執行的JAR文件。這很方便傳輸,版本管理以及獨立於部署生命周期來部署服務,跨不同的環境,諸如此類。
然后你可以運行WAR文件:
1
|
java -jar build/libs/gs-consuming-web-service-0.1.0.jar
|
如果你使用的是maven,你可以使用mvn spring-boot:run
來運行程序,或者你可以使用mvn clean package
構建JAR文件,並使用下面命令來運行:
1
|
java -jar target/gs-consuming-web-service-0.1.0.jar
|
注意:上面的產出物是一個可運行JAR文件。你也可以創建一個經典的WAR文件。
運行服務
如果使用的是Gradle,可以使用以下命令來運行服務:
1
|
gradlew clean build && java -jar build/libs/gs-consuming-web-service-0.1.0.jar
|
注意:如果你使用的是Maven,可以使用以下命令來運行服務:mvn clean package && java -jar target/gs-consuming-web-service-0.1.0.jar
。
你也可以通過Gradle直接運行該程序:
注意:使用mvn的話,命令是mvn spring-boot:run
。
可以看到日志輸出。該服務應該在幾秒鍾內啟動並運行起來。
1
|
Requesting forecast for94304ForecastforPaloAlto, CA2013-01-03PartlyCloudy°-57°2013-01-04PartlyCloudy41°-58°2013-01-05PartlyCloudy41°-59°2013-01-06PartlyCloudy44°-56°2013-01-07PartlyCloudy41°-60°2013-01-08PartlyCloudy42°-60°2013-01-09PartlyCloudy43°-58°
|
你也可以使用不同的郵編:java -jar build/libs/gs-consuming-web-service-0.1.0.jar 34769
1
|
Requesting forecast for34769ForecastforSaintCloud, FL2014-02-18Sunny51°-79°2014-02-19Sunny55°-81°2014-02-20Sunny59°-84°2014-02-21PartlyCloudy63°-85°2014-02-22PartlyCloudy63°-84°2014-02-23PartlyCloudy63°-82°2014-02-24PartlyCloudy62°-80°
|
總結
恭喜你!你開發了一個客戶端來使用Spring調用一個基於SOAP的web service。