引言
WebService是一种跨编程语言和跨操作系统平台的远程调用技术。说道远程调用,很容易联想到另一个远程调用技术Dubbo,的确,WebService可以理解为就是想Dubbo类似的一种技术。当然他们也是有不同点的,Dubbo只实现了有限集中语言的SDK(例如Java,Golang),Dubbo支持多种rpc协议,包括dubbo协议、http协议、hessian协议,甚至是webservice协议。而WebService是跨编程语言和跨操作平台,就是说比如服务端程序可以用Java编写部署在Linux平台,客户端程序采用其他编程语言编写部署在Windows上也是可以的。
WebService三个重要概念
UUDI,SOAP和WSDL是构成WebService技术中三个最重要的概念。
UUDI
UDDI (Universal Description, Discovery and Integration,通用描述、发现与集成服务)是一种目录服务,通过它,企业可以使用它对 WebServices 进行注册和搜索。企业将自己提供的WebService注册在UDDI,也可以使用别的企业在UDDI注册的WebService服务,从而达到资源共享。 UDDI旨在将全球的WebServcie资源进行共享,促进全球经济合作。
目前大部分企业使用webservice并不是必须使用UDDI,因为用户通过WSDL知道了WebServcie的地址,可以直接通过WSDL调用WebServcie。
WSDL
WebService Description Language,WebService服务器端首先要通过一个WSDL文件来说明自己有什么服务可以对外调用。简单的说,WSDL就像是一个说明书,用于描述WebService及其方法、参数和返回值。 WSDL文件保存在Web服务器上,通过一个url地址就可以访问到它。客户端要调用一个WebService服务之前,要知道该服务的WSDL文件的地址。通过这个地址获取到WSDL文件后,可以通过一些开发工具,生成相应WebService的代理类代码,客户端引用这些代码后,就可以调用WebService服务了。
WebService服务提供者可以通过两种方式来暴露它的WSDL文件地址:1.注册到UDDI服务器,以便被人查找;2.直接告诉给客户端调用者。
SOAP
SOAP即简单对象访问协议(Simple Object Access Protocal)。是一种简单的基于 XML 的协议,SOAP底层是通过HTTP协议发送请求和接收结果,发送的请求内容和结果内容都采用XML格式封装,并增加了一些特定的HTTP消息头,以说明HTTP消息的内容格式。所以我们可以简单理解为:soap=http+xml。Soap协议版本主要使用soap1.1、soap1.2。
SOAP不是webservice的专有协议,其他应用协议也使用soap传输数据。例如,SMTP、tr069等。
总结
总结来说,WebService交互的过程就是:WebService遵循SOAP协议通过XML封装数据,然后由Http协议来传输数据。
PS:从上面可以看出WebService是在Http协议的基础上做了进一步的封装,所以WebService的效率是不如Http的。
使用示例
服务端
我们先来搭建一个WebService的服务端(服务提供者)。
第一步,编写SEI(Service Endpoint Interface),SEI在webservice中称为portType,在java中称为接口。在本示例中我们创建一个名为QueryWeather
的接口。1
2
3
4
5
6
7
8
9
10
11
12
13/**
*
* 气温查询接口
*/
public interface QueryWeather {
/**
* 查询当前的气温
*/
public String queryNowTemperature();
}
第二步,编写SEI实现类,此类作为WebService提供服务的类,实现类要加上@WebService注解。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import javax.jws.WebService;
/**
*
* 气温查询实现类
*/
public class QueryWeatherImpl implements QueryWeather{
public String queryNowTemperature() {
return "28度";
}
}
第三步,通过Endpoint发布服务。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import javax.xml.ws.Endpoint;
public class QueryWeatherServer {
public static void main(String[] args) {
/*
* publish方法的2个参数:
* address: 服务地址 --> 提供访问的地址
* implementor: 服务类 --> 提供访问的接口的实现类
*/
Endpoint.publish("http://localhost:9090/queryWeather", new QueryWeatherImpl());
System.out.println("服务发布成功");
}
}
运行main方法后,查看服务的WSDL文件,验证服务端是否发布成功。
客户端
客户端想要调用WebService服务端提供的服务,首先要根据WSDL文件生成对应的代理类文件(.class文件),把这些.calass文件打成一个jar包,然后在客户端引入该jar。
第一步,使用 wsimport 生成WebService客户端所需要的.class文件。1
wsimport -d d:\WebService\client-jar http://localhost:9090/queryWeather?wsdl
wsimport是jdk自带的webservice客户端工具,wsimport.exe位于JAVA_HOME\bin目录下。它可以根据WSDL文件生成Java客户端调用WebService服务所需要的代码(不管服务端是否是Java编写的)。
常用参数为:
- -d <目录> :生成.class文件到指定目录。默认参数。
- -s <目录> :生成.java文件到指定目录。
- -p <包名> :将生成的文件(.java或.class),修改成指定的包名。例如:
wsimport -s . -p com.lzumetal.demo.webservice http://localhost:9090/helloworld?wsdl
。
第二步,将.class文件打成一个jar包。
在上面的d:\WebService\client-jar
目录下,执行如下命令。1
jar cvf queryweather-webservice.jar com
其中参数 queryweather-webservice.jar 是打包后的jar包名称,参数 com 是要打包的文件目录。
第三步,在客户端引入生成好的jar包,并调用服务端提供的接口。
在resources目录下新增目录lib,将jar复制到lib目录下,然后在IDEA中引入该jar到客户端项目。
编写客户端调用服务的代码。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import com.lzumetal.springboot.webservice.server.QueryWeatherImpl;
import com.lzumetal.springboot.webservice.server.QueryWeatherImplService;
public class QueryWeatherClient {
public static void main(String[] args) {
QueryWeatherImplService queryWeatherImplService = new QueryWeatherImplService();
QueryWeatherImpl queryWeatherImpl = queryWeatherImplService.getQueryWeatherImplPort();
System.out.println("查询当前温度为:" + queryWeatherImpl.queryNowTemperature());
}
}
在服务端运行的情况下,执行main方法,可以得到如下结果。1
查询当前温度为:28度
此外,还有另外一种客户端的调用方式,而且在实际开发中更多的也是采用此种方式,客户端的代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36import com.lzumetal.springboot.webservice.server.QueryWeatherImpl;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import java.net.URL;
public class QueryWeatherClient2 {
private QueryWeatherImpl queryWeatherImpl;
public static void main(String[] args) throws Exception {
// 1. 设置访问的服务端地址
URL url = new URL("http://localhost:9090/queryWeather?wsdl");
/*
2.设置服务名称和命名空间
namespaceURI: wsdl的命名空间(targetNamespace),其实就是服务提供者的包名翻转后的字符串
localPart: 是服务视图的名称(service的name值)
*/
QName qName = new QName("http://server.webservice.springboot.lzumetal.com/", "QueryWeatherImplService");
// 3. 生成服务视图
Service service = Service.create(url, qName);
// 4. 得到服务视图的实现类 --> WeatherServiceImpl
QueryWeatherImpl weatherServiceImpl = service.getPort(QueryWeatherImpl.class);
// 5. 调用接口方法得到结果
String temperature = weatherServiceImpl.queryNowTemperature();
System.out.println("查询当前温度为:" + temperature);
}
}
在这种方式中,如果WSDL文件的地址变化了(即服务端地址发生变化),我们就不需要重新去打一个jar,而只需要修改URL url = new URL("http://localhost:9090/queryWeather?wsdl");
这行代码中的地址就行了。
其实,在第一种方式中只是把上面的几个参数隐藏了,当我们查看QueryWeahterImplService
类中的代码,是可以看到上面的这些参数的。