# 什么是 webService

webService 也称为 web 服务,它是一种跨编程语言和操作系统平台的远程调用技术。
WebService 采用标准的 SOAP 协议传输 (SOAP Simple Object Access Protocol 简单对象访问协议),soap 属于 w3c 标准,并且 soap 协议是基于 http 的应用层协议传输 xml 数据。WebService 采用 WSDL 作为描述语言,也就是 WebService 的使用说明书。并且 W3C 为 WebService 制定了一套传输数据类型,使用 xml 进行描述,即 XSD (XML Schema Datatypes), 任何语言写的 webService 接口在发送数据的时候都要转成 webService 标准的 XSD 发送。

# WebService 的三要素

# SOAP

SOAP 也叫做简单对象访问协议,是一种简单的基于 xml 的协议,它使应用程序通过 HTTP 来交换数据,可以简单的理解为 SOAP = http + xml 。 SOAP 协议目前的主流版本为 SOAP1.1 和 SOAP1.2(SOAP1.2 是被纳入 w3c 标准后的版本)。SOAP 也不是 WebService 的专有协议,其他的应用程序也是用 SOAP 传输数据,例如:tr069 也是使用 SOAP 协议来传输数据

区分 http 请求和 SOAP (http + xml) 请求:
图片

SOAP 协议格式

  1. 必须有 Envelope 元素,此元素将整个 xml 文档表示为一条 SOAP 消息。
  2. 可选 Header 元素,包含头部信息。
  3. 必须有 Body 元素,包含所有的调用和响应信息。
  4. 可选的 Fault 元素,提供有关在处理此消息所发生的错误信息。

SOAP1.1 和 SOAP1.2 的区别

相同点:
1. 请求方式都是采用 POST 方式
2. 协议内容相同,都有 Envelope 和 Body 标签

不同点:
1. 数据格式不同:content-type 不同
- SOAP1.1:text/xml;charset=utf-8
- SOAP1.2:application/soap+xml;charset=utf-8
2. 命名空间不同
- SOAP1.1:http://schemas.xmlsoap.org/soap/envelope/
- SOAP1.2:http://www.w3.org/2003/05/soap-envelope

# WSDL

WSDL 是基于 XML 的用于描述 WebService 及其函数(方法)、参数和返回值。也就是说 wsdl 是对发布出来的服务中的方法和返回值以及参数的描述(也可以说是 WebService 的使用说明书)

WSDL 文档结构:

WSDL 文档主要包括的 5 个标签

  1. : 服务视图,WebService 的服务节点,它包括服务端点
  2. : 为每个服务端点定义消息格式和协议细节
  3. : 服务端点,描述 WebService 可执行的操作方法,以及相关消息,通过 binging 指向 protType
  4. : 定义一个操作(方法)的数据参数(可有多个参数)
  5. : 定义 WebService 使用的全部数据类型

# UDDI

UDDI 是一种目录服务,通过它,企业可注册并搜集 Web Service。企业将自己提供的 Web Service 注册在 UDDI,也可以使用别的企业在 UDDI 注册 Web Service 服务,从而达到资源共享。UDDI 旨在将全球的 Web Service 资源进行共享

# WebService 开发规范

# JAX-WS

JAX-WS (Java API for XML-Based Web Service): 一个远程调用可以转换基于 XML 协议 (例如:SOAP 协议), 在使用 JAX-WS 过程中,开发者不需要使用任何代码来编写生成和处理 SAOP。JAX-WS 运行时会自动将这些 API 调用转换为 SOAP 协议的消息。

在服务端,用户只需要通过 JAVA 语言定义远程调用所需要实现的接口 (SEI: Service EndPoit Interface), 并对其提供相关的实现,通过调用 JAX-WS 的服务来调用接口,这样就可以发布 Web Service 接口了

在客户端,用户可以通过 JAX-WS 的 API 创建一个代理来(用本地代理对象替代远程的服务对象)实现远程服务端调用。

从 Java5 开始就支持 JAX-WS2.0 版本,Java6 以后的版本支持 JAX-WS2.1 版本,Java1.7 支持 JAX-WS2.2 的版本。

# JAXM&SAAJ

JAXM(Java API for XML Message):主要定义了包含接收信息和发送信息所需要的 API,SAAJ (SOAP With Attachment API For Java) 是与 JAXM 搭配使用的 API,为构建 SOAP 和解析 SOAP 包提供了重要的支持,支持附件传输等。

