背景介绍:在多个系统之间交互,WebService使用的频率比较高,现在就用一个简单的示例来看看WebService在不同系统开发时是如何使用的,以及WSDL的使用。
WSDL:系统交互,首先必须知道其他系统的WSDL,通过WSDL可以获取到很多东西。那么WSDL是如何生成的呢?生成WSDL也就牵扯到了如何发布一个WebService服务了。那么下面我们就一步一步的来。
首先,我们创建一个Web工程,取名WebServiceServer,这里我就创建一个简单的Web工程,并没有选择WebService的Web工程。(本示例因为Ecilpse版本缘故,创建的是动态Web工程,这里不做要求) 。
在工程的src目录下加入代码,一个带返回值的,一个不带返回值的,既然程序员天生喜欢用HelloWorld,那我们也给世界打个招呼吧!
package com.webservice.domain;
public class HelloWorld {
/** * 本工程为了方便,就直接使用sysout打印信息,具体的业务逻辑需具情况实现 * 其实这里最好抽出接口出来,然后在用该类去实现接口 * 这里为了方便,就不使用接口了 */
public void sayHelloWorld() { System.out.println("----Hello World----"); }
public String sayHelloWorld(String somebody) { if (null != somebody) { String temp = somebody + "said Hello World!"; System.out.println(temp); return temp; }
return null; } } |
向工程的lib目录下导入如下jar包,并Build到工程中去,jar包版本不一定有要求(activation.jar貌似是重复的哈,不管啦)
在web.xml配置发布WebService是需要用到的Servlet,代码如下:
|
在Tomcat的webapps目录下新建文件夹WebServiceServer, 将WebServiceServer工程下的WEB-INF目录拷贝到WebServiceServer下,并将WebServiceServer工程编译后的classes目录整个都拷贝到WebServiceServer的WEB-INF下。(其实这个步骤就是手动部署一个tomcat应用),当然lib下面有我们刚才上面提到的所有的jar包,classes下面有我们工程编译后的class文件结构目录如下:
OK,基本工作差不多了,下面我们就启动我们的tomcat,看看我们发布是否成功,如果不出意外的话,应该是不会失败的啦,我们会在IE上看到如下界面:
好,下面我们继续,下面的内容可能麻烦一些,tomcat可以先不停,IE也用不着关,如果你嫌麻烦呢,可以XX。为了方便呢,我们在我们WebServiceServer工程的WEB-INF下新建一个deploy.wsdd文件,文件格式是xml格式的。内容如下(节点应该比较好理解,至于比较复杂的配置这个就需要另外查资料了,另外deploy.wsdd可以取另外的名字的):
xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
|
同样,在我们WebServiceServer工程的WEB-INF下新建批处理文件service.bat,内容如下:
java -cp lib/activation-1.1.jar;lib/activation.jar;lib/axis-1.4.jar;lib/commons-discovery-0.2.jar;lib/commons-logging.jar;lib/javamail-1.3.3_01.jar;lib/jaxb-api.jar;lib/jaxb-impl.jar;lib/jaxrpc-1.1.jar/;lib/jaxrpc-api.jar;lib/jaxrpc-impl.jar;lib/jaxws-api-2.0.jar;lib/jstl-1.1.2.jar;lib/log4j.jar;lib/mail.jar;lib/saaj-api-1.3.jar;lib/saaj-impl-1.3.jar;lib/standard.jar;lib/wsdl4j.jar;lib/XmlSchema-1.2.jar org.apache.axis.client.AdminClient -lhttp://localhost:8080/WebServiceServer/services/AdminService deploy.wsdd |
其中,-l后的参数是本地要发布WebService的AdminServlet对应的访问地址
注意:批处理文件里面的内容不可以随便换行,jar包必须跟lib目录下的jar包名字一样,deploy.wsdd必须是我们刚才新建的deploy.wsdd
OK,以上步骤如果完成,我们将WebServiceServer工程的WEB-INF下的deploy.wsdd和service.bat拷贝到Tomcat的webapps目录下的WebServiceServer的WEB-INF下,那么此时,WebServiceServer下的结构就如下了:
好,我们刚才tomcat不是没关嘛,如果你关了,嘿嘿,请重新启动你的猫子。启动了之后呢,我们就执行Tomcat的webapps目录下的WebServiceServer的WEB-INF下的service.bat,如果该bat文件执行不报错呢,那么在刷新浏览器的话就会出现如下改变:
看到没,唉,终于看到希望了,弄了这么长时间,终于给憋出来啦,高兴啊(针对我这样的菜鸟级人物来说的哈,大虾就不要拿你那大钳挥舞摇摆啦,哥怕你那“钢牙”),好,我们点击一下ServiceDemo后面的wsdl连接,就可以看到我们期待已久的WSDL啦,个瘪瘪的。嘿嘿,还有一点让你不敢相信的是,WSDL弄出来了,我们的WebService也就发布成功啦,你要是不信,弄个SoapUI工具,把浏览器上的http://localhost:8080/WebServiceServer/services/ServiceDemo?wsdl这个WSDL用SoapUI打开看看就明白啦。
将这个WSDL以文件的形式保存下来,就可以丢给别人用啦。
---------------------------------------------------
以下与上面无关,现在可以将你本地的tomcat关掉,然后将webapps下的WebServiceServer这个给干掉啦,以免影响下面的讲解
-------------------------------------------------------
好了,任务搞掂一个----------------------------下面继续,如果我们拿到别人给的WSDL文件,怎么用呢,别人在别的地方开发,我们这儿又没外网,怎么连上别人的机器调试我们的代码呢,很痛苦,是不是。嘿嘿,有别人的WSDL就不用皱眉头了撒。
拿到WSDL后我们一般会做两件事情,一是生成客户端,这个客户端是用WSDL生成的,我们可以使用里面提供的接口,另外一个是服务器端,这个服务器端很简单哈,一般是用作桩的,是一个假东西,假到什么程度呢,你写的代码我不认,我只要你提供的接口就行啦。OK,一步步来吧。
将我们的这个WSDL http://localhost:8080/WebServiceServer/services/ServiceDemo?wsdl保存为文件的形式,取名helloworld.wsdl。留着,用处大着呢。
为了避免新手误会,我们将我们上面贴出来的jar包copy一份出来,随便copy到一个什么目录,比如我们给这个目录取名resources。将我们的helloworld.wsdl也拷贝到这个目录下,另外在这个目录下新建create.bat文件(文件名随便取啦),该bat文件内容如下:
java -cp activation.jar;commons-logging.jar;mail.jar,log4j.jar;wsdl4j.jar;axis-1.4.jar;jaxws-api-2.0.jar;jaxb-api.jar;jaxb-impl.jar;XmlSchema-1.2.jar;commons-discovery-0.2.jar;jaxrpc-api.jar;jaxrpc-impl.jar;jaxrpc-api.jar;jaxrpc-impl.jar;saaj-api-1.3.jar;saaj-impl-1.3.jar;javamail-1.3.3_01.jar org.apache.axis.wsdl.WSDL2Java -s helloworld.wsdl |
注意:
1. bat文件内容不可以随便换行
2. 因为我这个是做例子,所以jar包取的是上面我们创建WebServiceServer时候的jar包,这个bat文件包含的jar包不一定要求跟上面我们创建WebServiceServer时候的jar包版本一致,在不同场合开发,不一定能找到相同版本的jar包,而且我这个例子的版本也不一定非常稳定。一句话:有这么些个名称的jar包就行了,但是bat文件内的jar包名必须跟resources里面的jar包对上号。
好了之后,我们就执行create.bat,执行之后,会生成一个localhost的目录,该目录下面包含了一些代码:
咦,看到没,久违的wsdd也出来了。好叻,有了wsdd和java文件,我们不是可以用我们最开始讲的模拟一个WebService服务出来嘛。
好,按照文档最开始讲的(这里讲的跟文档最开始讲的有些相同,但并没有任何关系哈,别弄混淆啦,就当WebServiceServer这个工程不存在了,我们现在只是在使用刚才的步骤),一步步来,新建WEB工程(这个WEB工程就是我们以后要用到的模拟桩了,就叫Test吧),把resources目录下面的包都Build到工程下,然后把localhost的目录下面的所有东西都copy到src下,当然这个时候会出错,因为我们代码放的包不对,代码的package后面有个localhost,而且代码里面也有些地方也有localhost,这个localhost其实是对应到你发布WSDL的IP地址的,我们刚才发布的时候用的是localhost对吧,没办法,我们只能采用笨办法,把这些localhost给干掉啦。
去掉这些让人烦心的错误之后,我们就需要写实现了,当然这个实现是模拟的。我们可以看到,HelloWorld.java这个里面提供了2个接口,刚好是我们WebServiceServer提供的两个接口,而HelloWorldService.java则有三个,所以,我们需要实现的是HelloWorld.java这个接口,在这个类名上面按住Ctrl和T 吧,出来了两个类,一个是ServiceDemoSoapBindingImpl.java,一个是ServiceDemoSoapBindingStub.java,该实现哪个呢,分别打开看一看就知道了,当然是实现 ServiceDemoSoapBindingImpl.java这个撒。
package WebServiceServer.services.ServiceDemo;
public class ServiceDemoSoapBindingImpl implements WebServiceServer.services.ServiceDemo.HelloWorld{ public void sayHelloWorld() throws java.rmi.RemoteException { System.out.println("I said hello world"); }
public java.lang.String sayHelloWorld(java.lang.String somebody) throws java.rmi.RemoteException { System.out.println(somebody + "said hello world"); System.out.println("shut up!!"); return "OK"; }
} |
再配置web.xml,然后启动tomcat,打开浏览器看是否第一步发布成功,这次就不需要写什么deploy.wsdd啦,刚才都已经给我们生成好了,就不用在忙乎了,这一步跳过,然后就是执行service.bat,看看服务端是否发布成功啦。这个时候,为了节约时间,我Test工程就没做,所以WSDL还是使用上面那个WSDL吧http://localhost:8080/WebServiceServer/services/ServiceDemo?wsdl。大家要按照具体情况具体去把Test工程的WSDL生成出来啊,我这里是因为时间关系,所以就直接使用最刚才生成好的WSDL。
那么现在呢,我们就做一下客户端,我们就新建一个Java工程,取名ClientDemo
我们将resources文件夹下面的localhost干掉,然后修改create.bat,去掉里面的命令-s,然后执行,又会生成一部分代码
再将localhost下面的文件夹及文件都拷贝到ClientDemo工程的src下,并且将我们刚才用到的resources目录下的lib也全部都拷贝到该工程的lib目录下,并且Build到该工程中。我们贴进去的代码肯定是报错了的,我们就不要嫌麻烦,把出错的地方的localhost都去掉吧,当然可能是我生成的WSDL水平太臭啦导致的,所以啊,为了摆脱这样的痛苦,你们加紧儿学吧,再在网上爬爬。
修改完成之后呢,我们就可以使用Export命令(ClientDemo工程上点右键就可以看到啦,当然如果你会直接用命令打那更好咯,我这里介绍的是非常非常笨的方法,只适用于菜鸟,不是菜鸟就不用学这个啦)
后面的就Next和Finish吧。
好,拿到这个jar包,我们就好办了,比如我们现在做的工程中有业务需要调用刚才WebServiceServer那个系统的接口,那么我们就可以用刚才生成的webserviceserver.jar,直接调用里面的接口就OK了。
好,模拟开始 /|/_________________________________________/|/
新建工程,就当作是另外一个系统吧,比如AgentDemo,其实也就是我们的业务工程(我们的业务需要调用别的系统的接口,因为不同的系统在不同地点开发,因此我们只能调用模拟桩了),上面说的模拟桩工程(就是Test工程),当然这个工程不仅仅要导入webserviceserver.jar,还必须导入resources目录下的所有依赖包,导入之后我们Build一下。
我们编写代码来调用我们别的系统提供给我们的接口吧
package com.webservice.agent;
import java.net.MalformedURLException; import java.net.URL; import java.rmi.RemoteException;
import javax.xml.namespace.QName; import javax.xml.rpc.ParameterMode; import javax.xml.rpc.ServiceException; import javax.xml.rpc.encoding.XMLType;
import org.apache.axis.AxisFault; import org.apache.axis.Constants; import org.apache.axis.client.Call; import org.apache.axis.client.Service;
public class CallHelloWorld {
public static Call getCall() { Service service = new Service(); Call call = null;
try { call = (Call)service.createCall(); call.setTargetEndpointAddress(new URL("http://localhost:8080/WebServiceServer/services/ServiceDemo?wsdl")); } catch (ServiceException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); }
return call; }
/* * 调用不带参的sayHelloWorld()方法 * 因为sayHelloWorld()这个方法在定义时既无返回值也无参数,所以看打印信息必须看tomcat的后台信息了 */ public static void callHelloWorld() { Call call = CallHelloWorld.getCall(); call.setOperationName(new QName("http://domain.webservice.com", "sayHelloWorld")); try { call.invoke(new Object[]{}); } catch (AxisFault e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } }
public static void callHelloWorld2() { Call call = CallHelloWorld.getCall(); call.setOperationName(new QName("http://domain.webservice.com", "sayHelloWorld")); call.addParameter("somebody", XMLType.SOAP_STRING, ParameterMode.IN); call.setReturnType(Constants.XSD_STRING); try { String value = (String)call.invoke(new Object[]{"zhangsan"}); System.out.println(value); } catch (AxisFault e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } }
public static void main(String[] args) { CallHelloWorld.callHelloWorld(); CallHelloWorld.callHelloWorld2(); } } |
运行该代码时要保证tomcat是启动的,运行结果会在控制台打印如下信息
而且会在tomcat的后台打印如下信息:
注:当然我这段代码用的是我们文档开头生成的WSDL地址,所以打印的内容是上面显示那样子的,按照正常流程,其实我们应该是将代码中的WSDL地址修改为Test工程(模拟桩工程) 的WSDL,这样才显得我们不用连别人的系统,连我们做的模拟装就可以跑我们的业务了。
值得说明的是http://domain.webservice.com这个东东在WSDL中可以看到的,其实说白了就是我们在编写wsdd文件时指向的实现类的包路径的倒序。
OK,就讲到这儿吧
另一种客户端使用方法不用使用Call对象,然后去反射调用申明的接口,可以直接将带Locator的类实例化出来,类似代码如下:
/** * 获取UAService_PortType实例 * @return UAService_PortType对象 * @see [类、类#方法、类#成员] */ public synchronized UAService_PortType getUAServicePortTypeInstance() { if (null == uaServicePortType) { String wsdlUrl = “wsdl地址”; UAService_ServiceLocator locator = new UAService_ServiceLocator(); try { uaServicePortType = locator.getUAServicePort(new URL(wsdlUrl)); } catch (MalformedURLException e) { logger.excepMethodDebugLog(e); } catch (ServiceException e) { logger.excepMethodDebugLog(e); } }
return uaServicePortType; } |
这样就可以使用UAService_PortType类的去调用里面申明的接口了。当然要针对具体情况看locator返回的是什么对象啦,这里仅仅只是给出了一个例子。
第三种方法
OmpFacadeLocator locator = new OmpFacadeLocator();
OmpFacadeSoapBindingStub stub = new OmpFacadeSoapBindingStub();
URL url = new URL(pro.getProperty("omp_wsdl_path"));
stub = (OmpFacadeSoapBindingStub)locator.getompFacadePort(url);
使用Locator对象获取stub实例,然后再用stub调用接口