在开发项目时遇到几回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);
也就是说解析不了这个请求数据。
固然这也是我目前的理解,接下来的时间,会努力解决这个问题。