天天看点

WebService入门详解

1、什么是webservice

先来考虑一个问题,如果我们要在自己的程序里面展示天气预报,那怎么弄?正确的做法是我们发送一个请求到一个系统,他会给我们返回来天气情况。这个就是一个webservice。天气预报系统就相当于webservice的服务端,我们的系统就相当于客户端。如http://www.webxml.com.cn这个网站上面就列举了多个webservice服务站点

2、JAVA中如何调用别人发布的webservice

2.1、获取webservice的wsdl文档

2.11、什么是wsdl文档

wsdl跟java一样,也是一种语言,是通过xml的形式说明该webservice如何调用。

2.12、如何获取wsdl文档

通过在webservice的url后面加?wsdl的方式,比如天气预报的的就是http://ws.webxml.com.cn/WebServices/WeatherWS.asmx?wsdl

2.13、WSDL解析

Wsdl文档从下往上读

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

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

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

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

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

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

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

2.2、通过wsdl文档生成客户端调用代码

2.21、使用jdk自带的命令生成

配置java环境变量后在命令窗口中输入wsimport –s . http://xxxx.xx.xx/xxx?wsdl即可生成java代码

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

注意:可能会报错解析组件 ‘s:schema’ 时出错。在该组件中检测到 's:schem’之类的,如果报错这个请移步:​​这里​​

2.3、生成代码后如何调用

先把生成的代码复制到项目中,然后通过读wsdl文档来调用,整体的调用代码类似于下面这样

//wsdl文档中service的name
SayHelloIntefaceService ss = new SayHelloIntefaceService();
//wsdl文档中portType的name
SayHelloInteface shf=ss.getSayHelloIntefacePort();
//wdl文档中complexType的name
String str=shf.sayhellow("lisi");
//str即为webservice的服务端返回的信息
System.out.println(str);      

3、一个关于WebService的Demo

3.1、服务端

package com.bxoon;

import javax.jws.WebMethod;


public interface WebServiceI {

    @WebMethod(exclude=true)
    String helloWord(String name);

    @WebMethod(exclude=true)
    String helloWord2(String name);

}      
package com.bxoon;


import javax.jws.WebService;
import javax.xml.ws.Endpoint;


@WebService
public class HelloWebService implements WebServiceI {


    @Override
    public String helloWord(String name) {
        return"Hello: "+name;
    }

    @Override
    public String helloWord2(String name){
        return"Hello: "+name;
    }

    public static void main(String[] args) {
        Endpoint.publish("http://127.0.0.1:8080/helloWord",new HelloWebService());
    }

}      

3.2 客户端

服务端启动之后通过访问http://127.0.0.1:8080/helloWord?wsdl来得到一个wsdl文档,类似于如下

WebService入门详解

然后通过命令

wsimport -s . http://127.0.0.1:8080/helloWord?wsdl      

来生成具体的代码,生成的代码类似于

WebService入门详解

我们把对应的java类copy到客户端项目中,得到这样的目录结构

WebService入门详解

然后我们编写图中的Client类,代码如下

package com.bxoon;

public class Client {

    public static void main(String[] args) {
        HelloWebServiceService service = new HelloWebServiceService();
        HelloWebService webService = service.getHelloWebServicePort();
        String result = webService.helloWord("11");
        System.out.println(result);
    }
}      

运行之后输出结果

Hello: 11      

到此,一个简单的WebService的helloword程序就完成了,下面对具体的代码进行解释。

用Jdk1.6.0_21以后的版本发布一个WebService服务.与Web服务相关的类,都位于javax.xml.ws.*包中。

主要类有:

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

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

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

其他注意事项:

  1. 给类添加上@WebService注解后,类中所有的非静态方法都将会对外公布。不支持静态方法,final方法。
  2. 如果希望某个方法(非static,非final)不对外公开,可以在方法上添加@WebMethod(exclude=true),阻止对外公开。
  3. 如果一个类上,被添加了@WebService注解,则必须此类至少有一个可以公开的方法,否则将会启动失败。
  4. 服务类中不能没有方法
  5. @WebMethod(exclude=true)屏蔽方法

4、其他调用webservice的方式

这里只写了一种最基本的调用webService的方式,实际上调用webService有很多种方式,下面一一介绍。

4.11、 使用ajax调用

