嗨,大家好,最近想跟大家聊聊关于WebService,我在工作中,会经常对接一些第三方协议,很多都是WebService协议,那么接下来,我们就一起来深入了解一下这门技术吧,还是从基本概念及原理讲起,然后再讲实例运用。
我们围绕着WebService是什么,WebService原理,WebService怎么用呢?这三个方面来阐述。
希望你们能认真看完,对你工作中应该会有帮助的。
WebService是什么
WebService是一种跨编程语言、跨操作系统平台的远程调用技术。
远程调用技术:远程调用是指一台设备上的程序A可以调用另一台设备上的方法B。比如,天气预报系统,淘宝网,校内网,百度等把自己的系统服务以WebService服务的形式暴露出来,让第三方网站和程序可以调用这些服务功能,这样扩展了自己系统的市场占有率。
跨编程语言:是指服务端、客户端程序的编程语言可以不同。
跨操作系统平台:是指服务端、客户端可在不同的操作系统上运行。
以上的介绍就是我们也就是为什么使用WebService的原因。WebService能解决:跨平台调用、跨语言调用、远程调用。
WebService原理
WebService遵循SOAP协议通过XML封装数据,然后由Http协议来传输数据。
WebService服务器端首先要通过一个WSDL文件来说明自己有什么服务可以对外调用。简单的说,WSDL就像是一个说明书,用于描述WebService及其方法、参数和返回值。 WSDL文件保存在Web服务器上,通过一个url地址就可以访问到它。
客户端要调用一个WebService服务之前,要知道该服务的WSDL文件的地址。
WebService服务提供商可以通过两种方式来暴露它的WSDL文件地址:1、注册到UDDI服务器,以便被人查找;2、直接告诉给客户端调用者。
SOAP协议
SOAP 是基于 XML 的简易协议,可使应用程序在 HTTP 之上进行信息交换。或者更简单地说:SOAP 是用于访问网络服务的协议。
SOAP 消息的基本结构
<?xml version="1.0"?> <soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope" soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding"> <soap:Header> ... </soap:Header> <soap:Body> ... <soap:Fault> ... </soap:Fault> </soap:Body> </soap:Envelope> 复制代码
一条 SOAP 消息就是一个普通的 XML 文档,包含下列元素:
- 必需的 Envelope 元素,可把此 XML 文档标识为一条 SOAP 消息
- 可选的 Header 元素,包含头部信息
- 必需的 Body 元素,包含所有的调用和响应信息
- 可选的 Fault 元素,提供有关在处理此消息所发生错误的信息
WSDL
WSDL 是基于 XML 的用于描述 Web Services 以及如何访问 Web Services 的语言。
WebService协议的文档
自定义WebService服务
Apache CXF 是一个开源的 Services 框架,CXF 帮助您来构建和开发 Services 这些 Services 可以支持多种协议,比如:SOAP、POST/HTTP、RESTful HTTP CXF 大大简化了 Service可以天然地和 Spring 进行无缝集成。
创建一个maven项目,引入依赖
<dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http-jetty</artifactId> <version>3.1.4</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>3.1.4</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-core</artifactId> <version>3.1.4</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> </dependency> 复制代码
编写一个实体类Car
@Data public class Car { private String brand;//品牌 private String plateNum;//车牌号 private double price;//价格单位:万 private String owner;//拥有者 } 复制代码
接口的代码编写
package com.cn.service; import com.cn.domain.Car; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; @WebService(targetNamespace = "http://service.cn.com/") public interface CarService { /** * 1.在类上添加@WebService注解,代表发布一个WebService服务。 * 2.给类添加上@WebService注解后,类中所有的非静态方法都将会对外公布。 * 3.如果希望某个方法不对外公开,可以在方法上添加@WebMethod(exclude=true),阻止对外公开。 * 4.如果一个类上,被添加了@WebService注解,则必须此类至少有一个可以公开的方法,否则将会启动失败。 * protected、private、final、static方法不能对外公开。 * 5.@targetNamespace 设置命名空间,默认包名,取反 */ @WebMethod public Car getCarInfo(@WebParam(name = "carBrand", targetNamespace = "http://service.cn.com/") String carBrand); } 复制代码
接口的实现类
package com.cn.service; import com.cn.domain.Car; public class CarServiceImpl implements CarService { @Override public Car getCarInfo(String carBrand) { Car car = new Car(); if (carBrand.equals("audi")) { car.setBrand("audi"); car.setOwner("Tom"); car.setPlateNum("皖A11011"); car.setPrice(50.00); } else if (carBrand.equals("bmw")) { car.setBrand("bmw"); car.setOwner("Jack"); car.setPlateNum("皖A8888M"); car.setPrice(60.00); } else { car.setBrand("其他"); car.setOwner("Rose"); car.setPlateNum("未知"); car.setPrice(00.00); } return car; } } 复制代码
服务发布
package com.cn; import com.cn.service.CarServiceImpl; import org.apache.cxf.jaxws.JaxWsServerFactoryBean; public class AppTest { public static void main(String[] args) { //发布服务的工厂 JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean(); //设置服务的地址 factory.setAddress("http://127.0.0.1:9999"); //设置服务类 factory.setServiceBean(new CarServiceImpl()); //发布服务 factory.create(); System.out.println("发布服务成功,服务的wsdl地址是:http://127.0.0.1:9999?wsdl"); } } 复制代码
发布成功后,可以在浏览器上输入网址:http://127.0.0.1:9999?wsdl
<wsdl:definitions name="CarServiceImplService" targetNamespace="http://service.cn.com/"> <wsdl:types> <xs:schema elementFormDefault="unqualified" targetNamespace="http://service.cn.com/" version="1.0"> <xs:element name="getCarInfo" type="tns:getCarInfo"/> <xs:element name="getCarInfoResponse" type="tns:getCarInfoResponse"/> <xs:complexType name="getCarInfo"> <xs:sequence> <xs:element minOccurs="0" name="carBrand" type="xs:string"/> </xs:sequence> </xs:complexType> <xs:complexType name="getCarInfoResponse"> <xs:sequence> <xs:element minOccurs="0" name="return" type="tns:car"/> </xs:sequence> </xs:complexType> <xs:complexType name="car"> <xs:sequence> <xs:element minOccurs="0" name="brand" type="xs:string"/> <xs:element minOccurs="0" name="owner" type="xs:string"/> <xs:element minOccurs="0" name="plateNum" type="xs:string"/> <xs:element name="price" type="xs:double"/> </xs:sequence> </xs:complexType> </xs:schema> </wsdl:types> <wsdl:message name="getCarInfoResponse"> <wsdl:part element="tns:getCarInfoResponse" name="parameters"> </wsdl:part> </wsdl:message> <wsdl:message name="getCarInfo"> <wsdl:part element="tns:getCarInfo" name="parameters"> </wsdl:part> </wsdl:message> <wsdl:portType name="CarService"> <wsdl:operation name="getCarInfo"> <wsdl:input message="tns:getCarInfo" name="getCarInfo"> </wsdl:input> <wsdl:output message="tns:getCarInfoResponse" name="getCarInfoResponse"> </wsdl:output> </wsdl:operation> </wsdl:portType> <wsdl:binding name="CarServiceImplServiceSoapBinding" type="tns:CarService"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="getCarInfo"> <soap:operation soapAction="" style="document"/> <wsdl:input name="getCarInfo"> <soap:body use="literal"/> </wsdl:input> <wsdl:output name="getCarInfoResponse"> <soap:body use="literal"/> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="CarServiceImplService"> <wsdl:port binding="tns:CarServiceImplServiceSoapBinding" name="CarServiceImplPort"> <soap:address location="http://127.0.0.1:9999/"/> </wsdl:port> </wsdl:service> </wsdl:definitions> 复制代码
客户端调用
客户端的调用方式有很多种,这里我就不一一介绍了,我来说一下我平时工作中用的方式。
Soap客户端-SoapClient
在接口对接当中,WebService接口占有着很大份额,而我们为了使用这些接口,不得不引入类似Axis等库来实现接口请求。
客户端引入依赖
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.4.3</version> </dependency> 复制代码
现在有了Hutool,就可以在无任何依赖的情况下,实现简便的WebService请求。
根据上面的wsdl文档,我们知道了:
- 方法名为:getCarInfo
- 参数只有一个,为:carBrand
- 定义了一个命名空间,URI为http://service.cn.com/
这样我们就能构建相应SOAP请求:
public class Test { public static void main(String[] args) { //设置webservice服务地址 SoapClient client = SoapClient.create("http://127.0.0.1:9999") // 设置要请求的方法,传入对应的命名空间 .setMethod("getCarInfo", "http://service.cn.com/") // 设置参数 .setParam("carBrand", "audi"); // 发送请求,参数true表示返回一个格式化后的XML内容 // 返回内容为XML字符串,可以配合XmlUtil解析这个响应 String result = client.send(true); System.out.println(result); System.out.println("----------------------------"); Document docResult = XmlUtil.readXML(result); Object brand = XmlUtil.getByXPath("//return//brand", docResult, XPathConstants.STRING); Object owner = XmlUtil.getByXPath("//return//owner", docResult, XPathConstants.STRING); Object plateNum = XmlUtil.getByXPath("//return//plateNum", docResult, XPathConstants.STRING); Object price = XmlUtil.getByXPath("//return//price", docResult, XPathConstants.STRING); System.out.println("brand = " + brand); System.out.println("owner = " + owner); System.out.println("plateNum = " + plateNum); System.out.println("price = " + price); } } 复制代码
返回的数据:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns2:getCarInfoResponse xmlns:ns2="http://service.cn.com/"> <return> <brand>audi</brand> <owner>Tom</owner> <plateNum>皖A11011</plateNum> <price>50.0</price> </return> </ns2:getCarInfoResponse> </soap:Body> </soap:Envelope> ---------------------------- brand = audi owner = Tom plateNum = 皖A11011 price = 50.0 Process finished with exit code 0 复制代码
SoapUI
链接:pan.baidu.com/s/1UIMv_E8x… 提取码:b9rg
soapUI是一个开源测试工具,通过soap/http来检查、调用、实现Web Service的功能/负载/符合性测试。 用这个图形化工具也可以调用WebService服务,作为测试使用。
总结
关于WebService的知识就介绍这么多,其实在工作中,了解这些就够用了。