# JAX-RS

JAX-RS 是 Java 针对 REST(Representtation state Transfer)风格制定的一套 Web 服务规范,由于该规范推出来的较晚,因此该规范(JAX-WS 的版本为 1.0)并未随 Java6 一起发行。

# WebService 应用场景

适用场景:
1. 用于软件集成和复用
2. 用于接口服务,不考虑客户端类型,不考虑性能
3. 服务端已经确定使用了 WebService,客户端只能选择 WebService 使用

不适用场景:
1. 对性能要求比较高 (因为 WebService 是采用 http 发送 soap 协议的数据,该协议迭代了太多标签,导致数据跟多,因此性能也有所降低)
2. 同构程序之间不建议使用

# wsimport 命令介绍

wsimport 命令是 jdk 自带的 webService 客户端工具,可以根据 wsdl 文档生成对应服务代理类(客户端调用类),当然不管服务端用什么语言写的,都可以生成调用 WebService 的客户端代码,服务端通过客户端调用 WebService。

wsimport 命令常用参数为:

  • -d <目录>: 指定放置生成的输出文件的位置
  • -s <目录>: 指定放置生成的源文件的位置
  • -p <包名>: 指定目标程序包

例如:在 com->test 文件夹中生成源文件

wsimport -p com.test -s . http://webService.webxml.com.cn/WebService/MobileCOdeWS.asmx?wsdl

注意:-s . 表示在 -p 指定的文件夹下生成源文件

# 发布 JAX-WS 的 Web Service 服务

  1. 创建 SEI 接口 (本质上就是 Java 接口)
public interface WeatherInterface {
    public String querryWeather(String cityName);
}
  1. 创建接口实现类
  1. @WebService 注解:表示该实现类是一个 Web Service 服务。

    • targetNamespace 属性:指定命名空间。

    • name 属性:指定 portType 的名称。

    • serviceName 属性:服务名称。

  2. @WebMethod 注解:定义公共方法。

    • operationName 属性:方法的名称。(也就是 WSDL 中的 operation 的名称)

    • exclude 属性:如果设置为 true 表示该方法不是 Web Service 服务中的方法。反之则是 WebService 中的方法。默认也是 > false。

  3. @WebResult 注解:定义返回值。

    • name 属性:返回结果值的名称
  4. @WebParam 注解:定义参数。

    • name 属性:指定参数的名称

@WebService(targetNamespace="http://service.cn_lc",name="WeatherWSSoap",portName="WeatherWSSoapPort",serviceName="WeatherWS"
)//只加这个注解就只能生成SAOP1.1的WSDL
//@BindingType(SOAPBinding.SOAP12HTTP_BINDING)
public class WeatherInterfaceImpl implements WeatherInterface {
    @WebMethod(operationName="getWeather",exclude=false)
    @Override
    @WebResult(name="resultWeather")
    public  String querryWeather(@WebParam(name="cityName")String cityName) {
        System.out.println("form client ..." + cityName);
        String weather = "晴";
        return weather;
    }
}

  1. 通过 EndPoint 发布 WebService 服务 (EndPoint 只能发布实现类,不能发布接口)

public class WeatherServer {
    public static void main(String[] args) {
        /*** 参数解释:* address:服务器地址* implementor:实现类*/
        Endpoint.publish("http://127.0.0.1:12345/weather",new WeatherInterfaceImpl());
        System.out.println("http://127.0.0.1:12345/weather?wsdl");
        }
}

图片
图片

# WebService 的四种客户端调用方式

第一种:通过 wsimport 生成客户端方式调用
1. 通过 wsimport 生成客户端代码
wsimport -p com.test jaxws -s http://127.0.0.1:12345/weather?wsdl
2. 阅读使用说明书 WSDL,使用生成客户端代码调用服务端


public class WeatherClient {
    public static void main(String[] args) {
        //创建服务视图
        WeatherWS weatherInterfaceImplService = new WeatherWS();
        //通过服务视图对象获取服务实现类
        WeatherWSSoap weatherInterfaceImpl = weatherInterfaceImplService.getPort(WeatherWSSoap.class);
        //通过服务实现对象调用查询方法
        System.out.println(weatherInterfaceImpl.getWeather("北京"));
    }
}