var xhr;
function invoke(){
  if(window.ActiveXObject){
    xhr = new ActiveXObject("Microsoft.XMLHTTP");
  }else{
    xhr = new XMLHttpRequest();
  }
  //指定请求地址
  var url = "http://127.0.0.1:7777/hello?wsdl";
  //定义请求类型和地址和异步
  xhr.open("POST", url, true);
  //设置Content-Type
  xhr.setRequestHeader("Content-Type", "text/xml;charset=UTF-8");
  //指定回调方法
  xhr.onreadystatechange = back;

  var textVal = document.getElementById("mytext").value;
  //组装消息体的数据
  var data = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:q0="http://server.hm.com/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
  +'<soapenv:Body>'
  +'<q0:sayHello>'
  +'<arg0>'+textVal+'</arg0>'
  +'</q0:sayHello>'
  +'</soapenv:Body>'
  +'</soapenv:Envelope>';
  xhr.send(data);

}
function back(){
  if(xhr.readyState == 4){
    if(xhr.status == 200){
      var doc = xhr.responseXML;
      alert(doc);
      alert(xhr.responseText);
      var tag = doc.getElementsByTagName("return")[0];
      alert(tag)

    }
  }
}      

4.12、通过URLConnection调用

//创建url地址
URL url = new URL("http://192.168.1.104:8080/hello");
//打开连接
URLConnection conn = url.openConnection();
//转换成HttpURL
HttpURLConnection httpConn = (HttpURLConnection) conn;
//打开输入输出的开关
httpConn.setDoInput(true);
httpConn.setDoOutput(true);
//设置请求方式
httpConn.setRequestMethod("POST");
//设置请求的头信息
httpConn.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:sayHello>"
    +"<arg0>renliang</arg0> "
    +"</q0:sayHello>"
    +"</soapenv:Body>"
    +"</soapenv:Envelope>";
//获得输出流
OutputStream out = httpConn.getOutputStream();
//发送数据
out.write(data.getBytes());
//判断请求成功
if(httpConn.getResponseCode() == 200){
  //获得输入流
  InputStream in = httpConn.getInputStream();
  //使用输入流的缓冲区
  BufferedReader reader = new BufferedReader(new InputStreamReader(in));
  StringBuffer sb = new StringBuffer();
  String line = null;
  //读取输入流
  while((line = reader.readLine()) != null){
    sb.append(line);
  }
  //创建sax的读取器
  SAXReader saxReader = new SAXReader();
  //创建文档对象
  Document doc = saxReader.read(new StringReader(sb.toString()));
  //获得请求响应return元素
  List<Element> eles = doc.selectNodes("//return");
  for(Element ele : eles){
    System.out.println(ele.getText());
  }
}      

4.13、使用jquery调用cxf

$(function(){
    $("#mybutton").click(function(){
      var data = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:q0="http://server.web.cxf.rl.com/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
          +'<soapenv:Body>'
          +'<q0:sayHello>'
          +'   <arg0>sss</arg0>'
          +' </q0:sayHello>'
          +'</soapenv:Body>'
          +'</soapenv:Envelope>';

        $.ajax({
          url:'http://localhost:8080/cxf-web-server/services/hello',
          type:'post',
          dataType:'xml',
          contentType:'text/xml;charset=UTF-8',
          data:data,
          success:function(responseText){
            alert($(responseText).find('return').text());
          },
          error:function(){
            alert("error");
          }
        })
    })
  })      

5、wsdl文档元素名称修改

有时,自动生成的WSDL文档的名字可能不规范,我们是可以自定义的。在注解@webservice中可以指定一些属性来完成这个功能。

@WebService(
  portName="myHelloService",修改端口名字
  serviceName="HelloServices",修改服务访问点集合名字
  name="HelloService",修改服务类的名字
  targetNamespace="hello.rl.com" 修改命名空间名字
)      

或者使用如下方式

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

6、其他发布WebService的方式

其实上面说的使用jdk自带的方式发布WebService只是众多发布WebService的方式中的一种。现阶段发布方式主要有以下5种

  1. CXF(常用)
  2. Xfire(比较古老)
  3. Axis2 (常用)
  4. Axis1
  5. HttpClient

    jws的发布对java webservice框架产生了巨大的影响,经过大浪淘沙,目前java开发webservice的框架主要包括axis2和cxf。

    ​​这篇文章​​介绍了发布WebService的多种方式以及实现,

下面对cxf和Axis2的使用方式做基本介绍,其他方式请自行寻找资料

6.1 CXF

6.1.1、什么是cxf

CXF全称Apache CXF,是apache基金会的一个为了简化webService开发的框架,可以让WebService的发布和调用更加简单。

Apache CXF = Celtix + Xfire

