深入了解java WebService
前言:WebService就目前来讲已经是一种成熟的技术了,优缺点也相当明显,本文旨在入门webService,详解其使用场景,结合具体demo进行分析。
本篇文章重点关注以下问题:
- WebService的优缺点及使用场景?
- 了解WebService的基本概念,Soap、WSDL、XML、HTTP、UDDI指什么,在WebService中扮演什么样的角色?
- 服务端如何发布服务,客户端如何调用服务,具体步骤怎样?
1、WebService的特点及使用场景
WebService是很常见的远程调用方法,其最大的优势就是跨平台语言,无论客户端是Java还是.NET都能轻松的调用。它采用SOAP(Simple Object Access Protocol)协议来封装序列化的消息,实际上是形成一个xml文件,可以通过http进行网络传输。WebService的客户端调用其实是使用生成文件的方式,只要知道发布接口的URL即可,而不需要额外传递jar包或者class文件。
WebService 的主要目标是跨平台的可互操作性。
优点:
- 跨防火墙的通信:一般要访问的Web服务器以及要访问的Web Service的客户端很可能位于防火墙后面,都默认关闭其它端口而开放HTTP端口,而Web service 正是基于HTTP的,所以它可以穿越防火墙;
- 应用程序集成:可以实现不同应用程序和在不同系统平台上开发出来的应用程序之间通信。与RMI、DOCM、CORBA最大的不同就是:Web Service 以 SOAP 作为基本通信协议从而避免了复杂的协议转换;
- B2B 的集成:跨公司的商务交易集成通常叫做B2B 集成。通过WebService ,公司可以把关键的商务应用“ 暴露” 给指定的供应商和客户,只要把商务逻辑“ 暴露” 出来,成为WebService ,就可以让任何指定的合作伙伴调用这些商务逻辑,而不管他们的系统在什么平台上运行,使用什么 开发语言。这样就大大减少了花在B2B 集成上的时间和成本,让许多原本无法承受EDI 的中小企业也能实现B2B 集成。
缺点:
- 单机应用程序:单机应用程序只要与本机上的其他程序通讯,这种情况下使用WebService消耗太大,而且不会带来任何好处;
- 局域网的同构应用程序:简言之,还是性能问题。只要从应用程序结构的角度看,有别的方法比WebService更有效、更可行,那就不要用WebService。
2、WebService初了解
先给WebService下个定义:WebService是一种跨编程语言和跨操作系统平台的远程调用技术。定义中有两个关键点:
- 一是跨语言跨平台,说明它有广为接受的协议,是标准。类似于SQL,不同语言不同平台通过sql操作数据库,而不同语言不同平台通过WebService发布、使用服务。Sql搭载的是数据,WebService搭载的是服务。
- 二是远程调用,说明它涉及网络通信,整个调用过程涉及不同模块,不同角色。
下面先介绍实现一个完整WebService功能需要的组成成员与流程。首先上图:
这幅图描述了完整WebService服务的整个过程以及发布、调用WebService的整个流程,大致流程如下:
- 服务发布:服务提供者设计服务的接口(功能A)并予以实现,本地调试通过后想服务注册中心发布,向UDDI注册;
- 查询服务:服务请求者需要使用功能A时,可以向UDDI查询,UDDI则会返回能满足请求者的服务(WSDL);
- 分析服务:服务请求者收到UDDI反馈的WSDL服务描述信息,予以分析,生成客户端所需的模块准备发起WebService请求;
- 发起服务:分析WSDL服务描述,生成相应的SOAP消息,发送给服务提供者,以期待获得服务;
- 完成服务:服务提供者按SOAP消息执行指定的服务,并将服务结果反馈给请求者。
- 服务提供者:即服务的提供者,它一直等待接收客户端的请求并反馈服务响应。(发布服务)
- 服务请求者:即服务的使用者,它利用SOAP消息向服务提供者发送请求并获得服务。(使用服务)
- 服务中介者:即UDDI,充当管理员的角色,其主要职责就是将服务请求者和合适的服务提供者联系起来,当然这个角色并不是必须的,尤其是客户端知道服务端的情况下。(绑定服务)
- XML:(Extensible Markup Language)扩展型可标记语言。xml与语言无关、与平台无关,经常作为信息载体用于保存数据,是Soap的基础。
- SOAP:(Simple Object Access Protocol)简单对象存取协议。是XML Web Service 的通信协议。当客户端通过UDDI找到服务WSDL描述文档后,它可以通过SOAP调用你建立的Web服务中的一个或多个操作。SOAP是XML文档形式的调用方法的规范,它可以支持不同的底层接口,像HTTP(S)或者SMTP,可以这样描述:SOAP=在HTTP的基础上+XML数据。
- WSDL:(Web Services Description Language) WSDL 文件是一个 XML 文档,用于说明一组 SOAP 消息:服务在什么地方(地址),提供什么样的方法(如何调用)。大多数情况下由软件自动生成和使用。
- UDDI:(Universal Description, Discovery, and Integration) UDDI是一种根据描述文档来引导系统查找相应服务的机制。UDDI利用SOAP消息机制(标准的XML/HTTP)来发布,编辑,浏览以及查找注册信息。它采用XML格式来封装各种不同类型的数据,并且返回需要的数据。
3、WebService样例输出
3.1 利用JAX-WS发布发布、调用WebService服务
3.1.1 发布WebService服务
直接上代码:
@WebService // @WebService : 用在类上,将此类发布成一个WebService服务 public class HelloService { public String sayHello(String name) { System.err.println("hello " + name); return "hello, " + name; } /** * @WebMethod(exclude=true)注解用于方法上,阻止对完公开此方法 */ @WebMethod(exclude=true) public void notPublish() { System.out.println("@WebMethod(exclude=true)注解用于方法上,阻止对完公开此方法"); } /** * final方法不能对外发布 */ public final void finalMethod() { System.out.println("final方法不能对外开放"); } /** * static方法不能对外发布 */ public static void staticMethod() { System.out.println("静态方法不能对外开放"); } public static void main(String[] args) { // Endpoint : 此类为端点服务类,它的publish方法用于将一个已经添加了@WebService注解的对象绑定到一个地址的端口上发布。 // 注意:EndPoint发布服务之后,将会以独立的线程运行,所以publish之后的代码可以正常运行。 Endpoint.publish("http://localhost:9999/sayHello", new HelloService()); } }
注释中已解释JAX-WS基本用法,最后提一点:如果一个类上被添加了@WebService注解,则此类至少有一个公开发布的方法,否则启动失败。
EndPoint在指定地址的一个端口上发布服务后,在浏览器中输入http://localhost:9999/sayHello?wsdl,如若可以获得WebService的说明文件,即WSDL文件,则说明服务发布成功。有关WSDL的具体说明可以查阅相关资料,这里不做阐述。
3.1.2 使用WebService服务
客户端调用WebService的方式:
- 通过wsimport生成客户端代码;
- 通过客户端编程的方式调用;
- 通过ajax调用(js + XML);
- 通过URLConnection调用;
1、通过wsimport生成客户端代码
WSDL文档不易阅读,但是JDK自带wsimoport命令可以根据WSDL文档生成客户端调用服务代码,无论服务端的WebService是用什么语言写的,都将在客户端生成java代码。对于上述发布的服务,在控制开输入命令:
wsimport –s . http://localhost:9999/sayHello?wsdl
-s用于指定生成java文件,后面的点指明源代码生成后所在的目录,点为当前目录
拷贝java代码至客户端的工程中,可以对照WSDL文件学习如何调用,下面上代码:
/** * 利用wsImport方式生成客户端调用WebService服务 * @author Administrator */ public class ClientMain { public static void main(String[] args) { // HelloServiceService 对应于WSDL文档中绑定的的Service节点 HelloServiceService helloServiceService = new HelloServiceService(); // HelloService 对应于WSDL中的portType节点,代表着WebService发布的一个函数库或是类 HelloService helloService = helloServiceService.getHelloServicePort(); // 下面的方法即为服务类HelloService发布的一个服务或方法 String result = helloService.sayHello("熊燕子"); System.out.println(result); } }
2、通过客户端编程的方式调用
通过JAX-WS生成客户端代码,并因此为基础调用WebService服务,其缺点是导致依赖的文件太多,而通过客户端编程的方式调用WebService可避免无必要的文件。当然,客户端编程也可以利用JAX-WS生成必要的服务接口。(如上述生成的HelloService.java接口)
/** * 以客户端编程的方式调用 * @author Administrator */ public class ClientInvoke { public static void main(String[] args) throws MalformedURLException { // 声明WSDL地址 URL wsdlUrl = new URL("http://localhost:9999/sayHello?wsdl"); // 客户端创建Service对象 // 第一个参数:WSDL中types节点下定义的命名空间 // 第二个参数:WSDL中Service节点的name属性定义的服务名 String nameSpace = "http://webService.wj.com/"; String serviceName = "HelloServiceService"; Service s = Service.create(wsdlUrl, new QName(nameSpace, serviceName)); // 获得WebService服务的接口 // 第一个参数:WSDL中types节点下定义的命名空间 // 第二个参数:WSDL中portType节点的name属性定义的服务 HelloService hs = s.getPort(new QName("http://webService.wj.com/","HelloServicePort"), HelloService.class); // 调用发布的服务或者说是方法 String ret = hs.sayHello("熊燕子"); System.out.println(ret); } }
对上述客户端调用WebService的方式做几点说明:
- QName对象意为全限定名,通过WSDL(本质是XML文件)中的命名空间和节点即可定位所需的Service对象;
- 客户端编程需要一个与服务端接口完全相同的类(仍然可以使用工具生成,但是只需要一个接口),如果返回的是一个POJO类,也需要一并放入客户端;
- 上述过程本质上寻找WSDL的具体节点元素,如下图所示:
3、通过AJAX的方式调用
因不符合项目使用场景就没输出demo,见谅。
4、通过URLConnection调用
URLConnection调用WebService本质上其实就是和Ajax方式类似,都是手动完善HTTP请求并发送,所以比较麻烦,这边也简单做了个样例展示一下:
/** * 通过UrlConnection调用Webservice服务 */ public class App { public static void main(String[] args) throws Exception { // 以服务的地址生成URL URL wsUrl = new URL("http://localhost:9999/sayHello"); // 打开URL的链接 HttpURLConnection conn = (HttpURLConnection) wsUrl.openConnection(); conn.setDoInput(true); conn.setDoOutput(true); conn.setRequestMethod("POST"); // WebService的提交方式都是"POST" conn.setRequestProperty("Content-Type", "text/xml;charset=UTF-8");// MIME类型固定 OutputStream os = conn.getOutputStream(); // 自己构造SOAP的请求体 String soap = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:q0=\"http://webService.wj.com/\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" + "<soapenv:Body> <q0:sayHello><arg0>aaa</arg0> </q0:sayHello> </soapenv:Body> </soapenv:Envelope>"; // 发送消息 os.write(soap.getBytes()); InputStream is = conn.getInputStream(); byte[] b = new byte[1024]; int len = 0; String s = ""; while((len = is.read(b)) != -1){ String ss = new String(b,0,len,"UTF-8"); s += ss; } System.out.println(s); is.close(); os.close(); conn.disconnect(); } }
通过上述过程不难看出,这种方式比较麻烦,不仅需要手动构造SOAP消息体的内容,还需要自己解析收到的服务反馈。到这里又人可能会问,消息体需要自己一个一个字的敲?其实不然,eclipse有支持的,下面详细说明。
3.2 CXF发布、调用WebService服务
请转看同主题下对CXF的详细介绍介绍。
4、补充说明:Eclipse对WebService开发的支持
1、打开Web Service Explorer,输入WSDL地址,单击运行,如下图所示:
2、确认后左侧导航会显示已发布的WebService服务信息,右侧为详细服务信息,输入相应参数后调用WebService服务,如下图所示:
3、调用服务后,在结果输出界面你会发现服务端反馈的结果,并可点击"source"查看详细收发的报文,如下图所示:
4、查看收发的报文
我在URLConnection方式中填充发送消息的SOAP内容就是直接拷贝过来的,是不是很方便。用这种方式也可以测试WebService服务是否发布成功。
代码地址:http://pan.baidu.com/s/1dFDz7rF,密码:mbyx