在開發項目時遇到幾回webservice的接口對接,項目組沒有人對這個東西瞭解,只是根據網上的demo,編寫代碼才成功調用,我我的在結束vue先後端分離學習,學習一下,不但願本身之後再次碰見webservice的問題。vue
我是根據spring的spring-ws項目學習,spring-ws的文檔要求,要先能看懂和寫出一個xsd文件,因此我先學習xsd。java
xsd是xml schema的簡稱,是以xml技術爲基礎的一種標記語言技術,spring-ws說這是支持範圍最廣的,不是最好的。
咱們都知道xml是能夠自定義標籤的,先從一個xml例子開始web
<?xml version="1.0" encoding="UTF-8"?> <HolidayRequest xmlns="http://mycompany.com/hr/schemas"> <Holiday> <StartDate>2006-07-03</StartDate> <EndDate>2006-07-07</EndDate> </Holiday> <Employee> <Number>42</Number> <FirstName>Arjen</FirstName> <LastName>Poutsma</LastName> </Employee> </HolidayRequest>
這是一個常規的xml例子,若是把這個例子改爲xsd的,應該是這樣的
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://mycompany.com/hr/schemas" xmlns:hr="http://mycompany.com/hr/schemas"> <xs:element name="HolidayRequest"> <xs:complexType> <xs:sequence> <xs:element ref="hr:Holiday"/> <xs:element ref="hr:Employee"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="Holiday"> <xs:complexType> <xs:sequence> <xs:element ref="hr:StartDate"/> <xs:element ref="hr:EndDate"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="StartDate" type="xs:NMTOKEN"/> <xs:element name="EndDate" type="xs:NMTOKEN"/> <xs:element name="Employee"> <xs:complexType> <xs:sequence> <xs:element ref="hr:Number"/> <xs:element ref="hr:FirstName"/> <xs:element ref="hr:LastName"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="Number" type="xs:integer"/> <xs:element name="FirstName" type="xs:NCName"/> <xs:element name="LastName" type="xs:NCName"/> </xs:schema>
這也是文檔上舉得例子,剛開始看的的時候比較疑惑xs是什麼東西,是否是Java對象中屬性就要寫成xs:XXX的樣子。spring
在xml中,xmlns是XML Namespaces的縮寫,也就是命名空間的意思,命名空間的格式要求是這樣的:後端
xmlns:<前綴>="<命名空間路徑>"
因此能夠知道,xs只是前綴的意思, elementFormDefault是要求這個命名空間的元素是否是必定要加前綴,其值可設置爲qualified或unqualified,qualified是必須加前綴,unqualified是能夠忽略前綴,通常都是必須的,防止衝突。瀏覽器
如今知道元素或者標籤(如下使用標籤)schema 、element 、complexType、sequence都是這個命名空間的標籤,那命名空間的標籤又是什麼意思;在瀏覽器中打開這個命名空間url路徑,結果以下:session
XML Schema 15 October 2014 Table of contents Introduction Resources Introduction This document describes the XML Schema namespace. It also contains a directory of links to these related resources, using Resource Directory Description Language. Related Resources for XML Schema Schemas for XML Schema DTD XML Schema 1.1 A (non-normative) DTD XMLSchema.dtd for XML Schema. It incorporates an auxiliary DTD, datatypes.dtd. XML Schema 1.0 A (non-normative) DTD XMLSchema.dtd for XML Schema. It incorporates an auxiliary DTD, datatypes.dtd. XML Schema XML Schema 1.1 An XML Schema schema document for XML Schema schema documents. XML Schema 1.0 An XML Schema schema document for XML Schema schema documents. Last updated with release of XML Schema 2nd edition in July 2004. Normative References W3C XML Schema Definition Language (XSD) 1.1 Part 1: Structures, W3C XML Schema Definition Language (XSD) 1.1 Part 2: Datatypes XML Schema Part 1: Structures (2nd Edition), XML Schema Part 2: Datatypes (2nd Edition), XML Schema Part 0: Primer (2nd Edition) XPath and XQuery Functions and Operators 3.1 introduces a new type, xs:numeric, that is a union of xs:decimal, xs:float, and xs:double.
實際上打開的是一個w3c的網頁,一個說明xsd的網頁,百度百科裏有一句話,可以明白的說明這個屬性的做用:app
用於標示命名空間的地址不會被解析器用於查找信息。其唯一的做用是賦予命名空間一個唯一的名稱。不過,不少公司經常會做爲指針來使用命名空間指向實際存在的網頁,這個網頁包含關於命名空間的信息
因此可知僅僅具備標識性的做用,能不能代開就看對方公司的要求了。前後端分離
這樣仍是說明不了schema 、element 、complexType、sequence爲何能夠用,實際上,當校驗xsd的合法性時,會使用xsd的DTD文件的,在這個文件中定義了這個標籤,這是xsd的DTD的連接裏面定義不少標籤,這四個標籤就在這個DTD文件中。dom
如今我知道了,爲何能夠用這些標籤。
complexType標籤額做用是決定這個元素是簡單元素仍是複雜元素;
<xs:element name="Number" type="xs:integer"/> --簡單元素 <xs:element name="Employee"> -- 複雜元素寫法一 <xs:complexType> <xs:sequence> <xs:element ref="hr:Number"/> <xs:element ref="hr:FirstName"/> <xs:element ref="hr:LastName"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="Employee"> -- 複雜元素寫法二 , 利於複用 <xs:complexType> <xs:sequence> <xs:element ref="second"/> --- 只是舉例,沒有加前綴 </xs:sequence> </xs:complexType> </xs:element> <xs:complexType name="second"> <xs:sequence> <xs:element ref="hr:Number"/> <xs:element ref="hr:FirstName"/> <xs:element ref="hr:LastName"/> </xs:sequence> </xs:complexType>
sequence 做用是子元素的標籤是否是順序必須一致,若是不要求順序,能夠寫成這樣:
<xs:complexType name="second"> <xs:all> <xs:element ref="hr:Number"/> <xs:element ref="hr:FirstName"/> <xs:element ref="hr:LastName"/> </xs:all> </xs:complexType>
all標籤也是在DTD文件中定義的,這樣就不驗證順序了。
因此上面的xsd例子能夠改寫成這個樣子:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://mycompany.com/hr/schemas" xmlns:hr="http://mycompany.com/hr/schemas"> <xs:element name="HolidayRequest"> <xs:complexType> <xs:sequence> <xs:element ref="hr:Holiday"/> <xs:element ref="hr:Employee"/> </xs:sequence> </xs:complexType> </xs:element> <xs:complexType name="Holiday"> <xs:sequence> <xs:element name="StartDate" type="xs:date"/> <xs:element name="EndDate" type="xs:date"/> </xs:sequence> </xs:complexType> <xs:complexType name="Employee"> <xs:all> <xs:element name="Number" type="xs:integer"/> <xs:element name="Number" type="xs:integer"/> <xs:element name="LastName" type="xs:string"/> </xs:all> </xs:complexType> </xs:schema>
到此爲止,我已經能寫出一個xsd文件了,把這個文件保存爲hr.xsd文件。
根據文檔上的例子,執行一下命令,建立一個spring-ws項目:
mvn archetype:create -DarchetypeGroupId=org.springframework.ws \ -DarchetypeArtifactId=spring-ws-archetype \ -DarchetypeVersion= \ -DgroupId=com.mycompany.hr \ -DartifactId=holidayService
悲催的是,建立失敗了,報錯一堆錯誤,也沒有搞定,只能用IDE建立項目,這是建立好的項目:
這是pom.xml文件的配置:
<dependencies> <!-- https://mvnrepository.com/artifact/org.springframework.ws/spring-ws-core --> <dependency> <groupId>org.springframework.ws</groupId> <artifactId>spring-ws-core</artifactId> <version>2.4.0.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.ws/spring-xml --> <dependency> <groupId>org.springframework.ws</groupId> <artifactId>spring-xml</artifactId> <version>2.4.0.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.ws/spring-ws-security --> <dependency> <groupId>org.springframework.ws</groupId> <artifactId>spring-ws-security</artifactId> <version>2.4.0.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.ws/spring-oxm --> <dependency> <groupId>org.springframework.ws</groupId> <artifactId>spring-oxm</artifactId> <version>1.5.10</version> </dependency> <!-- https://mvnrepository.com/artifact/org.jdom/jdom --> <dependency> <groupId>org.jdom</groupId> <artifactId>jdom</artifactId> <version>2.0.2</version> </dependency> </dependencies>
這是web.xml的配置:
<display-name> MyCompany HR Holiday Service </display-name> <servlet> <servlet-name>spring-ws</servlet-name> <servlet- class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring-ws</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <session-config> <session-timeout>30</session-timeout> </session-config>
這是根據文檔配置spring-ws-servlet.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:sws="http://www.springframework.org/schema/web-services" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd"> <context:component-scan base-package="com.mycompany.hr"/> <sws:annotation-driven/> </beans>
到此爲止,一個簡單的spring-ws項目配置出來了
配置完了,開始根據文檔編寫
這是我寫的一個簡單的端點:
package com.mycompany.hr.ws; import com.mycompany.hr.service.HumanResourceService; import org.jdom2.Element; import org.jdom2.Namespace; import org.jdom2.filter.Filters; import org.jdom2.xpath.XPathExpression; import org.jdom2.xpath.XPathFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.ws.server.endpoint.annotation.Endpoint; import javax.annotation.PostConstruct; /** * 假期端點 * Created by nyl * 2017/5/31 */ @Endpoint public class HolidayEndpoint { private static final String NAMESPACE_URL="http://mycompany.com/hr/schema"; private XPathExpression<Element> startDateExpression; private XPathExpression<Element> endDateExpression; private XPathExpression<Element> firstNameExpression; private XPathExpression<Element> lastNameExpression; @Autowired private HumanResourceService humanResourceService; @PostConstruct private void init () { Namespace namespace = Namespace.getNamespace("hr", NAMESPACE_URL); XPathFactory xPathFactory = XPathFactory.instance(); startDateExpression = xPathFactory.compile("//hr:startDate", Filters.element(), null, namespace); endDateExpression = xPathFactory.compile("//hr:endDate", Filters.element(), null, namespace); firstNameExpression = xPathFactory.compile("//hr:First", Filters.element(), null, namespace); lastNameExpression = xPathFactory.compile("//hr:Last", Filters.element(), null, namespace); } }
首先,我並無徹底按照文檔的例子來寫端點,沒有使用構造方法注入。
而後,HumanResourceService 和 HumanResourceServiceImpl我沒有看到源碼,是本身根據端點內容本身寫的。
HolidayEndpoint註釋爲@Endpoint。這將該類標記爲一種特殊類型的@Component,適用於在Spring-WS中處理XML消息,並使其適合組件掃描。
這也是爲何配置組件掃描context:component-scan的緣由,不配置,註解沒法生效。
下面是寫好的執行方法:
@Endpoint public class HolidayEndpoint { private static final String NAMESPACE_URL="http://mycompany.com/hr/schema"; private XPathExpression<Element> startDateExpression; private XPathExpression<Element> endDateExpression; private XPathExpression<Element> firstNameExpression; private XPathExpression<Element> lastNameExpression; @Autowired private HumanResourceService humanResourceService; @PostConstruct private void init () { Namespace namespace = Namespace.getNamespace("hr", NAMESPACE_URL); XPathFactory xPathFactory = XPathFactory.instance(); startDateExpression = xPathFactory.compile("//hr:startDate", Filters.element(), null, namespace); endDateExpression = xPathFactory.compile("//hr:endDate", Filters.element(), null, namespace); firstNameExpression = xPathFactory.compile("//hr:First", Filters.element(), null, namespace); lastNameExpression = xPathFactory.compile("//hr:Last", Filters.element(), null, namespace); } @PayloadRoot(namespace = NAMESPACE_URL, localPart = "HolidayRequest") public void handleHolidayRequesr( @RequestPayload Element holidayRequest) { String startDate = startDateExpression.evaluateFirst(holidayRequest).getText(); String endDate = endDateExpression.evaluateFirst(holidayRequest).getText(); String firstName = firstNameExpression.evaluateFirst(holidayRequest).getText(); String lastName = lastNameExpression.evaluateFirst(holidayRequest).getText(); System.out.println(startDate); System.out.println(endDate); System.out.println(firstName); System.out.println(lastName); } }
@PayloadRoot註釋告訴Spring-WS,handleHolidayRequest方法適用於處理XML消息。該方法能夠處理的消息類型由註釋值指示,在這種狀況下,它能夠處理具備HolidayRequest localPart 和http://mycompany.com/hr/schemas命名空間的XML元素;
這是和wsdl文件的port對應起來的。
修改spring-ws-servlet.xml配置,增長一下配置項:
<sws:dynamic-wsdl id="holiday" portTypeName="HumanResource" locationUri="/holidayService/" targetNamespace="http://mycompany.com/hr/definitions"> <sws:xsd location="/WEB-INF/hr.xsd"/> </sws:dynamic-wsdl>
能夠啓動項目測試了。
啓動項目,訪問路徑http://localhost:8080/holiday...,出現404錯誤,由於以前建立過一次項目,是可使用的,因此我懷疑是否是我這一次穿件項目的方式不對,打開個人war包,發現配置性文件,全都沒有打包進來,只有class文件,由於我用的是idea,因此打開項目描述文件,看到了下面內容:
<facet type="Spring" name="Spring"> <configuration> <fileset id="fileset" name="Spring Application Context" removed="false"> <file>file://$MODULE_DIR$/src/main/webapp/WEB-INF/spring-ws-servlet.xml</file> </fileset> </configuration> </facet>
也就是說,我這次建立的項目時spring項目,可是不是web項目,因此任何路徑都沒法訪問,因而從新建立web項目,按照上面的步驟,再來一遍,運行(新項目名稱是webservicelearn),訪問路徑http://localhost:8089/webserv...
獲得正常的結果:
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:sch="http://mycompany.com/hy/schemas" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://mycompany.com/hr/definitions" targetNamespace="http://mycompany.com/hr/definitions"> <wsdl:types> <xs:schema xmlns:hr="http://mycompany.com/hy/schemas" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://mycompany.com/hy/schemas"> <xs:element name="HolidayRequest"> <xs:complexType> <xs:all> <xs:element name="Holiday" type="hr:HolidayType"/> <xs:element name="EmployeeType" type="hr:EmployeeType"/> </xs:all> </xs:complexType> </xs:element> <xs:complexType name="HolidayType"> <xs:sequence> <xs:element name="StartDate" type="xs:date"/> <xs:element name="EndDate" type="xs:date"/> </xs:sequence> </xs:complexType> <xs:complexType name="EmployeeType"> <xs:sequence> <xs:element name="Number" type="xs:integer"/> <xs:element name="FirstName" type="xs:integer"/> <xs:element name="LastName" type="xs:integer"/> </xs:sequence> </xs:complexType> </xs:schema> </wsdl:types> <wsdl:message name="HolidayRequest"> <wsdl:part element="sch:HolidayRequest" name="HolidayRequest"></wsdl:part> </wsdl:message> <wsdl:portType name="HumanResource"> <wsdl:operation name="Holiday"> <wsdl:input message="tns:HolidayRequest" name="HolidayRequest"></wsdl:input> </wsdl:operation> </wsdl:portType> <wsdl:binding name="HumanResourceSoap11" type="tns:HumanResource"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="Holiday"> <soap:operation soapAction=""/> <wsdl:input name="HolidayRequest"> <soap:body use="literal"/> </wsdl:input> </wsdl:operation> </wsdl:binding> <wsdl:service name="HumanResourceService"> <wsdl:port binding="tns:HumanResourceSoap11" name="HumanResourceSoap11"> <soap:address location="http://localhost:8089/webservicelearn/holidayService/"/> </wsdl:port> </wsdl:service> </wsdl:definitions>
瀏覽器測試倒是出現了wsdl的內容,可是這個wsdl是有問題的,網上大多數spring-ws的教程或文章大多止步於此,只是能在瀏覽器中訪問出現wsdl內容,至於wsdl內容對不對,能不能經過soap ui的測試,不多有人關注。
實際上我認爲這個wsdl是有問題的,沒法經過soap ui的測試,也沒法經過工具jar包調用。
問題是這樣的,這是soap ui的請求數據:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://mycompany.com/hy/schemas"> <soapenv:Header/> <soapenv:Body> <sch:HolidayRequest> <!--You may enter the following 2 items in any order--> <sch:Holiday> <sch:StartDate>?</sch:StartDate> <sch:EndDate>?</sch:EndDate> </sch:Holiday> <sch:EmployeeType> <sch:Number>?</sch:Number> <sch:FirstName>?</sch:FirstName> <sch:LastName>?</sch:LastName> </sch:EmployeeType> </sch:HolidayRequest> </soapenv:Body> </soapenv:Envelope>
而項目中解析數據的代碼是:
startDateExpression = xPathFactory.compile("//hr:StartDate", Filters.element(), null, namespace); endDateExpression = xPathFactory.compile("//hr:EndDate", Filters.element(), null, namespace); firstNameExpression = xPathFactory.compile("//hr:FirstName", Filters.element(), null, namespace); lastNameExpression = xPathFactory.compile("//hr:LastName", Filters.element(), null, namespace);
也就是說解析不了這個請求數據。
固然這也是我目前的理解,接下來的時間,會努力解決這個問題。