Apache CXF支持多种协议:

a) SOAP1.1,1,2

b) HTTP

c) CORBA(Common Object Request Broker Architecture公共对象请求代理体系结构,早期语言使用的WS。C,c++,C#)

d) 并可以与Spring进行快速无缝的整合

e) 灵活的部署:可以运行有Tomcat,Jboss,Jetty(内置),IBMWS,BeaWS上面。

6.1.2、cxf目录详解(了解)

bin(目录)

bin 目录中是 CXF 框架中所提供的代码生成、校验、管理控制台工具(可执行命令)

docs(目录)

CXF 所有类(class)对应的 API 文档,为开发者使用 CXF 完成应用开发提供应有的帮助。

etc(目录)

包含一个基本的 Service 暴露所需要的 web.xml 文件,及其它的配置文件。

lib(目录)

lib 目录中包含 CXF 及其运行时所需要的和可选的第三方支持类包(.jar 文件),可以根据不同项目所需的 CXF 特性选择所需要的支持类包。如果不想一一去区分的话,可

以直接在 Web 项目中包含所有的 CXF 及其运行时所需要的第三方支持类包(.jar 文件)即可。

其中 cxf-2.0.2-incubator.jar 是 CXF 框架的二进制包文件,包含了全部的模块(modules),cxf-manifest-incubator.jar 是列表清单文件 manifest jar 。

以下的 jar 包是所有 CXF 项目所必需的:

cxf.jar

commons-logging.jar

geronimo-activation.jar (Or the Sun equivalent)

geronimo-annotation.jar (Or the Sun equivalent)

geronimo-javamail.jar (Or the Sun equivalent)

neethi.jar

jaxb-api.jar

jaxb-impl.jar

stax-api.jar

XmlSchema.jar

wstx-asl.jar

xml-resolver.jar

对于 Java2WSDL 和 WSDL2Java,除了必需的之外,还需要再增加如下 jar 包:

jaxb-xjc.jar

veliocity.jar

velocity-dep.jar

为了支持 JAX-WS ,除了必需的之外,还需要再增加如下 jar 包:

jaxws-api.jar

saaj-api.jar

saaj-impl.jar

asm.jar (可选的,但是可以提升包装类型的性能)

为了支持 XML 配置,除了必需的之外,还需要再增加如下 jar 包:aopalliance.jar

spring-beans.jar

spring-context.jar

spring-core.jar

spring.web.jar

为了独立的 HTTP 服务支持,除了必需的之外,还需要再增加如下 jar 包:geronimo-servlet.jar

jetty.jar

jetty-sslengine.jar

jetty-util.jar

sl4j.jar & sl4j-jdk14.jar (可选的,但是可以提升日志 logging)

为了支持 Aegis ,除了必需的之外,还需要再增加如下 jar 包:

jaxen.jar

jdom.jar

stax-utils.jar

为了支持 WS-Security ,除了必需的之外,还需要再增加如下 jar 包:bcprov-jdk14.jar

wss4j.jar

xalan.jar

xmlsec.jar

为了支持 HTTP Binding ,除了必需的之外,还需要再增加如下 jar 包:jra.jar

jettison.jar (仅为 JSON 服务所需的)

licenses(目录)

列表了引用第三方 jar 包的相关许可协议。

modules(目录)

modules 目录中包含了 CXF 框架根据不同特性分开进行编译的二进制包文件。发布基于 CXF 框架的 Web 项目时,可以选择使用该目录下的所有 .jar 文件,也可以选择 lib 目 录中的 cxf-2.0.2-incubator.jar 文件。

samples(目录)

samples 目录中包含了所有随 CXF 二进制包发布的示例,包含这些示例的源代码和相关 Web 应用配置文件,可以方便地用 Ant 来编译运行测试这些示例,来了解 CXF 的开发和

使用的方法。可以通过 samples 目录和它各个子目录下的 README.txt 的文件来详细了解示例的编译与运行的步骤。

6.1.3、使用cxf发布webservice

6.1.3.1、把cxf目录下lib下面的包全部加入到项目中

6.1.3.2、编写代码

1、创建接口
@WebService
@BindingType(value=javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_BINDING)
public interface SayHelloInteface {
  public String sayhellow(String name);
}      
2、创建实现类
@WebService
public class SayHellowIntefaceImpl implements SayHelloInteface {

