1. WebService简介
1.1 什么是WebService?
Web Service, 能使得运行在不同机器上的不同应用无须借助附加的、专门的第三方软件或硬件, 就可相互交换数据或集成。依据Web Service规范实施的应用之间, 无论它们所使用的语言、 平台或内部协议是什么, 都可以相互交换数据
通俗的讲,Web Service就是一个部署在Web服务器上的一个,它向外界暴露出一个能够通过Web进行调用的API。这就是说,你能够用编程的方法通过Web来调用这个应用程序。我们把调用这个Web Service 的应用程序叫做客户端,发布这个web服务的机应用程序器称为Web Service服务器
1.2 WebService作用
例如:天气预报,我们在不同的网站或者客户端上都看得见过天气预报的功能,而这些网站和服务端经常情况下不是同一种平台,有的是Java,PHP,.NEt…
但是这些平台都能调用中国气象局的服务接口获得对应的天气预报信息,这个时候面对不同的平台的请求WebService就派上用场了.
1.3 WebService需要了解的一些概念
- WebService的技术规则
- XML( Extensible Markup Language)用于传输格式化的数据,是Web服务的基础.
- SOAP(Simple Object Access Protocol(简单对象访问协议) SOAP = http+xmlsocket
- WSDL(WebService Description Language WebService描述语言) (最常用的)
- UDDI(通用描述、发现及整合)(不太常用)
2. 使用WebService
我们经常见到按照手机号查询号码归属地功能.该功能就是典型的WebService的服务的应用
按照普通的方法完成该功能:
a) 登录http://www.webxml.com.cn
b) 单击手机查询服务
c) 选择要调用的方法 例如: getMobileCodeInfo
d) 输入要查询的手机号单击”调用” 就会返回查询结果
这里是手动查询的,我们需要使用代码来实现该功能.
强调
实质上该服务接口支持多种查询方式,查询接口就是一个URI,可以想象成一个远程的Servlet,我们向该Servlet
发送数据,后台便会返回一些结果数据.
网站地址先放在这里(具体的原理下面慢慢解释):
http:///ws.webxml.com.cn/WebServices/MobileCodeWS.asmx
http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl
2.1 三种请求方式访问WebService
2.1.1 GET请求
代码:
/**
* 测试GET请求获得手机号归属地
* http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx/getMobileCodeInfo?mobileCode=string&userID=string
*/
public static void testGet(String mobileCode,String userId) throws Exception{
/* 拼接请求的路径 */
StringBuilder urlStr = new StringBuilder("http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx");
urlStr.append("/getMobileCodeInfo?");
urlStr.append("mobileCode=");
urlStr.append(mobileCode);
urlStr.append("&userID=");
urlStr.append(userId);
//1. 创建 URL 对象
URL url = new URL(urlStr.toString());
//2. 获得 URl 链接
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
urlConn.setConnectTimeout(5000);//设置延时时间
urlConn.setRequestMethod("GET");//设置GET请求方式
//3. 如果请求状态为OK 200
if(urlConn.getResponseCode() == HttpURLConnection.HTTP_OK){
//3.1 获得输入流
InputStream in = urlConn.getInputStream();
//3.2 读取流中的内容到内存
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len = -1;
while((len = in.read(buff, 0, buff.length)) != -1){
baos.write(buff, 0, len);
}
//打印ByteArrayOutputStream
System.out.println("GET获得数据:"+baos.toString("utf-8"));
baos.close();
in.close();
}
测试:
public static void main(String[] args) throws Exception {
testGet("13488121965","");
}
结果:
GET获得数据:<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://WebXml.com.cn/">134******65:陕西 西安 陕西移动大众卡</string>
2.1.2 POST请求
我们使用一个HttpClient的框架来做一个POST请求.该框架是Apache旗下的开源框架,方便模拟一些网络浏览器的请求操作.
HttpClient 访问网络的实现步骤
1. 准备一个请求客户端:浏览器
2. 准备请求方式:GET ,POST
3. 设置要传递的参数
4. 执行请求
5. 获取结果
首先加入jar包:
commons-codec-1.3.jar
commons-logging-1.1.1.jar
代码:
public static void testPost(String mobileCode,String userID) throws Exception {
//1. 创建客户端
HttpClient client = new HttpClient();
//2.创建POST请求对象,并给定请求的URL地址
PostMethod postMethod = new PostMethod("http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx/getMobileCodeInfo");
//3. 设置要传递的参数
postMethod.setParameter("mobileCode", mobileCode);
postMethod.setParameter("userID", userID);
//4. 执行POST请求,获得结果码
int resultCode = client.executeMethod(postMethod);
//5. 获取结果
String resultStr = postMethod.getResponseBodyAsString();
System.out.println("返回状态码 resultCode:"+ resultCode +"\nPOST请求:"+resultStr);
}
测试:
public static void main(String[] args) throws Exception {
testPost("134******65","");
}
运行结果:
返回状态码 resultCode:200
POST请求:<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://WebXml.com.cn/">134******65:陕西 西安 陕西移动大众卡</string>
2.1.3 SOAP 请求
SOAP: Simple Object Access Protocol(简单对象访问协议)
- SOAP作为一个基于XML语言的协议用于有网上传输数据。
- SOAP = 在HTTP的基础上+XML数据。
- SOAP是基于HTTP的。
- SOAP的组成如下
1.Envelope : 必须的部分。以XML的根元素出现。
2.Headers : 可选的。
3.Body : 必须的。在body部分,包含要执行的服务器的方法。和发送到服务器的数据。
代码:
@SuppressWarnings("deprecation")
public static void testSoap() throws Exception {
//1. 创建客户端
HttpClient client = new HttpClient();
//2.创建POST请求对象,并给定请求的URL地址
PostMethod postMethod = new PostMethod("http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx");
//3. 设置请求体
postMethod.setRequestBody(new FileInputStream("soap.xml"));
//设置请求头
postMethod.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
//4. 执行请求获得结果码
int resultCode = client.executeMethod(postMethod);
//5. 获取结果
String resultStr = postMethod.getResponseBodyAsString();
System.out.println("返回状态码 resultCode:"+ resultCode +"\nPOST请求:"+resultStr);
}
soap.xml:
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<getMobileCodeInfo xmlns="http://WebXml.com.cn/">
<mobileCode>134******65</mobileCode>
<userID></userID>
</getMobileCodeInfo>
</soap12:Body>
</soap12:Envelope>
测试
public static void main(String[] args) throws Exception {
testSoap("134******65","");
}
结果:
返回状态码 resultCode:200
POST请求:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope
xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<getMobileCodeInfoResponse xmlns="http://WebXml.com.cn/">
<getMobileCodeInfoResult>134******65:陕西 西安 陕西移动大众卡</getMobileCodeInfoResult>
</getMobileCodeInfoResponse>
</soap:Body>
</soap:Envelope>
该方式比较麻烦,还必须配置特定的soap.xml
2.1 调用WebService服务接口
其实,webservice为我们提供的WSDL描述,该描述包括了服务接口的类,和获得服务内容的请求方法以及参数等等…非常详细的信息.
根据这些信息我们可以使用一个JDK安装的时候自带的工具wsimport来生成本地的代理Java类.
- 该工具就在JDk安装的路径的bin目录下
- 记得设置jdk\bin 环境变量 要指定path
- 语法 wsimport [opations]
- wsimport使用:
- wsdl_uri:wsdl 的统一资源标识符
- d : 指定要输出的文件的位置
- s : 表示要解析java的源码 ,默认解析出的是class字节码
- p : 指定输出的包名
通过服务接口的URI生成java源文件
格式:
wsimport -s . -d [文件要输出的路径] -p [输出的包名] [解析的URI地址]
创建一个文件夹,cmd打开并切换到该文件夹下使用命令
wsimport -s . -d ./ -p com.bart.webservice http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL
切换到当前路径:使用java打包
jar -cvf [包名].jar [打包的目录]
jar -cvf mobile.jar ./
把打包好的JAR包放在项目的环境变量中
使用该JAR包中的对象和方法.该方法使用哪些类和方法实际上是从WSDL描述文件总得到的信息,
关于该文件的解释,我们在下面会详细解释.
public static void main(String[] args) {
//生成服务对象
MobileCodeWS ws=new MobileCodeWS();
//取得webservice服务的访问方式 : Soap1.1 Soap 1.2 Http-get http-Post
MobileCodeWSSoap mobileCodeWSSoap = ws.getMobileCodeWSSoap();
/**
* 返回的数据有两种类型 :
* 1. 简单的数据类型 。基本数据类型 :整数、布尔、字符串 等
* 2. 复合的数据类型 :结构体 ,数组 ,对象
*/
//1.简单的数据
String result=mobileCodeWSSoap.getMobileCodeInfo("134******65", "");
System.out.println("返回的结果:"+result);
//2. 复合的数据 List<String> List<Student>
ArrayOfString databaseInfo = mobileCodeWSSoap.getDatabaseInfo();
List<String> results=databaseInfo.getString();
//遍历集合
for(String temp:results){
System.out.println(temp);
}
//jsp Select
}
输出:
返回的结果:
134******65:广东 广州 广东移动神州行卡
全部 数据 265903
安徽 安庆 658
安徽 蚌埠 456
....
2.2 创建自己的WebService服务
2.2.1 需求
我们希望实现一个功能,通过手机的系统名返回该手机的描述信息,
包括osName(系统名),owner(所属公司),total(占有率)
2.2.2 实现
- 使用@webService注解被发布的服务类
使用Endpoint.publish([发布的地址], [发布的服务对象]);
- 创建Phone类
/**
* 封装手机信息
*/
public class Phone {
private String osName;//操作系统名
private String owner;//所属公司
private double total;//占有率
// ...
}
- 创建PhoneService类
/**
* 手机信息的服务类
*/
@WebService (serviceName="PhoneManager",//修改服务名
targetNamespace="http://publish.service.com") //修改命名空间 ,默认包名,取反
public class PhoneInfoService {
/* @WebMethod(operationName="getMObileInfo") : 修改方法名
* @WebResult(name="phone") : 修改返回参数名
* @WebParam(name="osName") : 修改输入参数名
* @WebMethod(exclude=true) : 把该方法排除在外
*/
/**
* 根据操作系统名返回手机的信息
* @param osName
* @return phone
*/
@WebMethod(operationName="getMObileInfo")
public @WebResult(name="phone")Phone getPhoneInfo(@WebParam(name="osName")String osName){
Phone phone = new Phone();
if("ios".equals(osName)){
phone.setOsName("ios");
phone.setOwner("apple");
phone.setTotal(14.2);
}else if("android".equals(osName)){
phone.setOsName("android");
phone.setOwner("google");
phone.setTotal(85.6);
}else if("windowPhone".equals(osName)){
phone.setOsName("window phone");
phone.setOwner("microsoft");
phone.setTotal(0.2);
}
return phone;
}
@WebMethod(exclude=true)//把该方法排除在外
public void sayHello(String str){
System.out.println("hello"+str);
}
}
2.2.3 发布
public static void main(String[] args) {
/*
* 1. address:定义发布的webservice服务地址
* 2. Endpoint.publish(address,obj)发布服务信息
* */
String address = "http://127.0.0.1:6969/ws/phoneService";
Endpoint.publish(address, new PhoneInfoService());
System.out.println("wsdl地址:"+address+"?WSDL");
}
运行main函数,如果没有报错,表示发布成功:
输出的URL赋值粘贴到浏览器:
http://127.0.0.1:6969/ws/phoneService?WSDL
会返回一个phoneService.xml的文件,该文件就是上面一直没有解释的但是很重要的
WSDL描述文件.
2.2.4 WSDL的解释
几个标签
元素 | 定义 |
---|---|
<portType> | web service 执行的操作 |
<message> | web service 使用的消息 |
<types> | web service 使用的数据类型 |
<binding> | web service 使用的通信协议 |
直接在代码中解释
<!-- Published by JAX-WS RI (http://jax-ws.java.net). RI's version is JAX-WS
RI 2.2.9-b130926.1035 svn-revision#5f6196f2b90e9460065a4c2f4e30e065b245e51e. -->
<!-- Generated by JAX-WS RI (http://jax-ws.java.net). RI's version is JAX-WS
RI 2.2.9-b130926.1035 svn-revision#5f6196f2b90e9460065a4c2f4e30e065b245e51e. -->
<!-- wsdl 配置文件
[1]. targetNamespace="http://publish.bart.com/" : 为发布的服务类的包名的倒序
[2]. name="PhoneInfoServiceService : 生成的服务累的
-->
<definitions
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://publish.service.com" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://publish.service.com"
name="PhoneManager">
<!--
<types> 元素定义 web service 使用的数据类型。 为了最大程度的平台中立性,WSDL 使用 XML Schema 语法来定义数据类型.
-->
<types>
<xsd:schema>
<xsd:import namespace="http://publish.service.com"
schemaLocation="http://127.0.0.1:6969/ws/phoneService?xsd=1" />
</xsd:schema>
</types>
<!--
<message> 元素定义一个操作的数据元素. 每个消息均由一个或多个部件组成.可以把这些部件比作传统编程语言中一个函数调用的参数.
通信消息的数据结构的抽象类型化定义.使用Types所定义的类型来定义整个消息的数据结构.
-->
<message name="getMObileInfo">
<part name="parameters" element="tns:getMObileInfo" />
</message>
<message name="getMObileInfoResponse">
<part name="parameters" element="tns:getMObileInfoResponse" />
</message>
<!-- WSDL端口
它可描述一个 web service、可被执行的操作,以及相关的消息。 可以把 <portType> 元素比作传统编程语言中的一个函数库(或一个模块、或一个类)。
[1]. <portType name="PhoneInfoService"> : 其实就是我们暴露出去的服务的类
[2]. <operation name="getPhoneInfo"> : 暴露出去的服务类的方法
[3]. <input ... message="tns:getPhoneInfo"></input> : 暴露方法的输入参数及其类型 tns:getPhoneInfo信息可以参照开头<types>
中的schemaLocation="http://127.0.0.1:6969/ws/phoneService?xsd=1"描述
[4]. <output ... message="tns:getPhoneInfoResponse"> : 肯定是输出参数类型了
-->
<portType name="PhoneInfoService">
<operation name="getMObileInfo">
<input
wsam:Action="http://publish.service.com/PhoneInfoService/getMObileInfoRequest"
message="tns:getMObileInfo" />
<output
wsam:Action="http://publish.service.com/PhoneInfoService/getMObileInfoResponse"
message="tns:getMObileInfoResponse" />
</operation>
</portType>
<!--
<binding> 元素为每个端口定义消息格式和协议细节.一般格式为:[暴露的类名]PortBinding
-->
<binding name="PhoneInfoServicePortBinding" type="tns:PhoneInfoService">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="document" />
<operation name="getMObileInfo">
<soap:operation soapAction="" />
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
</binding>
<!--
暴露出去的服务类的服务类,我们暴露出去的服务类最终在客户端是一个借口,而这个[暴露的服务类]Service有一个方法
返回我们暴露的服务类的对象,该方法 : get[暴露的服务类]Port()
-->
<service name="PhoneManager">
<port name="PhoneInfoServicePort" binding="tns:PhoneInfoServicePortBinding">
<soap:address location="http://127.0.0.1:6969/ws/phoneService" />
</port>
</service>
</definitions>
<types> 标签中的 <xsd>中的schemaLocation="http://127.0.0.1:6969/ws/phoneService?xsd=1",
复制到浏览器地址栏访问,会得到另外一个xml文件,
将http://127.0.0.1:6969/ws/phoneService?xsd=1
该文件描述了服务类中的所有的类型信息.
<!--
Published by JAX-WS RI (http://jax-ws.java.net).
RI's version is JAX-WS
RI 2.2.9-b130926.1035
svn-revision#5f6196f2b90e9460065a4c2f4e30e065b245e51e.
-->
<xs:schema xmlns:tns="http://publish.service.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="1.0" targetNamespace="http://publish.service.com">
<xs:element name="getMObileInfo" type="tns:getMObileInfo" />
<xs:element name="getMObileInfoResponse" type="tns:getMObileInfoResponse" />
<!-- 服务方法的方法入参 -->
<xs:complexType name="getMObileInfo">
<xs:sequence>
<xs:element name="osName" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<!-- 服务方法的返回值 tns:phone 指定的是返回值的类型 -->
<xs:complexType name="getMObileInfoResponse">
<xs:sequence>
<xs:element name="phone" type="tns:phone" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<!-- 复杂类型用来描述一个对象 -->
<xs:complexType name="phone">
<xs:sequence>
<xs:element name="osName" type="xs:string" minOccurs="0" />
<xs:element name="owner" type="xs:string" minOccurs="0" />
<xs:element name="total" type="xs:double" />
</xs:sequence>
</xs:complexType>
</xs:schema>
2.2.5 模拟客户端调用服务接口
解析服务端接口的WSDL
wsimport -s . -d ./ -p com.client.localproxy02 http://127.0.0.1:6969/ws/phoneService?WSDL
- 打包 :
jar -cvf mobile2.jar ./
根据描述信息进行解析
public static void main(String[] args) {
PhoneManager piss = new PhoneManager();
PhoneInfoService pis = piss.getPhoneInfoServicePort();
Phone phone = pis.getMObileInfo("android");
System.out.println(phone.getOsName());
System.out.println(phone.getOwner());
System.out.println(phone.getTotal());
/* android
google
85.6
*/
}
3. 使用接口发布WebService
有的时候我们的发布的服务类需要实现好几个接口,此时就必须连使用的接口一同发布.
3.1 创建服务类
3.2 接口Animal.java
/**
* 发布的服务接口
*/
@WebService
public interface Animal {
public String run();
}
3.3 接口实现类Cat.java
@WebService(serviceName="CatService",targetNamespace="http://publish2.service.com")
public class Cat implements Animal{
@WebMethod(operationName="getRunInfo")
@Override
public @WebResult(name="runInfo")String run() {
return "猫|跑得快|非常快|像风一样";
}
}
3.4 发布服务
public static void main(String[] args) {
String uri = "http://127.0.0.1:8989/ws/AnimalService";
Animal animal = new Cat();
Endpoint.publish(uri,animal);
System.out.println("WSDL:"+uri+"?WSDL");
}
WSDL地址:WSDL:http://127.0.0.1:8989/ws/AnimalService?WSDL
3.5 解析该地址
- 解析
wsimport -s . -d ./ -p com.client.localproxy03 http://127.0.0.1:8989/ws/AnimalService?WSDL
- 打包
jar cvf mobile3.jar ./
3.6 测试
public static void main(String[] args) {
CatService cs = new CatService();
Cat cat = cs.getCatPort();
String str = cat.getRunInfo();
String[] arr = str.split("\\|");
for(String s : arr){
System.out.println(s);
}
}
输出结果:
猫
跑得快
非常快
像风一样