认识 WebService

 什么是服务

1)现在的应用程序变得越来越复杂,甚至只靠单一的应用程序无法完成全部的工作。更别说只使用一种语言了。

2)大家在写应用程序查询数据库时,并没有考虑过为什么可以将查询结果返回给上层的应用程序,甚至认为,这就是数据库应该做的,其实不然,这是数据库通过 TCP/IP 协议与另一个应用程序进行交流的结果,而上层是什么样的应用程序,是用什么语言,数据库本身并不知道,它只知道接收到了一份协议,这就是SQL92查询标准协议。

3)既然数据库可以依据某些标准对外部其他应用程序提供服务、而且不关心对方使用什么语言,那我们为什么就不能实现跨平台、跨语言的服务呢?只要我们用Java写的代码,可以被任意的语言所调用,我们就实现了跨平台,跨语言的服务!

复杂的网络应用

WebService 定义:顾名思义就是基于Web的服务。它使用Web(HTTP)方式,接收和响应外部系统的某种请求,从而实现远程调用。

Webservice 理解:我们可以调用互联网上查询天气信息Web服务,然后将它嵌入到我们的程序(C/S或B/S程序)当中来,当用户从我们的网点看到天气信息时,他会认为我们为他提供了很多的信息服务,但其实我们什么也没有做,只是简单了调用了一下服务器上的一段代码而已WebService 可以将你的服务(一段代码)发布到互联网上让别人去调用,也可以调用别人机器上发布的 WebService,就像使用自己的代码一样。

既然 WebService 是一种远程调用技术,那么最直接的问题就是:1、我怎么去调用别人的程序。2、我的程序怎么才能被别人调用

作为一种通用技术,那么是否有通用的使用说明书呢?答案是肯定的:WSDL 就是 WebService 的说明书(网上有更详细的教程,这里不特别介绍)

WSDL:WebService Description Language——Web服务描述语言。 通过XML形式说明服务在什么地方——地址。通过XML形式说明服务提供什么样的方法——如何调用。

分享一个 Webservice 服务网站:http://www.webxml.com.cn

WSDL文档从下往上读,它包含几个值得注意的关键节点

Types数据类型定义的容器,它使用某种类型系统(一般地使用XML Schema中的类型系统)。(入参和出参的数据类型) 

Message:通信消息的数据结构的抽象类型化定义。使用Types所定义的类型来定义整个消息的数据结构(入参和出参)。 

Operation:对服务中所支持的操作的抽象描述,一般单个Operation描述了一个访问入口的请求/响应消息对(方法)。 

PortType:对于某个访问入口点类型所支持的操作的抽象集合,这些操作可以由一个或多个服务访问点来支持(服务类)。 

Binding:特定服务访问点与具体服务类的绑定(不看内容,看关系)。 

Port:定义为webservice单个服务访问点。 

Service:相关服务访问点的集合。

下面演示 jdk 怎样通过 WSDL 生成客户端代码,通过客户端代码完成调用。

1. wsimport 是 jdk 自带的,可以根据 wsdl 文档生成客户端调用代码的工具。

2. 无论服务器端的 WebService 是用什么语言写的,都将在客户端生成Java代码。服务器端用什么写的并不重要。

3. wsimport.exe位于 JAVA_HOME\bin 目录下。

常用参数为:

1)-d<目录>  - 将生成.class文件。默认参数。

2)-s<目录>  - 将生成.java文件和class文件。

3)-p<生成的新包名> -将生成的类,放于指定的包下。

(wsdlurl) - http://server:port/service?wsdl,必须的参数。

注意:-s不能分开,-s后面有个小点,用于指定源代码生成的目录。点即当前目录。如果使用了-s参数则会在目录下生成两份代码,一份为.class代码。一份为.java代码。.class代码,可以经过打包以后使用。.java代码可以直接Copy到我们的项目中运行。

我们在F盘下新建一个文件夹,将生成的代码保存在此文件夹中。现在文件夹里是空的

 

打开命令行,输入java命令。参照上面的命令解释,下面命令的意思是:生成 java 文件和 class 文件,创建子文件夹来放这些文件

最后生成的结果如下

上面是通过直接访问WSDL的URL这种方式来生成的,我们也可以把 WSDL下载下来,比如,放在f:/wsCode/EnglishChinese.wsdl

 

用下面的命令

 

生成效果是一样的

注意:如果用第一种方式生成报错,那么就把WSDL下载下来,删除报错提示的那几行,然后用第二种方式重新生成