  @Override
  public String sayhellow(String name) {
    return name+"hello world";
  }
}      
3、使用cxf类发布
public static void main(String[] args) {
  //运行
  JaxWsServerFactoryBean jwsf=new JaxWsServerFactoryBean();
  //CXF拦截器
  jwsf.getInInterceptors().add(new LoggingInInterceptor());
  jwsf.getOutInterceptors().add(new LoggingOutInterceptor());

  jwsf.setAddress("http://127.0.0.1:8080/sayHello");
  jwsf.setServiceClass(SayHelloInteface.class);
  jwsf.setServiceBean(new SayHellowIntefaceImpl());
  jwsf.create();
}      

6.1.4、使用cxf的wsdl2java命令生成客户端代码

wsdl2java –d . http://127.0.0.1:6666/helloworld?wsdl

6.1.5、调用

调用方式不变

SayHelloIntefaceService ss = new SayHelloIntefaceService();
SayHelloInteface shf=ss.getSayHelloIntefacePort();
String str=shf.sayhellow("lisi");
System.out.println(str);      

6.2使用Axis2创建WebService服务

​​http://www.blogjava.net/tianchijiaozi/archive/2013/03/15/396452.html#Post​​

7、在web项目中发布webservice

上面是介绍了如何通过Main方法启动WebService,但在实际项目中不可能是采用main方法来启动的,那么如何在项目启动的时候启动一个WebService呢?下面介绍

  1. 创建服务接口在接口上加@webservice
  2. 创建服务接口的实现类
  3. 在web.xml中配置CXFServlet

    ​<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <servlet> <servlet-name>cxf</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>cxf</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping> <session-config> <session-timeout>60</session-timeout> </session-config> </web-app> ​

  4. 配置cxf-servlet.xml

    ​<jaxws:server id="bye" address="/bye" serviceClass="com.rl.cxf.web.inter.ByeInter"> <jaxws:serviceBean> <bean class="com.rl.cxf.web.inter.ByeInterImpl"></bean> </jaxws:serviceBean> <jaxws:outInterceptors> <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"></bean> </jaxws:outInterceptors> <jaxws:inInterceptors> <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean> </jaxws:inInterceptors> </jaxws:server> ​

  5. 使用wsdl2java生成客户端代码
  6. 调用webservice

8.WebService的工作原理

再说WebService之前这里先说说RPC,什么是RPC呢?

RPC(Remote Procedure Call),远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。(来自百度百科)      

简单来说RPC是java中远程调用的一种技术或者约定,并不是一种具体的解决方案或者框架,而具体的框架有下面这些比较火的(排名不分先后)

  1. SpringCloud(Spring的,基于Socket的,SOA架构的分布式框架)
  2. Dubbo(x)(阿里巴巴的,基于Socket的,SOA架构的分布式框架)
  3. WebService(跨语言的,基于SOAP协议,走xml数据或json数据)
  4. Hessian(跨语言的,基于Binary-RPC协议,走二进制数据)
  5. HttpClient(通常用于RESTful风格的调用,跨语言,基于http和json)
  6. jdk原生(HttpURLConnection)

我们上面用的就是jdk原生的这种。本文也主要对这种方式的工作原理做简单解析,原理大概分为以下几个步骤

  1. Service端会在程序启动的时候扫描有标注@WebService的类,然后通过反射方法找到这个类的需要发布的方法,然后在用户访问wsdl文档的时候把这些东西组成SOAP协议内容然后返回给用户浏览器,用户就可以得到wsdl文档。同时服务端在启动的时候会启动一个socket用来等待客户端的连接。
  2. 客户端通过wsdl文档生成了代码然后在程序里面调用,实际上是通过了一个socket发送了一个请求给服务端,请求里面有指明客户端需要调用哪个类的哪个方法,以及请求参数,请求报文类似于
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">  
   <soap:Body>  
       <ns1:getWeather xmlns:ns1="http://fixed.adapter.solar2.erayt.com/">  
           <arg0>2013-06-22T18:56:43.808+08:00</arg0>  
       </ns1:getWeather>  
   </soap:Body>  
</soap:Envelope>      

Webservice的通讯协议是SOAP。

8.1 SOAP协议

SOAP=http+xml、下面看一个SOAP的报文示例

WebService入门详解

可以很清楚的看到SOAP就只是把http的报文体换成了XML。

  1. 上诉XML中描述了客户端需要调用哪个类的那个方法,服务端接收到客户端的请求之后,通过对应的信息以及参数反射调用具体的方法,最后把调用结果返回给客户端。

自此,整个WebService的调用执行完毕,客户端成功的通过远程调用的方式执行到了服务端的代码并拿到了返回值,这就是RPC