這些天稍微玩了一下Axis,以前做WebServices都使用JBuilder,感覺(jué)做WebServices如此簡(jiǎn)單,現(xiàn)在自己手動(dòng)做,原來(lái)也是如此簡(jiǎn)單。高興之余寫一個(gè)簡(jiǎn)單的初學(xué)手冊(cè),就算是學(xué)習(xí)成果吧。當(dāng)然對(duì)Axis理解的還不很深,所以錯(cuò)誤之處望指點(diǎn)。
Axis是一個(gè)實(shí)現(xiàn)WebService的Framework,Apache Web Services Project(http://ws.apache.org )的一個(gè)之項(xiàng)目,現(xiàn)在這個(gè)項(xiàng)目有很多之項(xiàng)目 Axis(http://ws.apache.org/axis/ )是其中一員,還有XML-RPC(這個(gè)也是我比較喜歡的東東J)。
現(xiàn)在Axis主要由兩個(gè)版本一個(gè)是Axis一個(gè)是Axis2。兩個(gè)好象有比較多的不同,我這里說(shuō)的是Axis,過(guò)幾天演技一下Axis2,然后再寫一篇吧。
好了現(xiàn)在開始做個(gè)WebService 吧:
第一步當(dāng)然是先去Axis主頁(yè)下載一個(gè)來(lái)啦。下Release就行,最新的是1.2.1,source好象沒(méi)有打包的只有CVS的。下來(lái)以后解壓縮,主要有以下文件夾
Docs 顧名思義,這里放的是文檔,其實(shí)Axis的文檔作的很好,我就是按照它的User Guide一步步做下來(lái)的。
Lib 運(yùn)行Axis時(shí)要用到的jar包,要完全正常運(yùn)行還缺兩個(gè)mail.jar activation.jar 這兩個(gè)是javaMail包,到處都能弄到。
Samples Axis自帶的例子包括很多種應(yīng)用
Webapps Axis是發(fā)布到Servlet Container中的,要把Axis集成到你的項(xiàng)目中,就把這個(gè)文件夾里的內(nèi)容合并到你的項(xiàng)目中就行了。
還有一個(gè)xmls文件夾,放得是一些可能用到的xml例子。
第二步,建一個(gè)項(xiàng)目,Web項(xiàng)目,用Eclipse或者Idea都可以啊。如果你非要用記事本類的東西,我也不攔著你。
把Axis中的Webapps\axis文件夾下的東西統(tǒng)統(tǒng)Copy到你的Web文件夾下。其實(shí)有些東西是沒(méi)用的,比如classes文件夾里的東西都可以去掉了,還有那幾個(gè)jws文件也沒(méi)有用。雖然axis最方便的發(fā)布WebServices的方法是把你的.java改成.jws的放到Web發(fā)布文件夾下的根目錄下,但是這種方法沒(méi)有什么適用價(jià)值。然后運(yùn)行以下Tomcat(或者其他的Application Server)。然后瀏覽一下你的剛剛發(fā)布的這個(gè)項(xiàng)目,如果正常的話就可以看到Axis的默認(rèn)畫面,

這個(gè)頁(yè)面不是必須的,在真正項(xiàng)目開發(fā)中可以把它去掉或換個(gè)名字。點(diǎn)擊List連接進(jìn)入已經(jīng)發(fā)布的WebServices列表。

開始時(shí)應(yīng)該只有AdminService和Version。后面兩個(gè)就是我們?cè)谙旅嬉龅?/SPAN>WebServices。
第三步,如果上面的一切正常,就可以正式開始做WebServices了。首先做一個(gè)Services實(shí)現(xiàn)類。Calc.java有兩個(gè)方法plus和subtract。這個(gè)Service所用到的數(shù)據(jù)類型都是基本類型。
public class Calc {
public int plus(int a,int b){
return a+b;
}
public int substract(int a,int b){
return a-b;
}
}
然后在WEB-INF目錄下加入一個(gè)server-config.wsdd。這是WebServices的發(fā)布描述文件,作用類似于web.xml。它有自己的格式,但是具體的標(biāo)記是什么樣子的,在Axis的文檔中沒(méi)有詳細(xì)的一一列出,只是提到了常用的一些。在axis的源碼中有一些wsdd的XSD文件,如果你用的是IDEA可以把這些XSD映射到uri,這樣編輯器就有提示了。
下面這我們本文中的server-config.wsdd的樣子:
<?xml version="1.0" encoding="UTF-8"?>
<deployment name="defaultClientConfig"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"
xmlns:handler="http://xml.apache.org/axis/wsdd/providers/handler" xmlns="http://xml.apache.org/axis/wsdd/">
<globalConfiguration name="defaultClientConfig">
<requestFlow name="RequestFlow1">
<handler name="Handler1" type="java:org.apache.axis.handlers.JWSHandler">
<parameter name="scope" value="session"/>
</handler>
<handler name="Handler2" type="java:org.apache.axis.handlers.JWSHandler">
<parameter name="scope" value="request"/>
<parameter name="extension" value=".jwr"/>
</handler>
</requestFlow>
</globalConfiguration>
<handler name="URLMapper" type="java:org.apache.axis.handlers.http.URLMapper"/>
<handler name="LocalResponder" type="java:org.apache.axis.transport.local.LocalResponder"/>
<handler name="Authenticate" type="java:org.apache.axis.handlers.SimpleAuthenticationHandler"/>
<transport name="http">
<requestFlow name="RequestFlow1">
<handler name="Handler1" type="URLMapper"/>
<handler name="Handler2" type="java:org.apache.axis.handlers.http.HTTPAuthHandler"/>
</requestFlow>
</transport>
<transport name="local">
<responseFlow name="ResponseFlow1">
<handler name="Handler1" type="LocalResponder"/>
</responseFlow>
</transport>
<service name="AdminService" provider="java:MSG">
<parameter name="allowedMethods" value="AdminService"/>
<parameter name="enableRemoteAdmin" value="false"/>
<parameter name="className" value="org.apache.axis.utils.Admin"/>
<namespace>http://xml.apache.org/axis/wsdd/</namespace>
</service>
<service name="Version" provider="java:RPC">
<parameter name="allowedMethods" value="getVersion"/>
<parameter name="className" value="org.apache.axis.Version"/>
</service>
<service name="CalcService" provider="java:RPC">
<parameter name="allowedMethods" value="*"/>
<parameter name="className" value="org.mstar.ws.Calc"/>
<parameter name="scope" value="request"/>
</service>
<service name="FooService" provider="java:RPC">
<parameter name="allowedMethods" value="*"/>
<parameter name="className" value="org.mstar.ws.FooService"/>
<parameter name="scope" value="session"/>
<typeMapping encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns1="http://ws.mstar.org"
qname="ns1:FooBean"
languageSpecificType="java:org.mstar.ws.FooBean"
serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"
deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"
name="FooBean"/>
<requestFlow name="requestFlow1">
<handler name="Handler1" type="java:org.mstar.ws.FooHandler"/>
</requestFlow>
<responseFlow>
<handler name="Handler1" type="java:org.mstar.ws.FooHandler"/>
</responseFlow>
</service>
</deployment>
這個(gè)文件比Axis自帶的那些deploy.wsdd要多很多東西,在Axis的文檔中它使用命令來(lái)把對(duì)deploy.wsdd進(jìn)行發(fā)布的。在我的例子中是直接把server-config.wsdd寫好放到WEB-INF下。所以要把Service上面那些東西加上,否則系統(tǒng)不能正常運(yùn)行。
<service name="CalcService" provider="java:RPC">
<parameter name="allowedMethods" value="*"/>
<parameter name="className" value="org.mstar.ws.Calc"/>
<parameter name="scope" value="request"/>
</service>
是Calc的發(fā)布描述。其中scope屬性默認(rèn)是request所以不寫也可以。其他parameter看名字就知道干什么的了。這樣你在List頁(yè)面中就可以查看CalcService的WSDL了。
第四步就是寫客戶端程序了。
WSClient.java
try {
String endpoint = "http://localhost:8080/ws/services/CalcService";
Service service = new Service();
Call call = service.createCall();
call.setTargetEndpointAddress(endpoint);
call.setOperationName(new QName("http://ws.mstar.org", "plus"));
Object[] params = new Object[2];
params[0] = 10;
params[1] = 20;
Integer result = (Integer) call.invoke(params);
System.out.println("Result: " + result);
} catch (Exception e) {
e.printStackTrace();
}
這是動(dòng)態(tài)的調(diào)用WebService的方法,并不需要根據(jù)WSDL生成客戶端存根。但是對(duì)于Service中有復(fù)合類型的時(shí)候就不可以了。下一個(gè)例子講的就是如何做客戶端存根。如果這個(gè)例子能夠正常運(yùn)行就可以了。
第五步做一個(gè)稍微復(fù)雜一點(diǎn)的例子,對(duì)于WebServices不能僅僅對(duì)簡(jiǎn)單類型的數(shù)據(jù)進(jìn)行操作,也應(yīng)該能對(duì)復(fù)雜類型進(jìn)行操作。下面的例子就是:
先要一個(gè)復(fù)雜類型的類
public class FooBean {
private String foo1;
private Integer foo2;
private Boolean foo3;
public FooBean(String foo1, Integer foo2, Boolean foo3) {
this.foo1 = foo1;
this.foo2 = foo2;
this.foo3 = foo3;
}
…. Setter and Getter
}
然后是一個(gè)有FooBean的Service
public class FooService {
public FooBean getFooBean(String foo1,Integer foo2,Boolean foo3){
return new FooBean(foo1+"(Remote)",foo2+10,!foo3);
}
}
很簡(jiǎn)單,就是返回一個(gè)FooBean,在參數(shù)上做了一些手腳J。
然后在server-config.wsdd中加入描述
<service name="FooService" provider="java:RPC">
<parameter name="allowedMethods" value="*"/>
<parameter name="className" value="org.mstar.ws.FooService"/>
<parameter name="scope" value="session"/>
<typeMapping encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns1="http://ws.mstar.org"
qname="ns1:FooBean"
languageSpecificType="java:org.mstar.ws.FooBean"
serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"
deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"
name="FooBean"/>
</service>
這里多了typeMapping標(biāo)記用來(lái)描述復(fù)雜數(shù)據(jù)類型FooBean。對(duì)入復(fù)雜類型的序列化可以是自定義的,在serializer deserializer屬性中改。那個(gè)xmlns,要留意一下,因?yàn)樵诳蛻舳松纱娓鶗r(shí)的AntTask中要寫一些Mapping,來(lái)確定那些xmlns映射到哪些package。當(dāng)然這些也可以在WSDL中找到。而WSDL中的xmlns也是在這里定義的。
下面也各客戶端。由于這次的Service中有復(fù)雜類型所以要根據(jù)WSDL生成這些復(fù)雜類型和Service的存根。取得WSDL的方法可以使用java2WSDL來(lái)做,但那樣太麻煩,其實(shí)在List頁(yè)面中用右鍵-另存為就可以了。然后寫一個(gè)ant文件
<?xml version="1.0" encoding="UTF-8"?>
<project default="GenJavaSub" basedir=".">
<taskdef name="wsdl2java" classname="org.apache.axis.tools.ant.wsdl.Wsdl2javaAntTask"/>
<target name="GenJavaSub">
<property name="output.dir" value="src"/>
<property name="generated.dir" value="src/org/mstar/wsclient/generated"/>
<property name="wsdl.url" value="wsdl/FooService.wsdl"/>
<delete dir="${generated.dir}"/>
<mkdir dir="${generated.dir}"/>
<wsdl2java all="true" debug="false" helperGen="true"
noimports="false"
output="${output.dir}"
serverside="false"
skeletonDeploy="false"
typeMappingVersion="1.1"
url="${wsdl.url}"
verbose="false"
noWrapped="false">
<mapping namespace="http://ws.mstar.org" package="org.mstar.wsclient.generated"/>
<mapping namespace="http://localhost:8080/ws/services/FooService" package="org.mstar.wsclient.generated"/>
</wsdl2java>
</target>
</project>
這里上面的東西比較好理解,在下面的mapping中一定要注意,因?yàn)槊總€(gè)Service和ComplexType都有自己的xmlns,這里就是把xmlns映射到指定的package 比如 把http://ws.mstar.org 映射到org.mstar.wsclient.generated包。這些namespace可以在wsdl文件中找到。運(yùn)行ant就會(huì)看見(jiàn)在src中的org.mstar.wsclient.generated中多了幾個(gè)java文件。接下在我們就可以用這幾個(gè)類了。還有一點(diǎn)要注意wsdl2java 的output屬性要指向src,而不是generated文件夾。
客戶端調(diào)用就相對(duì)容易多了
FooServiceService serviceLocator = new FooServiceServiceLocator();
try {
FooService service = serviceLocator.getFooService();
FooBean fooBean = service.getFooBean("fooBean", 10, true);
System.out.println(fooBean);
} catch (ServiceException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
運(yùn)行客戶端看看!
做簡(jiǎn)單的WebService就基本上這樣了。但這樣的Webservice還遠(yuǎn)遠(yuǎn)不能用戶真正的項(xiàng)目中,還有很多問(wèn)題沒(méi)有解決,比如安全問(wèn)題,有狀態(tài)會(huì)話問(wèn)題等等。這些問(wèn)題很多可以通過(guò)Hanlder來(lái)解決,他有點(diǎn)類似于FilterServlet。具體的東西我還有沒(méi)有研究,axis在這方面的資料就很少了。研究明白以后再寫一篇吧。
本文源碼:http://www.tkk7.com/Files/mstar/WsLearn.rar