WebService

引言

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
16
import javax.jws.WebService;


/**
*
* 气温查询实现类
*/
@WebService
public class QueryWeatherImpl implements QueryWeather{

@Override
public String queryNowTemperature() {
return "28度";
}

}

第三步,通过Endpoint发布服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import 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
15
import 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
36
import 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类中的代码,是可以看到上面的这些参数的。

------ 本文完 ------