生成的class文件是不需要的,只需要 java 文件,接下来就是使用这些java文件。

第一步:将jdk生成的java文件复制到项目中

写测试方法进行调用测试。这其实就是调用普通的Java方法,但是需要读懂 WSDL 明白创建哪些对象,调用哪些方法,这是关键!

调用WebService步骤

1)打开WSDL文档

2)从下往上读WSDL文档,先找到Services(服务访问点集合),根据Services里面binding属性找到binding元素,再根据binding元素的type属性找到绑定的portType(服务类)

3)根据WSDL的地址生成客户端代码 wsimport -s . -p com.jwen.trans d:/wsCode/EnglishChinese.wsdl

4)把客户端代码拷贝到项目中

5)创建服务访问点集合对象

6)根据服务访问点获得服务类

7)调用服务类的方法

下面演示jdk发布 WebService 服务

注意:用 jdk1.6.0_21 以后的版本发布一个 WebService 服务。在JDK1.6中JAX-WS规范定义了如何发布一个WebService服务。 JAX-WS是指Java Api for XML – WebService。

与Web服务相关的类,都位于javax.xml.ws.*包中。主要类有:

a)@WebService:它是一个注解,用在类上指定将此类发布成一个webservice服务.

b)Endpoint:此类为端点服务类,它的方法publish用于将一个已经添加了@WebService 注解对象绑定到一个地址的端口上。Endpoint 是 jdk 提供的一个专门用于发布服务的类,它的publish方法接收两个参数,一个是本地的服务地址,二是提供服务的类。它位于 javax.xml.ws.* 包中。

public static Endpoint.publish(String address, Object implementor) 在给定地址处针对指定的实现者对象创建并发布端点。stop方法用于停止服务。

其他注意事项:

1)给类添加上@WebService注解后,类中所有的非静态方法都将会对外公布。不支持静态方法,final方法。

2)  如果希望某个方法(非static,非final)不对外公开,可以在方法上添加@WebMethod(exclude=true),阻止对外公开。

3)  如果一个类上,被添加了@WebService注解,则必须此类至少有一个可以公开的方法,否则将会启动失败。

4)  服务类中不能没有方法

5)  @WebMethod(exclude=true)屏蔽方法

 项目结构如下图所示

HelloServer.java 的代码如下:

@WebService
public class HelloServer {
    /**
     * 1.需要方法权限是public
     * 2.不能是final类型
     * 3.方法不能是静态的
     * 4.服务类至少有一个方法
     * @param name
     * @return
     */
    public String sayHello(String name){
        return name + " hello!";
    }

    @WebMethod(exclude=true)
    public String sayBye(String name){
        return name + " bye!";
    }
    
}

ServerPublish.java 的代码如下:

public class ServerPublish {
    
    public static void main(String[] args) {
        //jdk发布webservice服务, 第一个参数服务地址,第二个参数具体服务类
        Endpoint.publish("http://127.0.0.1:8080/hello", new HelloServer());
    }

}

运行 main 方法后,在浏览器地址栏中输入 http://127.0.0.1:8080/hello?wsdl 查看 WSDL,可见,WebService成功发布。可以用前面介绍的方式进行调用。

调用WebService的方式是不是只有调用客户端代码这一种呢?显然不是,还是有其他方式的。

下面演示 ajax 调用WebService,注意运行前设置IE可信任站点和自定义级别

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
    var xhr;
    
    function invoke(){
        //创建ajax对象
        xhr = new ActiveXObject("Microsoft.XMLHTTP");
        //指定要访问的地址,就是我们WSDL地址,说明书
        var url = "http://127.0.0.1:8080/hello?wsdl";
        //打开连接,参数1.请求方式, 2,url地址. 3.是否同步,true异步,false同步
        xhr.open("POST", url, true);
        //指定发送的数据类型
        xhr.setRequestHeader("Content-Type", "text/xml;charset=UTF-8");
        //设置回调函数
        xhr.onreadystatechange = _back;
        var mytext = document.getElementById("mytext").value;
        //定义消息体
        var data ='<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:q0="hello.ren.liang" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
            +'<soapenv:Body>'
            +'<q0:sayHello>'
            +' <arg0>'+mytext+'</arg0>'
            +'</q0:sayHello>'
            +'</soapenv:Body>'
            +'</soapenv:Envelope>';
        
        //发送消息体
        xhr.send(data);
    }
    //回调函数
    function _back(){
        //判断成功状态
        if(xhr.readyState == 4 && xhr.status == 200){
            //以文本形式
            var result = xhr.responseText;
            //以xml文档对象
            var obj = xhr.responseXML;
            //解析文档
            var returns = obj.getElementsByTagName("return");
            alert(returns[0].text);
        }
    }
