WebService学习
WebService是干啥的?
WebService就是两个系统之间的远程调用。
WebService可以帮助我们干什么?
比如一个需求,我们需要在我们的项目当中,获取当地区域每天实时的天气预报信息显示在项目首页当中,那么这个天气预报信息的由来,就必须使用WebService进行远程调用外部提供的天气预报接口进行获取。
了解WebService基本原理。
WebService的原理其实就是基础的Socket通信,它是基于Socket通信来实现的。
/**
* 复习Socket
* 创建服务端
* ServerSocket
*/
public class MyServer {
public static void main(String [] args) throws Exception{
//创建ServerSocket对象,指定服务端监听的端口号
ServerSocket ss = new ServerSocket(5666);
//接受客户端的请求
Socket accept = ss.accept();
//获取字节输入流,流当中存储着客户端发送的数据
InputStream inputStream = accept.getInputStream();
//将字节流转换为字符流
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
//获取字符串
String readLine = br.readLine();
System.out.println("服务端接收:"+readLine);
ss.close();
}
}
/**
* Socket通信
* 客户端创建
*/
public class MyClient {
public static void main(String [] args) throws Exception{
//创建客户端,指定服务端的ip地址以及端口号
Socket socket = new Socket("172.20.184.56",5666);
//获取输出流,编写发送数据
OutputStream outputStream = socket.getOutputStream();
//发送数据到客户端
outputStream.write("你好,socket".getBytes());
//关闭客户端
socket.close();
}
}
通过WebService来调用网络上的服务
http://www.webxml.com.cn/zh_cn/index.aspx
一些网站会提供一些免费的服务供我们进行使用
案例:调用上面网页上提供的免费服务
http://ws.webxml.com.cn/WebServices/WeatherWS.asmx?wsdl
基本概念
了解WebService当中重要的两个关键概念
WSDL和SOAP
WSDL
WSDL全程为WebService Description Language Web服务描述语言
WSDL是使用XML文件,对WebService服务进行描述的一种规范。
我们就是根据WSDL定义的描述xml文件,对调用的服务进行了解,知道如何去调用该服务。
通过XML形式说明服务在什么地方-地址。
通过XML形式说明服务提供什么样的方法 – 如何调用。
SOAP
soap :Simple Object Access Protocol
简单对象访问协议:soap是一个基于http协议外加xml正文的服务于WebService的一个访问协议,这个协议也是WebService可以跨语言调用的原理所在。
soap内部使用的就是http协议,不过与http协议不同的是,soap请求的正文格式为xml,通过xml进行传输WebService所需的数据。
SOAP = 在HTTP的基础上+XML数据。
SOAP是基于HTTP的。
SOAP的组成如下:
Envelope – 必须的部分。以XML的根元素出现。
Headers – 可选的。
Body – 必须的。在body部分,包含要执行的服务器的方法。和 发送到服务器的数据。
示例:
响应的信息,同发送信息一样,先必须是HTTP协议,然后再遵循SOAP协议
通过JDK发布一个WebService服务
/**
*
* 通过JDK来发布一个WebService服务
* 1、要求JDK的版本必须在1.6以上
* 2、在我们定义的服务的接口上,或者是类上使用@WebService()注解
* 3、Endpoint.publish()方法当中需要传入两个参数
* 第一个参数为创建的WebService服务绑定的链接
* 第二个参数为WebService的实例对象
* 4、Endpoint.publish()方法会在开一个线程,使用新开的线程来新开 WebService服务
*/
@WebService
public class MyFirstWebService {
public String sayHello(String name){
System.out.println("hello "+name);
return "hello"+name;
}
public static void main(String [] args){
Endpoint.publish("http://localhost:9999/one",new MyFirstWebService());
}
}
通过wsimport命令生成客户端代码
通过java提供的wsimport命令,访问我们在Endpoint.publish()方法当中第一个参数指定的url,加后缀?wsdl,在浏览器当中进行访问。
可以发现,在浏览器当中,会生成一个xml文件,这个文件就是这个服务的WSDL说明文件。
Endpoint.publish(“http://localhost:9999/one”,new MyFirstWebService());
通过java提供的命令wsimport,获取客户端代码
执行命令:
wsimport -s . http://localhost:4567/one?wsdl
运行结束后,可以发现在目录下会生成客户端代码,代码目录结构就是当初定义服务端代码的包结构
wsimport是jdk自带的,可以根据wsdl文档生成客户端调用代码的工具.当然,无论服务器端的WebService是用什么语言写的,都将在客户端生成Java代码.服务器端用什么写的并不重要.
wsimport.exe位于JAVA_HOME\bin目录下.
常用参数为:
-d<目录> - 将生成.class文件。默认参数。 -s<目录> - 将生成.java文件。 -p<生成的新包名> -将生成的类,放于指定的包下。
(wsdlurl) - http://server:port/service?wsdl,必须的参数。
示例:
C:/> wsimport –s . http://192.168.0.100/one?wsdl
注意:-s不能分开,-s后面有个小点,用于指定源代码生成的目录。点即当前目录。
如果使用了-s参数则会在目录下生成两份代码,一份为.class代码。一份为.java代码。
.class代码,可以经过打包以后使用。.java代码可以直接Copy到我们的项目中运行。
详细分析WSDL文件
WSDL文件是向我们的开发者阐述调用的WebService服务的基本信息的,如通过什么类来创建调用服务的实例,调用实例的某个方法可以做成什么样的功能。
WSDL文件描述我们的WebService服务,它的描述结构是详细信息都在文件头部位置,而WebService的基本信息都位于WSDL文件的尾部,所以我们要想通过WSDL来了解我们的WebService的话,就从文件的底部,一点一点的按照层级向上进行查看即可。
调用网络上的WebService服务
调用天气预报服务
步骤:
-
首先找到我们要调用服务的WSDL描述,根据服务链接去通过wsimport命令生成客户端代码。
-
当我们通过wsimport命令生成客户端代码的时候,有时候会遇到这种错误,
我们只需要将网页上的WSDL描述保存到本地磁盘当中,将错误的节点信息删除即可。
生成的客户端代码
步骤2:将代码直接粘贴到项目src目录当中,根据根据WSDL描述,进行调用服务。
通过客户端代码调用WebService服务
public class Client {
public static void main(String args []){
//根据WSDL描述文件,进行调用客户端代码,使用服务
//<wsdl:service name="WeatherWS">
WeatherWS wws = new WeatherWS();
//<wsdl:port name="WeatherWSSoap" binding="tns:WeatherWSSoap">
WeatherWSSoap weatherWSSoap = wws.getWeatherWSSoap();
//<wsdl:operation name="getWeather">
//<soap:operation />
//<wsdl:input>
// <soap:body use="literal" />
//</wsdl:input>
// <wsdl:output>
// <soap:body use="literal" />
// </wsdl:output>
//</wsdl:operation>
ArrayOfString weather = weatherWSSoap.getWeather("烟台", null);
//遍历返回的天气信息
List<String> string = weather.string;
for (int i = 0; i < string.size(); i++) {
System.out.println(string.get(i));
}
}
}
如果将本地保存的WDSL文件删除,在调用服务,会引起错误吗?
WebService通过HTTP协议完成远程调用: (深入分析) – RPC
WebService只采用HTTP POST方式传输数据,不使用GET方式; – 握手,WSDL-get,
普通http post的contentType为
application/x-www-form-urlencoded
WebService的contentType为-即在Http的基础上发SOAP协议
text/xml 这是基于soap1.1协议。
application/soap+xml 这是基于soap1.2协议。
WebService从数据传输格式上作了限定。WebService所使用的数据均是基于XML格式的。目前标准的WebService在数据格式上主要采用SOAP协议。SOAP协议实际上就是一种基于XML编码规范的文本协议。
SOAP – Simple Object Access protocol 简单对像访问协议。是运行在HTTP协议基础之上的协议。其实就是在HTTP协议是传输XML文件,就变成了SOAP协议。
SOAP1.1和SOAP1.2的 namespace不一样。可以通过查看类
javax.xml.ws.soap.SOAPBinding来查看里面的常量
默认情况下,Jdk1.6只支持soap1.1
即:@BindingType(value=javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_BINDING)
WebService和Web服务器的区别
WebService和Web服务器有什么区别呢?我们可以把WebService看作是Web服务器上应用;反过来说,Web服务器是WebService运行时所必需的容器。这就是它们的区别和联系。
使用JDK1.6发布的简单Web服务,其内部其实是使用Socket实现。可以查看:SUN公司未对外公布的API类
com.sun.xml.internal.ws.transport.http.server. ServerMgr获知,请使用反编译工具。
WebService的特点
WebService通过HTTP POST方式接受客户的请求
WebService与客户端之间一般使用SOAP协议传输XML数据.
它本身就是为了跨平台或跨语言而设计的。
通过Ajax调用WebService服务
原理:其实就是通过Ajax发送Http请求,不过对该http请求进行重构,将请求体转换为符合soap协议要求的xml请求体
通过js与ajax将http重构为soap协议,在请求webService服务即可。
步骤:
使用原生的Ajax来调用WebService服务
function goWebService(){
//获取XMLHttpRequest对象
xhr = createXmlHttpRequest();
//定义调用的WebService的URL
var wsURL = "http://172.20.184.56:4567/one";
//定义请求体,因为WebService支持soap协议进行访问调用,所以我们要 //将http协议转换为soap协议,将请求体转换为xml格式的数据。
var requestBody = '<soapenv:Envelope xmlns:q0="http://main.webs.mao.com/"'+
' xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ' +
' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> ' +
' <soapenv:Header></soapenv:Header><soapenv:Body><q0:sayHello><arg0>孙艳文</arg0></q0:sayHello></soapenv:Body></soapenv:Envelope>';
xhr.open("POST",wsURL,true);
//设置请求头,设置请求体为xml,soap协议。
xhr.setRequestHeader("content-type","text/xml;charset=utf8");
//指定回调函数
xhr.onreadystatechange = callBack;
//发送请求,调用WebService服务
xhr.send(requestBody);
}
通过URLConnection调用WebService服务
通过定义Java代码的方式,使用URLConnection来调用WebService服务。
和ajax原理一致,我们都是通过http发送协议,将http重新设置请求头和请求正文,重构成soap协议,通过soap协议进行调用WebService服务
public class GoWebServiceByURLConnectino {
public static void main(String args []) throws Exception{
/**
* 通过URLConnection的方式调用WebService
* 原理和ajax调用WebService一致,都是将发出http协议的请求 * 重构为soap协议的请求,通过发出的soap协议的请求来
* 调用WebService服务
*/
//步骤1:指定WebService服务的url地址
String wsUrl = "http://172.20.184.56:4567/one";
//步骤2:创建URL对象,传入定义的url链接
URL url = new URL(wsUrl);
//步骤3:获取URLConnection对象
URLConnection openConnection = url.openConnection();
//步骤4:将获取的URLConnection转型为HttpURLConnection,因为我们发送的是http请求
HttpURLConnection huc = (HttpURLConnection)openConnection;
//步骤5:设置调用WebService服务的一些参数
//是否有输入参数
huc.setDoInput(true);
//是否有输出参数
huc.setDoOutput(true);
//设置http请求方式为post请求
huc.setRequestMethod("POST");
//设置请求头为soap协议的请求头
huc.setRequestProperty("content-type","text/xml;charset=UTF-8");
//步骤6::设置请求体,soap协议的请求体为xml格式,和ajax调用WebService一致,自己构建请求体
String requestBody = "<soapenv:Envelope xmlns:q0=\"http://main.webs.mao.com/\""
+ " xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"> "
+ "<soapenv:Header></soapenv:Header><soapenv:Body><q0:sayHello><arg0> maokexing</arg0></q0:sayHello></soapenv:Body></soapenv:Envelope>";
//步骤7:获取输出流,将构建的请求体通过输出流,写到请求当中
OutputStream out = huc.getOutputStream();
out.write(requestBody.getBytes());
out.close();
//获取响应的响应码 200即响应成功
int responseCode = huc.getResponseCode();
System.out.println(responseCode);
if(responseCode==200){
//步骤8:获取输入流,获取调用WebService返回的结果信息
InputStream in = huc.getInputStream();
int len = 0;
byte [] buf = new byte[1024];
StringBuffer sb = new StringBuffer();
while((len = in.read(buf))!=-1){
String str = new String(buf,0,len);
sb.append(str);
}
System.out.println(sb.toString());
in.close();
}
}
}
通过客户端编程调用WebService服务
这种方式需要依赖一个Service类、一个QName类,以及通过wsimport命令生成的客户端接口。
这种方式其实就是对wsimport生成客户端代码的一个升级,只是比wsimport命令使用了更少的客户端代码,只依赖一个接口即可。
首先先通过wsimport命令生成客户端代码,不过我们只需要一个接口即可。
/**
* 通过客户端编程的方式来调用WebService服务。
* 这种方式其实就是对wsimport通过命令行方式生成客户端代码的简化。
* 这种方式需要依赖一个由wsimport命令生成的接口以及jdk提供的Service类和Qname类
*/
public class Demo {
public static void main(String[] args) throws Exception {
//通过客户端编程的方式调用WebService
//步骤1:使用wsimport命令生成我们需要的客户端接口
//步骤2:使用Service,创建Service类的实例
//Service不可被直接通过构造方法来创建实例,在内部提供了一个静态create()方法
//create()方法当中需要传入两个对象作为参数,第一个对象就是封装了调用的WebService 的wsdl的url地址的URL对象
//第二个参数是一个QName对象,该QName对象,用于描述我们要获取WSDL描述文件当中的 哪一个server服务,相当于是
//WSDL说明文档的定位符。
//QName构造方法的参数
//参数1:targetNamespace="http://main.webs.mao.com/"
//参数2:name="MyFirstWebServiceService"
Service service = Service.create(
new URL("http://172.20.184.167:4567/one?wsdl"),
new QName("http://main.webs.mao.com/", "MyFirstWebServiceService")
);
//步骤3:通过创建的service对象,调用getPort()方法,获取我们定义的接口的实例对象。 进行调用服务。
MyFirstWebService myFirstWebService = service.getPort(
new QName("http://main.webs.mao.com/","MyFirstWebServicePort"), MyFirstWebService.class
);
//步骤4:调用服务
String sayHello = myFirstWebService.sayHello("maokexing123123");
System.out.println(sayHello);
}
}
调用WebService方式总结
一共有四种方式来调用WebService服务,分别为
- 通过wsimport命令的方式生成客户端代码
- 通过ajax来调用WebService服务
- 通过URLConnection调用WebService服务
- 通过客户端编程来调用WebSerivce服务
三种监听工具
监听WebService的调用
1、使用eclipse的工具 WebServiceExplorer
2、使用eclipse的工具TCP/IP Monitor
3、HttpWatch来监控WebService
SOAP请求过程分析
第一步:使用get方式获取wsdl文件,称为握手。
对于JDK1.6生成的ws服务,由于内部有一两个配置文件,所以会发出两次get请求。
其他的一般为一次。
第二步:用户发出请求将使用post方式。
第三步:服务器响应成功。
通过注解修改WSDL文件
WSDL文件的内容,一般由服务默认生成,但为了更好的向开发人员提供使用说明书,一般应做一些简单的修改。至少不应该暴露我们的包结构。而targetNamespace默认情况下为倒置的包名,这已经暴露了我们的包结构。
通过在类文件上添加以下注解,可以修改wsdl生成的各元素,而不是直接去修改wsdl文件,直接去修改wsdl文件是无效的。
WebService的注解包括:
@WebService-定义服务 --类上
@WebMethod-定义方法 - 方法
@WebResult-定义返回值 – 返回值
@WebParam-定义参数 – 参数
@WebService-定义服务 --类上
我们定义的默认WebService服务,在生成WSDL文件的时候,会根据默认的信息,将我们的包结构暴露在WSDL文件当中,我们可以通过@WebService注解来更改生成的WSDL中的namespace节点的内容
//通过@WebService注解当中的参数属性targetNamespace,定义生成的WSDL文件当中的服务信息
@WebService(targetNamespace="http:www.firstWebService.com")
public class MyFirstWebService {
@WebMethod-定义方法 - 方法
更改WSDL文件当中的WebService的方法信息 方法名称
//通过@WebMethod()注解当中的operationName来修改WSDL文档当中的方法名称
@WebMethod(operationName="hello")
public String sayHello(String name) {
name = new String(name.getBytes("utf-8"),"gbk");
System.out.println("web service 服务被调用了"+name);
return "hello"+name;
}