原文連接:http://spring.io/guides/gs/producing-web-service/html
生成SOAP web service
該指南將帶領你使用Spring建立一個基於SOAP的web service的整個過程。java
指南內容
你將建立一個服務,該服務經過一個基於WSDL的SOAP web service向外暴露歐洲國家的數據。git
注意:爲了簡化該示例,你將使用硬編碼方式嵌入英國,西班牙及波蘭。github
準備事項
15分鐘web
喜好的編輯器或IDEspring
JDK1.6或更高版本apache
Gradle 1.11+或Maven 3.0+編程
你也能夠直接經過該指南導入代碼,或經過Spring工具集(Spring Tool Suite,簡稱STS)經過網頁瀏覽代碼,從而幫助你學習該章節內容。源碼下載地址: https://github.com/spring-guides/gs-producing-web-service.git。api
如何完成該指南
如同大多數的示例教程同樣,你能夠從頭開始並完成每一個步驟,或者你也能夠跳過已經熟悉的基礎章節。不管怎樣,最終你要獲得能夠工做的代碼。spring-mvc
想從頭開始,請移動到使用Gradle構建章節。
想跳過基礎部分,請作如下事情:
下載並解壓該向導的源代碼,或者使用Git複製一份:
git clone https://github.com/spring-guides/gs-soap-service.git
切換到
gs-soap-service/initial
跳到添加Spring-WS依賴章節。
當完成後,你可使用gs-soap-service/complete
目錄中的代碼檢查你的結果。
使用Gradle構建
首先你要設置一個基本的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文件。
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 |
buildscript { repositories { mavenLocal() mavenCentral() maven { url "http://repo.spring.io/libs-release" } } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:1.1.5.RELEASE") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'idea' apply plugin: 'spring-boot' jar { baseName = 'gs-producing-web-service' version = '0.1.0' } repositories { mavenLocal() mavenCentral() maven { url "http://repo.spring.io/libs-release" } } dependencies { compile("org.springframework.boot:spring-boot-starter-web") } task wrapper(type: Wrapper) { gradleVersion = '1.11' } |
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 |
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 |
<?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-producting-web-service</artifactId> <version>0.1.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.1.5.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <properties> <!-- use UTF-8 for everything --> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <start-class>hello.Application</start-class> </properties> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
注意:你可能注意到咱們指定了maven-complier-plugin的版本。一般並不推薦這樣作。這裏主要是爲了解決咱們的CI系統默認運行在該插件的早期版本(java5以前)的一個問題。
Spring Boot Maven插件提供了不少便利的特性:
將classpath中的全部jar包構建單個可運行的jar包,從而更容易執行和傳播服務。
搜索
public static void main()
方法並標記爲可運行的類。提供了一個內置的依賴管理器,設置依賴版本以匹配Spring Boot依賴。你能夠覆蓋爲任何你但願的版本,但默認會使用Boot選擇的版本。
使用Spring工具集構建
若是你擁有Spring工具集,只需簡單的直接導入該指南。
添加Spring-ws依賴
你建立的項目須要添加spring-ws-core
和wsdl4j依賴到構建文件中。
maven代碼:
1 2 3 4 5 6 7 8 9 |
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-ws</artifactId> </dependency> <dependency> <groupId>wsdl4j</groupId> <artifactId>wsdl4j</artifactId> <version>1.6.1</version> </dependency> |
gradle代碼:
1 2 3 4 5 6 |
dependencies { compile("org.springframework.boot:spring-boot-starter-ws") compile("wsdl4j:wsdl4j:1.6.1") jaxb("com.sun.xml.bind:jaxb-xjc:2.2.4-1") compile(files(genJaxb.classesDir).builtBy(genJaxb)) } |
建立XML格式來定義領域對象
該web service領域對象被定義在一個XML格式文件中(XSD),Spring-WS將自動導出爲一個WSDL文件。
建立一個XSD文件,包含一個操做來返回一個國家的名稱,人口,首都和貨幣。
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 |
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://spring.io/guides/gs-producing-web-service" targetNamespace="http://spring.io/guides/gs-producing-web-service" elementFormDefault="qualified"> <xs:element name="getCountryRequest"> <xs:complexType> <xs:sequence> <xs:element name="name" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="getCountryResponse"> <xs:complexType> <xs:sequence> <xs:element name="country" type="tns:country"/> </xs:sequence> </xs:complexType> </xs:element> <xs:complexType name="country"> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="population" type="xs:int"/> <xs:element name="capital" type="xs:string"/> <xs:element name="currency" type="tns:currency"/> </xs:sequence> </xs:complexType> <xs:simpleType name="currency"> <xs:restriction base="xs:string"> <xs:enumeration value="GBP"/> <xs:enumeration value="EUR"/> <xs:enumeration value="PLN"/> </xs:restriction> </xs:simpleType> </xs:schema> |
基於XML格式建立領域類
接下來的步驟是根據XSD文件來建立java類。正確的方式是使用maven或gradle插件在構建時間自動建立。
maven插件配置;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>jaxb2-maven-plugin</artifactId> <version>1.6</version> <executions> <execution> <id>xjc</id> <goals> <goal>xjc</goal> </goals> </execution> </executions> <configuration> <schemaDirectory>${project.basedir}/src/main/resources/</schemaDirectory> <outputDirectory>${project.basedir}/src/main/java</outputDirectory> <clearOutputDir>false</clearOutputDir> </configuration> </plugin> |
生成的類放置在target/generated-sources/jaxb
目錄。
gradle插件配置以下,首先須要在構建文件中配置JAXB:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
configurations { jaxb } jar { baseName = 'gs-producing-web-service' version = '0.1.0' from genJaxb.classesDir } // tag::dependencies[] dependencies { compile("org.springframework.boot:spring-boot-starter-ws") compile("wsdl4j:wsdl4j:1.6.1") jaxb("com.sun.xml.bind:jaxb-xjc:2.2.4-1") compile(files(genJaxb.classesDir).builtBy(genJaxb)) } // end::dependencies[] |
注意:上面的構建文件擁有tag及end註釋。目的是爲了可以在本指南中更容易抽取出來並作進一步解釋。在你的構建文件中無需這些註釋。
接下來的步驟是添加任務genJaxb
,該任務會生成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 |
task genJaxb { ext.sourcesDir = "${buildDir}/generated-sources/jaxb" ext.classesDir = "${buildDir}/classes/jaxb" ext.schema = "src/main/resources/countries.xsd" 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) { 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提供數據,須要建立一個國家倉庫,在本指南中建立了一個硬編碼的僞造的國家倉庫實現。
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 |
package hello; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; import io.spring.guides.gs_producing_web_service.Country; import io.spring.guides.gs_producing_web_service.Currency; import org.springframework.stereotype.Component; import org.springframework.util.Assert; @Component public class CountryRepository { private static final List<Country> countries = new ArrayList<Country>(); @PostConstruct public void initData() { Country spain = new Country(); spain.setName("Spain"); spain.setCapital("Madrid"); spain.setCurrency(Currency.EUR); spain.setPopulation(46704314); countries.add(spain); Country poland = new Country(); poland.setName("Poland"); poland.setCapital("Warsaw"); poland.setCurrency(Currency.PLN); poland.setPopulation(38186860); countries.add(poland); Country uk = new Country(); uk.setName("United Kingdom"); uk.setCapital("London"); uk.setCurrency(Currency.GBP); uk.setPopulation(63705000); countries.add(uk); } public Country findCountry(String name) { Assert.notNull(name); Country result = null; for (Country country : countries) { if (name.equals(country.getName())) { result = country; } } return result; } } |
建立國家服務終端
爲了建立一個service endpoint,x須要一個pojo對象,以及一些Spring WS註解來處理SOAP請求:
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 |
package hello; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.ws.server.endpoint.annotation.Endpoint; import org.springframework.ws.server.endpoint.annotation.PayloadRoot; import org.springframework.ws.server.endpoint.annotation.RequestPayload; import org.springframework.ws.server.endpoint.annotation.ResponsePayload; import io.spring.guides.gs_producing_web_service.GetCountryRequest; import io.spring.guides.gs_producing_web_service.GetCountryResponse; @Endpoint public class CountryEndpoint { private static final String NAMESPACE_URI = "http://spring.io/guides/gs-producing-web-service"; private CountryRepository countryRepository; @Autowired public CountryEndpoint(CountryRepository countryRepository) { this.countryRepository = countryRepository; } @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest") @ResponsePayload public GetCountryResponse getCountry(@RequestPayload GetCountryRequest request) { GetCountryResponse response = new GetCountryResponse(); response.setCountry(countryRepository.findCountry(request.getName())); return response; } } |
@Endpoint
向Spring WS註冊了該類爲一個處理來臨的SOAP消息的潛在對象。
@PayloadRoot
被Spring WS用來根據消息的命名空間及localPart來選擇處理該請求的方法。
@RequestPayload
指明來臨的消息將被映射到該方法的request參數。
@ResponsePayload
註解將使得Spring WS將返回值與響應負載映射起來。
注意:在以上代碼中,若是你沒有運行任務來根據WSDL生成領域對象,那麼在你的IDE中io.spring.guides類將會報告編譯時錯誤。
配置web service bean
使用Spring WS相關的bean配置選項建立一個新的類:
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 |
package hello; import org.springframework.boot.context.embedded.ServletRegistrationBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.ws.config.annotation.EnableWs; import org.springframework.ws.config.annotation.WsConfigurerAdapter; import org.springframework.ws.transport.http.MessageDispatcherServlet; import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition; import org.springframework.xml.xsd.SimpleXsdSchema; import org.springframework.xml.xsd.XsdSchema; @EnableWs @Configuration public class WebServiceConfig extends WsConfigurerAdapter { @Bean public ServletRegistrationBean dispatcherServlet(ApplicationContext applicationContext) { MessageDispatcherServlet servlet = new MessageDispatcherServlet(); servlet.setApplicationContext(applicationContext); servlet.setTransformWsdlLocations(true); return new ServletRegistrationBean(servlet, "/ws/*"); } @Bean(name = "countries") public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema countriesSchema) { DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition(); wsdl11Definition.setPortTypeName("CountriesPort"); wsdl11Definition.setLocationUri("/ws"); wsdl11Definition.setTargetNamespace("http://spring.io/guides/gs-producing-web-service"); wsdl11Definition.setSchema(countriesSchema); return wsdl11Definition; } @Bean public XsdSchema countriesSchema() { return new SimpleXsdSchema(new ClassPathResource("countries.xsd")); } } |
這裏Spring WS使用了不一樣的servlet類型來處理SOAP消息:
MessageDispatcherServlet
。注入及設置MessageDispatcherServlet
給ApplicationContext
是很是重要的。若是不這樣作,Spring WS沒法自動檢測到Spring bean。經過給
dispatcherServlet
bean命名,替代了Spring Boot中默認的DispatcherServlet bean
。DefaultMethodEndpointAdapter
配置了註解驅動的Spring WS編程模型。這使得使用前面提過的諸如@Endpoint
等各類各樣的註解成爲可能。DefaultWsdl11Defination
使用XsdSchema
暴露了一個標準的WSDL 1.1。
請注意你須要爲MessageDispatcherServlet
及DefaultWsdl11Definition
制定bean名稱,這是很是重要的。Bean名稱決定了生成的WSDL文件在哪一個web service是可用的。在本例中,WSDL可經過http://<host>:<port>/ws/countries.wsdl
來訪問。
該配置也使用了WSDL位置servlet轉化servlet.setTransformWsdlLocations(true)
。若是你訪問http://localhost:8080/ws/countries.wsdl,soap:address
將擁有正確的值。若是你使用本機的公共IP來訪問該WSDL,你將看到的是IP。
建立該程序的可執行文件
儘管咱們能夠將該程序打包成一個傳統的war包並部署到一個外部的應用程序服務器中,可是最簡單的方式仍是下面所演示的,建立一個能獨立運行的應用程序。你能夠經過老但好用的java main()
方法,將全部文件打包到單個可執行的jar包中。同時,能夠藉助於Spring的支持內置Tomcat servlet容器做爲HTTP運行時,從而無需部署到外部的實例中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package hello; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.context.annotation.ComponentScan; @ComponentScan @EnableAutoConfiguration public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } |
main()
方法使用了SpringApplication
輔助方法,將Application.class
做爲參數傳遞給其自身的run()
方法。這告訴Spring讀取Application
中的註解元數據,並將其做爲Spring 應用程序上下文的組件。
@ComponentScan
註解告訴Spring遞歸搜索hello
包及其子包中找到被直接或者間接使用了Spring的@Component
註解的類。該指令確保了Spring發現並註冊CountryRepository
及CountriesEndpoint
,由於他們被標記爲@Component
及@Endpoint
,這是一種@Component
註解。
@EnableAutoConfiguration
註解會基於classpath內容切換到默認的合理的行爲。例如,因爲應用程序依賴Tomcat的內置版本(tomcat-embed-core.jar),Spring會替你設置並配置一個默認的合理的Tomcat服務器。而且該程序還依賴Spring MVC(spring-webmvc.jar),Spring會配置並註冊以惡搞Spring MVC DispatcherServlet
,根本無需web.xml
文件!自動配置是強大的,彈性的機制。請查看API文檔獲取更多細節。
構建可執行的jar包
你能夠建立一個包含全部必須的依賴,類,及資源的可執行的JAR文件。這很方便傳輸,版本管理以及獨立於部署生命週期來部署服務,跨不一樣的環境,諸如此類。
1
|
./gradlew build |
而後你能夠運行WAR文件:
1
|
java -jar build/libs/gs-soap-service-0.1.0.jar |
若是你使用的是maven,你可使用mvn spring-boot:run
來運行程序,或者你可使用mvn clean package
構建JAR文件,並使用下面命令來運行:
1
|
java -jar target/gs-soap-service-0.1.0.jar |
注意:上面的產出物是以惡搞可運行JAR文件。你也能夠建立一個經典的WAR文件。
運行服務
若是使用的是Gradle,可使用如下命令來運行服務:
1
|
./gradlew clean build && java -jar build/libs/gs-soap-service-0.1.0.jar |
注意:若是你使用的是Maven,可使用如下命令來運行服務:mvn clean package && java -jar target/gs-soap-service-0.1.0.jar
。
你也能夠經過Gradle直接運行該程序:
1
|
./gradlew bootRun |
注意:使用mvn的話,命令是
mvn spring-boot:run
。
能夠看到日誌輸出。該服務應該在幾秒鐘內啓動並運行起來。
測試該程序
如今該程序正在運行,你能夠測試它。建立一個名爲request.xml
文件,包含如下的SOAP請求;
1 2 3 4 5 6 7 8 9 |
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:gs="http://spring.io/guides/gs-producing-web-service"> <soapenv:Header/> <soapenv:Body> <gs:getCountryRequest> <gs:name>Spain</gs:name> </gs:getCountryRequest> </soapenv:Body> </soapenv:Envelope> |
有不少方式來測試該SOAP接口。你可使用SoapUI等工具,或者若是你使用的是*nix/Mac系統的話,直接可使用命令行,以下所示:
1
|
$ curl --header "content-type: text/xml" -d @request.xml http://localhost:8080/ws |
你將看到以下的響應結果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?xml version="1.0"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header/> <SOAP-ENV:Body> <ns2:getCountryResponse xmlns:ns2="http://spring.io/guides/gs-producing-web-service"> <ns2:country> <ns2:name>Spain</ns2:name> <ns2:population>46704314</ns2:population> <ns2:capital>Madrid</ns2:capital> <ns2:currency>EUR</ns2:currency> </ns2:country> </ns2:getCountryResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope> |
注意:輸出多是一個緊湊的XML文檔,而不是上面顯示的格式友好的文檔。若是系統中安裝了xmllib2,可使用
curl <args above> > output.xml | xmllint --format output.xml
來查看格式友好的結果。
總結
恭喜你!你使用Spring Web Service開發完成了一個基於SOAP的服務。