</script>
</head>
<body>
<input type="text" id="mytext">
<input type="button" value="click" onclick="invoke();">
</body>
</html>

Java 也可以通过 WSDL 直接调用 WebService

package com.rl.client;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;


public class TestHttpURLConnClient {
    
    public static void main(String[] args) throws Exception {
        //定义webservice的URL
        URL url = new URL("http://127.0.0.1:8080/hello?wsdl");
        //打开连接获得URLConnection
        URLConnection uc = url.openConnection();
        //强转成HttpURLConnection
        HttpURLConnection httpuc = (HttpURLConnection)uc;
        //打开输入输出的开关
        httpuc.setDoInput(true);
        httpuc.setDoOutput(true);
        //设置请求方式
        httpuc.setRequestMethod("POST");
        //设置content-type  text/xml;charset=UTF-8
        httpuc.setRequestProperty("Content-Type", "text/xml;charset=UTF-8");
        //组装消息体
        String data = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:q0=\"http://server.rl.com/\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
                +"<soapenv:Body>"
                +" <q0:sayBye>"
                +"    <arg0>wangwu</arg0>" 
                +" </q0:sayBye>"
                +" </soapenv:Body>"
                +"</soapenv:Envelope>";
        //根据HttpURLConnection获得输出流
        OutputStream out = httpuc.getOutputStream();
        //用输出流把消息发送到服务端
        out.write(data.getBytes());
        //如果请求成功
        if(httpuc.getResponseCode() == 200){
            //获得输入流
            InputStream in = httpuc.getInputStream();
            //使用输入缓冲区
            BufferedReader br = new BufferedReader(new InputStreamReader(in));
            //读取响应的消息
            StringBuffer sb = new StringBuffer();
            String line = null;
            while((line = br.readLine()) != null){
                sb.append(line);
            }
            //解析消息,定义SAXReader对象
            SAXReader  reader = new SAXReader();
            //获得文档对象
            Document doc = reader.read(new StringReader(sb.toString()));
            //使用XPath的方式获得到return这个元素的集合
            List<Element> eList = doc.selectNodes("//return");
            //遍历元素集合
            for(Element ele : eList){
                System.out.println(ele.getText());
            }
        }
        
    }

}

上面的两种方式共同点都是需要知道消息体的组织方式,与客户端调用相比较,上面的两种方式不需要导出代码,但需要组织消息体,而客户端调用对参数的处理则方便的多,因为对象都是现成的。

关于怎样查看消息体,参考https://www.cnblogs.com/jwen1994/p/10589150.html

自动生成的 WSDL 文档的名字有时不规范,可以手动进行修改。

修改前的普通代码

@WebService
public class HelloServer {

    @WebMethod(exclude=false)
    public String sayHello(String name){
        return name + " hello!";
    }
    
    @WebMethod(exclude=false)
    public  String sayBye(String name){
        return name + " bye!";
    }    
}

修改后的代码

@WebService(
        serviceName="MyHelloServerService",
        portName="MyHelloServer",
        name="MyHelloServer",
        targetNamespace="hello.jwen.com"
        )
public class HelloServer {
    /**
     * 1.需要方法权限是public
     * 2.不能是final类型
     * 3.方法不能是静态的
     * 4.服务类至少有一个方法
     * @param name
     * @return
     */
    @WebMethod(exclude=false)
    public String sayHello(String name){
        return name + " hello!";
    }
    
    @WebMethod(exclude=false)
    public @WebResult(name="byeResult") String sayBye(@WebParam(name="personName") String name){
        return name + " bye!";
    }
    
    
    
}

 参数说明:

 @WebService(

  portName="myHelloService",修改端口名字

  serviceName="HelloServices",修改服务访问点集合名字

  name="HelloService",修改服务类的名字

  targetNamespace="hello.rl.com" 修改命名空间名字

)

@WebResult(name="sirHello")修改返回值的元素的父标签名字

@WebParam(name="sir")修改传入参数的元素的父标签名字

猜你喜欢

转载自www.cnblogs.com/jwen1994/p/10585297.html