一天的时间调通了一个 WebService 的 JAVA 端的 C/S、一个 ANDROID 端的 C/S,调通了而已,很不爽,很闷。因为刚刚上手 JAVA & Eclipse,对于我这个用惯了 VS 2010 的同学来说,感觉大大的不好。被迫和陌生的感觉很容易让我这个巨蟹座的男人直接地由然而生强烈的抵触情绪。不过话说回来了,网络方面的东西我一直很感兴趣,苦于没有项目参与。谁让项目要求我 AXIS2 + Ecliipse + Tomcat + Android 呢?看在网络项目开发的面子上,忍了!
于是用两天的时间好好地看了看 20 多年前的故事,抽空儿看了看 SOA & SOAP & WebService 的东西,看了看 WSDL 和 SOAP 标准。
简单地说 WSDL 这东西,一种网络服务描述规范,它是一个实现 WebService 的组件,也只是提供一种描述的框架,如果想用 SOAP,就往里面添 SOAP 的元素节点,如果想用 MIME 的东西,就往里面添 MIME 的元素节点,想用 HTTP-GET/POST 的东西,就往里面添相应的元素节点。
搞清楚整个 WSDL 结构,了解构成 WSDL 内容的主要元素标签就可以了,还挺有层次感的:
SOA 这个东西很好。不过使用 AXIS2 + Eclipse + Tomcat 配起一个 WebService 架构,很烦!SOA 和 WebService 不是一个东西,SOA 是一个概念和模式、一种业务,WebService 是一种实现 SOA 理念的框架,一个工具。
开个玩笑说,话说 SOAP 这个词儿我刚看到的时候,还以为是所谓的 SOA + P……
SOAP 应是一种应用协议,初衷是针对 RPC 设计的;但和 HTTP 最初是奔着应用协议而设计的命运越来越像,它越来越有“堕落”到“传输协议”的倾向。不过无论 SOAP 还是 HTTP,都还是用于消息传输的应用协议。
有人说 SOAP = RPC + XML+ HTTP,基本上吧,不准的。还是试图点出 SOAP 和 HTTP 的关系了的。SOAP 消息的请求和响应动作,是需要映射为 HTTP 的请求和响应来完成 C/S 之间的通信的。怎么映射,不管了,反正就是这么回事儿。对于 WebService 来说,除了 SOAP 还有 HTTP-GET/POST 和 MIME 两种消息通信方式。但对于更好地实现 SOA 理念的要求来说,我们只关注 SOAP 就可以了。
SOAP 传的是什么,是 XML 数据元素,嗯……是序列化的 XML 数据元素。SOA 所谓的与终端平台无关嘛,那就是,无论什么业务,大家一起来遵循同一规范就是了—— SOAP + XML(应用协议和数据格式),这是典型的天下大一统的历史演变趋势,所以,我们泱泱华夏七千年文明历史,老祖宗早早就觉悟了,早早地推动天下大一统的治世理念了;洋人文明史才一千多年,觉悟得晚,刚刚才知道,是时候去推动天下大一统了,要不,这天下也太他妈的乱了。
对于开发来说,程序员需要搞清楚的应该是代码逻辑,尤其是这种遵循业务规范做开发编程的,与其说要搞清楚代码逻辑,不如说,必须得先搞清楚业务逻辑是什么,规范是什么,然后它们是如何反映到代码结构中的才是。
若是用 AXIS2 的 WSDL2JAVA 生出来的 STUB,那你开发客户端可就省大事儿了,不过自己去用代码建起整个请求响应的操作过程,包括指定端点,指定 URI,指定方法签名,指定参数,指定传输方式,指定请求目标,指定 SOAP 消息的哪个部分该填充哪个对像等等……你只需要指明要调用哪个服务方法,传什么参,怎么操作返回值就好了,关注点很集中,因为整个 SOAP 是怎么跑的你根本不用关心,反正肯定能成功就对了。
若你不想用或者不会用或者害怕用 WSDL2JAVA 工具生出来的代码,想自己写客户端,或者类似于做 ANDROID 上面的东西,要走 KSOAP2,客户端就得自己去写。这个时候就只能给自己个“勤奋好学求知”的理由去好好搞清楚整个 SOA & SOAP & WebService 是怎么个回事儿,回头来再看代码了。
不过使用 AXIS2 的包儿去写客户端和使用 KSOAP2 的包儿去写客户端还是有很大不同的。AXIS2 在背后还是替你做了很多工作,在这点上你一点儿选择都没有,使得基于 AXIS2 的包儿开发的客户端的代码逻辑很不怎么 SOAP,所以先看基于 KSOAP2 的客户端代码逻辑吧。
看之前,先想想,要想完整地在客户端处理一个 SOAP 请求/响应过程,需要准备什么?
我们回头看看 SOAP 的结构,一个信封,一个可有可无的头,一个体。信封指明谁来处理消息,也就是把消息寄给谁。使用 HTTP 通信的话,则须用 URI 填充 SOAP Action HTTP Header 域,这个域指明了这封信是要交给谁的。SOAP 体用来装 RPC 对像,而 RPC 包含需要调用的函数的一切信息,至少函数名、参数是必须指定的。使用 HTTP 传输 SOAP,则须指定服务端的 endpoint,用 URL 指定。
看,这些信息是明确一个完整 SOAP 过程的最小集了。它明确了,这个 SOAP 消息要怎么寄(HTTP),要寄到哪儿(endpoint/URL),寄给谁(URI/SOAP Action Header),寄的是什么(SOAP Body/RPC),而要求都写在 RPC 里面了。
好了,现在回头看代码:
// RPC 对像,服务方法所在命名空间和方法名
SoapObject rpc = new SoapObject(NAMESPACE, METHOD_NAME);
// 方法的参数名和参数值
rpc.addProperty(PARA_NAME, PARA_VALUE);
// 使用 HTTP 传输,指定 URL,就是要把信寄到哪儿,URL 指定到 ?wsdl
HttpTransportSE ht = new HttpTransportSE(URL);
// 要写信了,指明是 soap 1.1 标准
SoapSerializationEnvelope envelope =
new SoapSerializationEnvelope(SoapEnvelope.VER11);
// 将 RPC 塞信里面
envelope.bodyOut = rpc;
// 这步很重要,告诉把信寄给谁,填充 SOAP Action Header 域
ht.call(SOAP_ACTION, envelope);
// 处理回馈
if (envelope.getResponse() != null) {
// 返回对像是一个包含基础类型的封装体
SoapPrimitive re = (SoapPrimitive) envelope.getResponse();
System.out.println(re.toString());
}
这段使用 KSOAP2 包开发的客户端代码逻辑,就已经很明白了吧!
使用 AXIS2 包开发的客刻端代码逻辑很不好看,它只需要照顾到几个围绕要调用的服务方法的方面:函数所在的服务端点、命名空间和函数签名、参数:
// 使用 RPC 方式调用WebService
RPCServiceClient rpcClient = new RPCServiceClient();
Options soption = rpcClient.getOptions();
// 函数所在的服务端点在哪儿,不用指定到 WSDL
EndpointReference enpointref =
new EndpointReference("http://192.168.1.100:8080/axis2/services/hikaliv");
// 将端点引用与 RPC 对像关联
soption.setTo(enpointref);
// 参数
Object[] inargs = new Object[]{"hikaliv"};
// 返回参数类型
Class[] returnclasses = new Class[]{String.class};
// 指定要调用的方法及命名空间
QName qname = new QName(NAMESPACE, METHOD_NAME);
// 采用同步阻塞调用,等待结果反馈
Object[] result=rpcClient.invokeBlocking(qname, inargs, returnclasses);
for(Object obj : result){
System.out.println(obj);
}
就聊到这儿。