采用 wsimport 生成客户端代码方式的特点:这种方式使用简单,但是一些关键的元素 (比如 wsdl 地址、命名空间、服务类名等都写死在生成的客户端代码中) 不方便维护


第二种:通过 Service 编程调用方式
1. 通过 wsimport 生成客户端代码
wsimport -p com.test jaxws -s http://127.0.0.1:12345/weather?wsdl
2. 自己编写服务视图类,并通过该服务视图类来获取服务实现类实例


public class WeatherClient {
    public static void main(String[] args) {
        //创建WSDL的URL,注意不是服务地址
        URL url = new URL("http://127.0.0.1:12345/weather?wsdl");
        // 创建服务名称//namespaceURI:命名空间地址。//localPart:服务视图名。
        QName qname = new QName("http://service.cn_lc", "WeatherWS");
        //创建服务视图//1.wsdlDocumentLocation - wsdl地址//2.serviceName - 服务名称
        Service service = Service.create(url, qname);
        WeatherWSSoap weatherWSSoap = service.getPort(WeatherWSSoap.class);
        String result = weatherWSSoap.getWeather("成都");
        System.out.println(result);
    }
}


第三种:通过 HttpURLConnection 调用方式
1. 创建服务地址
2. 打开一个通向服务地址的连接
3. 设置参数(例如请求方式为 POST)
4. 组织 SOAP 数据发送数据
5. 接收服务端相应,并打印


public class WeatherClient {
    public static void main(String[] args) throws IOException {
        //第一步:创建服务地址
        URL url = new URL("http://127.0.0.1:54321/weather");
        // 第二步:打开一个通向服务端地址的连接
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        //设置参数
        connection.setRequestMethod("POST");
        connection.setRequestProperty("content-type", "text/xml;charset=utf-8");
        //设置输入输出
        connection.setDoOutput(true);
        connection.setDoInput(true);
        //准备SOAP数据,发送请求
        String soapxml = getXML("成都");
        OutputStream out = connection.getOutputStream();
        OutputStreamWriter writer = new OutputStreamWriter(out, "utf-8");
        writer.write(soapxml);
        writer.close();
        out.close();
        //第五步接收服务端响应并打印
        if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
            InputStream input = connection.getInputStream();
            InputStreamReader reader = new InputStreamReader(input, "utf-8");
            BufferedReader buffered = new BufferedReader(reader);
            String temp = null;
            StringBuilder sb = new StringBuilder();
            while ((temp = buffered.readLine()) != null) {
                sb.append(temp);
            }
            buffered.close();
            reader.close();
            input.close();
            System.out.println(sb.toString());
        }
    }

    public static String getXML(String cityName) {
        return "<?xml version=\"1.0\" ?>" + "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\">" + "<S:Body>" + "<ns2:getWeather xmlns:ns2=\"http://service.cn_lc\">" + "<cityName>" + cityName + "</cityName>" + "</ns2:getWeather>" + "</S:Body>" + "</S:Envelope>";
    }
}


第四种:通过 ajax 方式调用服务端


<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>查询天气</title>
    <script type="text/javascript">function querryWeather() {
            var xhr = new XMLHttpRequest(); xhr.open("post", "http://127.0.0.1:12345/weather", true);
            //设置数据类型
            xhr.setRequestHeader("content-type", "text/xml;charset=utf-8");
            //设置回调函数
            if (4 == xhr.readState && 200 == xhr.status) {
                alert(xhr.responseText);
            }
            //组织数据
            var soapXml = "<?xml version=\"1.0\" ?>" + "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\">" + "<S:Body>" + "<ns2:getWeather xmlns:ns2=\"http://service.cn_lc\">" + "<cityName>" + document.getElementById("cityName").value + "</cityName>" + "</ns2:getWeather>" + "</S:Body>" + "</S:Envelope>"; alert(soapXml);
            //发送数据
            xhr.send(soapXml);
        }
    </script>
</head>

<body>
    <center>天气查询:<input type="text" id="cityName" /><input type="button" value="查询"
            οnclick="javascript:querryWeather();" /></center>
</body>

</html>

# CXF

CXF 是一个开源的 WebService 框架,提供了很多完善的功能
CXF 支持的协议有 SOAP1.1/SOAP1.2,REST
CXF 支持的数据格式有 XML,JSON (仅在 REST 方式下支持,不再 SOAP 方式下支持,因为 SOAP 使 http+xml)