WebService 開發工具 AXIS 指南
本文檔是開源文檔,你可 以自由使用和傳播!
Axis ( A pache eX tensible I nteraction S ystem )是一款開源的 WebService 運行引擎,它是 SOAP 協 議的一個實現,其本身來源於 Apache 的另一個項目 Apache SOAP 。 Axis 分爲 1.x 系列和 Axis 2 系列,兩個系列體系結 構和使用上有較大的區別,相對而言, Axis1.x 更加穩定,文檔也比較齊全,因此本文內容以 Axis 1.x 系列最新版本 1.4 爲基礎, Axis 有 C++ 和 Java 兩個版本的實現,本文描述 Java 版的 Axis 。
採用 Axis 實 現 WebService 時,只需要 POJO 即 可,不要求實現特定的接口或繼承特定的父類,不必爲提高效率而購買專門的開發工具,在 Eclipse 中開發不需要添加任何插件。
Axis 對運行環境的要求是隻要支持 Servlet2.3 或以上即可,目前主要的應用服務器都可運行 Axis ,如 Tomcat 、 JBoss 、 Websphere 、 WebLogic 、 Jetty 。並且 Axis 可以很容易和應用程序整合 在一個 Web 應用中,將應用程序的功能展示爲 WebService 。
此外, Axis 的 另一個好處就是不用和某個應用服務器綁定在一起,具有很好的可移植性,除配置文件可能要作小的修改外,服務的實現代碼不需要做任何修改。
Axis 官方網站爲 http://ws.apache.org/axis
2 環境配置
本文例子代碼運行環境要求如下:
Windows XP SP2 中文版, JDK1.4 或以上, Apache Axis1.4 , Tomcat 4.1 或其他任何支持 Servlet2.3 的應用服務器,本文以 Tomcat 4.1 爲例, Java 開發工具爲 Eclipse3.1 。
如果還沒有安裝 JDK , 從 http://java.sun.com/javase 下載並安裝到本地硬盤,以下用 JAVA_HOME 表示 JDK 安裝在本地的路徑。
從 http://ws.apache.org/axis 下載 Axis1.4 ,安裝包是一個壓縮文件,不用安裝程序,直接解壓到本地硬盤即可。以下用 AXIS_HOME 表示 axis 解壓到本地的路徑。
從 http://tomcat.apache.org 下載 Tomcat 4.1 安裝包,按 Tomcat 安裝指南安裝到本地,以下用 TOMCAT_HOME 表示 Tomcat 安裝在本地的路徑。安 裝完成後驗證安裝正確,缺省情況下輸入 http://localhost:8080 可進入 Tomcat 頁面。
下面在 Tomcat 中增加一個 Web 應用,併爲其增對 Axis 支 持:
在 TOMCAT_HOME/webapps 下創建一個新目錄 myservices ,然後將 AXIS_HOME/webapps/axis 目錄下的文件和子目錄都複製到 myservices 目錄下。並將 AXIS_HOME/lib/axis-ant.jar 也複製到 myservices/WEB-INF/lib 目錄下,該 jar 文 件提供對 ant 腳本的支持。
啓動 Tomcat ,然後在瀏覽器中輸入 http://localhost:8080/myservices ,如果能顯示下面的頁面,則表示 axis 已 成功安裝到 myservices 應用中,下面創建的 WebService 都在該應用中運行。
進一步點擊 Validation 可以顯示運行環境信息,點擊 List 可以列出 Axis 自帶的兩個 WebService 。 以後增加的 WebService 也顯示在這裏。
以下代碼在 Eclipse 中開發,爲此在 Eclipse 創建一個 java 項目,名稱爲 axis-guide ,並將 Axis 的包加到項目的 java build path 中,如下圖所示: Axis 這 些包位於 Web 引用 myservices 的 WEB-INF/lib 目錄下。
3 第一個 WebService
3.1 服務端
準備好運行環境後,下面就開始開發 WebService ,該 WebService 名爲 EchoService ,有一個方法 echo , 獲得一個輸入信息後,加入附加信息返回給調用者。原代碼如下:
EchoService.java
package chen.axisguide.ch3;
public class EchoService {
public String echo(String msg) {
return "your input message is " + msg; } }
|
可以看到該類是一個 POJO ,不需要其他任何附加說明來表明其是一個 WebService 實現。
爲了讓 Axis 能識別服務,需要爲服務提供一個描述文件,通常文件名爲 deploy.wsdd ,因爲該文件用於將服務部署到 Axis 引 擎所在的 Web 應用中。 deploy.wsdd 文件格式爲 xml 格 式, EchoService 的 deploy.wsdd 內容如下:
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<service name="EchoService" provider="java:RPC" > <parameter name="className" value="chen.axisguide.ch3.EchoService" /> <parameter name="allowedMethods" value="*" /> </service>
</deployment>
|
service 節點的 name 屬性定義服務的名字,在 客戶端訪問時會用到該名字, provider 中定義 WebService 的提供方式 爲 RPC 。
第一個 parameter 用來描述服務實現類的類名,包括包名。第二個 parameter 參數描述類中哪 些方法可以暴露爲 WebService 方法,這裏用 * 表示全 部方法。
如果要卸載一個已部署的 WebService ,需要提供一個卸載描述文件,用 Axis 提 供的工具讀取該文件來卸載已部署的 WebService 。 上述 deploy.wsdd 對應的卸載 文件爲 undeploy.wsdd ,內容如下:
<undeployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<service name="EchoService"/>
</undeployment>
|
deploy.wsdd 和 undeploy.wsdd 和原代 碼 EchoService.java 位於同一個目錄中。
在項目 axis-guide 的根目錄下創建一個 Ant 的 build.xml 來執行編譯和部署任務。目錄結構如下圖所示:
build.xml 內容如下:
<?xml version="1.0" encoding="utf-8"?> <project name="myservice" default="deployws"> <!-- If your Tomcat installation folder is another , replace below with yourself --> <property name="web.home" location="C:/Tomcat4.1/webapps/myservices" /> <property name="webclass.dir" location="${web.home}/WEB-INF/classes" /> <property name="weblib.dir" location="${web.home}/WEB-INF/lib" /> <property name="src.dir" location="src" />
<path id="lib.class.path"> <fileset dir="${weblib.dir}"> <include name="**/*.jar"/> </fileset>
</path>
<taskdef name="axis-admin" classname="org.apache.axis.tools.ant.axis.AdminClientTask"> <classpath refid="lib.class.path"/> </taskdef>
<target name="compile"> <javac srcdir="${src.dir}" destdir="${webclass.dir}" source="1.4" compiler="javac1.4" debug="on"> <classpath> <path refid="lib.class.path"/> </classpath> <exclude name="**/*Test.java" /> </javac> </target>
<target name="deployws" depends="compile"> <axis-admin url="http://localhost:8080/myservices/servlet/AxisServlet" xmlfile="${src.dir}/chen/axisguide/ch3/deploy.wsdd"/>
</target>
<target name="undeployws"> <axis-admin url="http://localhost:8080/myservices/servlet/AxisServlet" xmlfile="${src.dir}/chen/axisguide/ch3/undeploy.wsdd"/> </target>
</project>
|
task-def 定義了一個名爲 axis-admin 的任務,該任務負責根據 deploy.wsdd 或 undeploy.wsdd 來部署 或卸載 WebService 。此任務的實現類 org.apache.axis.tools.ant.axis.AdminClientTask 位於 axis-ant.jar 文件中。
服務實現類原代碼編譯後的 .class 文件放在 myservices/WEB-INF/classes 目錄下,在 build.xml 的 compile 任務中實現。
在 Eclipse 環境中用 Ant 運行該 build.xml ,執行成功後 在瀏覽器中輸入 http://localhost:8080/myservices/servlet/AxisServlet ,可以看到如下的頁面,表示 EchoService 已部署成功。
3.2 客戶端
WebService 部署成功後,下面開發客戶端去調用 WebService 的方法,打印返回結果。
Client.java
package chen.axisguide.ch3;
import java.rmi.RemoteException;
import javax.xml.rpc.ParameterMode; import javax.xml.rpc.ServiceException;
import org.apache.axis.client.Call; import org.apache.axis.client.Service; import org.apache.axis.encoding.XMLType;
/** * 訪問 EchoService 的客戶端 * * */ public class Client {
/** * @param args * @throws ServiceException * @throws RemoteException */ public static void main(String[] args) throws ServiceException, RemoteException { // wsdl 中 address 節點的 location 屬性 String endpoint = "http://localhost:8080/myservices/services/EchoService"; // 要調用的方法名 String method = "echo";
Service service = new Service(); Call call = (Call) service.createCall(); // 設置客戶端訪問的遠程端點 call.setTargetEndpointAddress(endpoint); // 設置輸入參數類型 call.addParameter("p1",XMLType.XSD_STRING,ParameterMode.IN); // 設置返回值類型 call.setReturnType(XMLType.XSD_STRING);
// 輸入參數值 String msg = "call from java"; // 調用遠程方法 String result = (String)call.invoke(method,new Object[]{msg});
System.out.println(result);
}
}
|
確認 Tomcat 已啓動,在 Eclipse 環境中運行 Client ,如果沒有錯誤,在控制檯將會打印從 EchoService 的 echo 方法獲得的結果。
客戶端訪問 WebService 的主要步驟包括:
l 創建 Call 對象,設置 WebService 的訪問端點;
l 註冊序列化和反序列對象,詳見下節;
l 設置訪問方法的參數類型以及返回值類型;
l 初始化參數值;
l 調用 WebService 的方法,獲得 返回結果。
JAX-RPC 規範中定義了三種訪問服務端的方法:
1. 在客戶端生成遠程服務端的靜態代理文件,編譯後提供給客戶端訪問服務使用, Axis 提供了工具可根據服務的 wsdl 生 成靜態代理文件。但這種方法會在客戶端增加若干附加文件,服務端接口發生變化時需要重新在客戶端生成文件。
2. 根據服務端接口文件訪問服務。這種方法要求服務實現一個接口,並且該接口要從 java.rmi.Remote 擴展,客戶端通過獲得該接口的代理來訪問服務。這種方法需要在客戶端保存一個服務的接口文件。
3. 動態調用接口。客戶端只須通過服務端口、訪問方法名和參數類型即可訪問服務的方法。訪問方式類似於用 反射來調用類方法。這種方法的客戶端和服務端的耦合度比前兩種要低,上面 Cilent.java 用的就是這種方法。 後面的例子也都採用這種方式訪問服務端。
4 序列化
Axis 中客戶端和服務器端之間的消息使用 SOAP 封 裝, java 程序中的數據需要序列化後保存在 XML 文 檔中通過網絡傳輸到接收方,接收方從 XML 文檔中反序列化爲 java 虛 擬機可識別的數據對象。對 java 基本類型, Axis 提 供了默認的序列化和反序列化功能,不需要特別申明即可使用。對自定義類型,如果是 JavaBean ,則可用 beanMapping 在 wsdd 文件中申明該類型所需要的 序列化和反序列化器即可。 Axis 已自帶 JavaBean 的序列化和反序 列化功能。 如果 Axis 自帶的序列化和反序列化器 不能對自定義對象進行序列化處理,你可以自定義序列化和反序列化器,然後用 typeMappping 在 wsdd 文件中申明即可。
在 Axis 中, xml 數據類型和 java 類 型之間的映射關係如下:
XML 類 型 |
java 類 型 |
xsd:base64Binary |
byte[] |
xsd:boolean |
boolean |
xsd:byte |
byte |
xsd:date |
java.util.Date |
xsd:dateTime |
java.util.Calendar |
xsd:decimal |
java.math.BigDecimal |
xsd:double |
double |
xsd:float |
float |
xsd:hexBinary |
byte[] |
xsd:int |
int |
xsd:integer |
java.math.BigInteger |
xsd:long |
long |
xsd:QName |
javax.xml.namespace.QName |
xsd:short |
short |
xsd:string |
java.lang.String |
下面的例子中服務器端向客戶端返回一個列表,列表中包含自定義的 Book 對象。
服務實現代碼如下:因爲只是說明對象的序列化方式,因此就直接創建 對象放入列表對象中,沒有從數據庫中搜索數據。
LibraryService.java :對象序列化服務端代碼
package chen.axisguide.ch4;
import java.util.ArrayList; import java.util.List;
public class LibraryService {
// 返回一個包含 Book 對象的列表 public List query(String keyword) {
Book book1 = new Book(); book1.setAuthor("Jack Johnson"); book1.setContent("about java development."); book1.setIsbn("123-456-789"); book1.setName("Java deplopment handbook");
Book book2 = new Book(); book2.setAuthor("Kim Kent"); book2.setContent("about ruby development."); book2.setIsbn("123-654-897"); book2.setName("ruby deplopment handbook");
List res = new ArrayList(); res.add(book1); res.add(book2);
return res;
} }
|
服務端代碼中不需要對序列化做特殊處理。
Book 的定義如下:
Book.java
package chen.axisguide.ch4;
public class Book { private String isbn; private String name; private String author; private String content;
public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getIsbn() { return isbn; } public void setIsbn(String isbn) { this.isbn = isbn; } public String getName() { return name; } public void setName(String name) { this.name = name; }
}
|
序列化的處理在服務部署文件中定義:
deploy.wsdd : LibraryService 描述 文件
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<service name="LibraryService" provider="java:RPC" > <parameter name="className" value="chen.axisguide.ch4.LibraryService" /> <parameter name="allowedMethods" value="*" />
<beanMapping qname="myNS:Book" xmlns:myNS="LibraryService" languageSpecificType="java:chen.axisguide.ch4.Book"/> </service>
</deployment>
|
beanMapping 節點定義了 Book 對 象的序列化和反序列化使用 Axis 自帶的針對 JavaBean 的序列化和反序列化器來實現。
其中, xmlns 定義命名空間爲 LibraryService ,和服務同名, qname 定 義爲命名空間 myNS 中的 Book ,與被序列化的類名相同, 這種命名方式可使處理的對象一目瞭然。當然也可命名爲其他名字。
下面看一下客戶端代碼中對序列化的處理。
Cilent.java :訪問 LibraryService 客 戶端代碼。
package chen.axisguide.ch4;
import java.rmi.RemoteException;
import javax.xml.namespace.QName; import javax.xml.rpc.ParameterMode; import javax.xml.rpc.ServiceException;
import org.apache.axis.client.Call; import org.apache.axis.client.Service; import org.apache.axis.encoding.XMLType;
/** * 訪問 LibraryService 的客戶端 * * */ public class Client {
/** * @param args * @throws ServiceException * @throws RemoteException */ public static void main(String[] args) throws ServiceException, RemoteException { // wsdl 中 address 節點的 location 屬性 String endpoint = "http://localhost:8080/myservices/services/LibraryService"; // 要調用的方法名 String method = "query";
Service service = new Service(); Call call = (Call) service.createCall(); // 設置客戶端訪問的遠程端點 call.setTargetEndpointAddress(endpoint);
QName qn = new QName("LibraryService","Book"); call.registerTypeMapping(Book.class, qn, new org.apache.axis.encoding.ser.BeanSerializerFactory(Book.class, qn), new org.apache.axis.encoding.ser.BeanDeserializerFactory(Book.class, qn));
// 設置輸入參數類型 call.addParameter("p1",XMLType.XSD_STRING,ParameterMode.IN); // 設置返回值類型 call.setReturnType(XMLType.XSD_ANYTYPE);
// 輸入參數值 String msg = "key word"; // 調用遠程方法 Object[] result = (Object[])call.invoke(method,new Object[]{msg});
for(int i = 0; i < result.length; i++) { Book bk = (Book)result[i]; System.out.println("ISBN: " + bk.getIsbn() + " Book Name: " + bk.getName()); }
}
}
|
在客戶端,首先爲 Book 定義一個 QName 對象,構造函數中的第 一個參數值要和 wsdd 中 beanMapping 節點的 xmlns:myNS 屬性值相同,第二個參數值要和 beanMapping 的 qname 屬性值相同。然後在 Call 對象中註冊自定義類型所用的序列化和反序列化器。通過以下語句來實現:
call.registerTypeMapping(Book.class, qn, new org.apache.axis.encoding.ser.BeanSerializerFactory(Book.class, qn), new org.apache.axis.encoding.ser.BeanDeserializerFactory(Book.class, qn)); |
最後在 build.xml 中增加對 deploy.wsdd 文件的部署。內容如下:
<target name="deployws" depends="compile"> <axis-admin url="http://localhost:8080/myservices/servlet/AxisServlet" xmlfile="${src.dir}/chen/axisguide/ch4/deploy.wsdd"/>
</target>
|
5 處理器 (Handler)
Axis 引擎在處理消息過程中,可以自定義處理器對請求或響應的消息進行攔截處理,處理器的動作對服務的實 現部分透明。這種機制可以將日誌、安全檢查、加密解密等與業務無關的操作抽取出來單獨處理,降低模塊間的依賴關係。
自定義處理器時從 org.apache.axis.handlers.BasicHandler 繼承,在方法 invoke(MessageContext msgContext) 進行攔截處理。消息等運行環境信息可以從 msgContext 得到。
自定義處理器要發揮作用,需要在 wsdd 文件中進行說明,方法如下:
1- 用 <handler name=”handlername” type=”java:classname”> 定義處理器,如果要給處理器傳遞參數,可在 <handler> 下用 <parameter name=”name” value=”value” /> 進行說明。 之後在自定義處理器類代碼中用 getOption(“name”) 可得到 value 的值。
2- 在 <service> 中 用 <requestFlow> 或 <responseFlow> 定義在請求消息流或返回消息流中要調用的處理器。如果有多個處理器,則按其在 <requestFlow> 或 <responseFlow> 中排列順序依次調用。格式如下:
<service>
<requestFlow>
<handler type=”handlername” />
</requestFlow>
</service>
下面的例子中服務器端和客戶端代碼使用第 3 節的代碼,在請求消息流中增加兩個處理器,第一個處理器在控制檯打印出 SOAP 包的 body 內容,然後在 MessageContext 中設置一個屬性值;第二個處理器從 MessageContext 中取出第一個處理器設置的屬性值,在控制檯打印出來。
第一個處理器原代碼如下:
FirstHandler.java
package chen.axisguide.ch5;
import javax.xml.soap.SOAPException;
import org.apache.axis.AxisFault; import org.apache.axis.Message; import org.apache.axis.MessageContext; import org.apache.axis.handlers.BasicHandler; import org.apache.axis.message.SOAPBody;
public class FirstHandler extends BasicHandler {
public void invoke(MessageContext msgContext) throws AxisFault {
Message reqMsg = msgContext.getRequestMessage();
try { SOAPBody reqBody = (SOAPBody) reqMsg.getSOAPBody(); System.out.println("==================================="); System.out.println("request SOAP body " + reqBody.getAsString()); System.out.println("==================================="); // 設置屬性傳遞到下一個 handler msgContext.setProperty("handler1","handler1 processed");
} catch (SOAPException e) {
e.printStackTrace(); } catch (Exception e) {
e.printStackTrace(); }
}
}
|
第二個處理器原代碼:
SecondHandler.java
package chen.axisguide.ch5;
import org.apache.axis.AxisFault; import org.apache.axis.MessageContext; import org.apache.axis.handlers.BasicHandler;
public class SecondHandler extends BasicHandler {
public void invoke(MessageContext msgContext) throws AxisFault {
System.out.println("Get data from previous handler : " + msgContext.getProperty("handler1"));
}
}
|
wsdd 文件內容如下:
deploy.wsdd
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <handler name="handler1" type="java:chen.axisguide.ch5.FirstHandler" /> <handler name="handler2" type="java:chen.axisguide.ch5.SecondHandler" />
<service name="EchoService" provider="java:RPC" > <parameter name="className" value="chen.axisguide.ch5.EchoService" /> <parameter name="allowedMethods" value="*" /> <requestFlow> <handler type="handler1" /> <handler type="handler2" /> </requestFlow> </service>
</deployment>
|
在 build.xml 中增加對 deploy.wsdd 文件的部署處理。內容如下:
<target name="deployws" depends="compile"> <axis-admin url="http://localhost:8080/myservices/servlet/AxisServlet" xmlfile="${src.dir}/chen/axisguide/ch5/deploy.wsdd"/> </target>
|
服務端和客戶端原代碼和第 3 節相同,不再列出。
在 Eclipse 中用 Ant 運行 build.xml ,在 Tomcat 中重新載入 Web 應 用,然後在 Eclipse 中運行 Client.java ,在 Tomcat 的控制檯將會打印出 SOAP body 信息以及 handler1 屬性值。
6 訪問控制
開發好 WebService 並向外暴露服務後,由於 WebService 訪問協議都是公開的,客戶端只要知道服務的 wsdl ,就可以訪問 WebService 中的方法。爲防止非法的訪問,可以要求客戶端訪問 WebService 時提供用戶名和口令,只有在服務端通過身份認證後纔可繼續訪問方法,否則請求被拒絕。
下面看一下 Axis 中 如何使用用戶名、口令來控制對 WebService 的訪問。
在 myservices/WEB-INF 下創建一個名爲 users.lst 的文本文件,一行表示一對用戶名、口令,之間用空格分隔,內容如下:
user1 password1 user2 password2 |
實現一個 WebService ,它有一個 foo() 方 法,該方法打印出通過身份認證的用戶的用戶名,代碼如下:
SecurityService.java
package chen.axisguide.ch6;
import org.apache.axis.MessageContext;
public class SecurityService { public void foo() {
MessageContext msgContext = MessageContext.getCurrentContext(); System.out.println(" 你 已通過身份認證 !"); System.out.println(" 你 的用戶名 : " + msgContext.getUsername());
} }
|
爲了讓 Axis 知曉要對該服務的訪問驗證用戶名和口令,需要在 wsdd 文件中增加一個 Axis 自帶的處理器 SimpleAuthenticationHandler ,該處理器會從 MessageContext 中取出用戶名和口令,並與 users.lst 中的用戶名和口令進行比對,如果匹配,則通過驗證,繼續後面的處理,否則拋出 (401)Unauthorized 異常。
deploy.wsdd
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<service name="SecurityService" provider="java:RPC" > <parameter name="className" value="chen.axisguide.ch6.SecurityService" /> <parameter name="allowedMethods" value="*" /> <requestFlow> <handler type="java:org.apache.axis.handlers.SimpleAuthenticationHandler" /> </requestFlow> </service> </deployment>
|
客戶端在訪問 WebService 時,需要在 Call 對 象中設置在 users.lst 文件中已有的用戶名和對應的口令。客戶端代碼如下:
package chen.axisguide.ch6;
import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;
import org.apache.axis.AxisFault; import org.apache.axis.client.Call; import org.apache.axis.client.Service;
/** * 訪問 SecurityService 的客戶端 * * */ public class Client {
/** * @param args * @throws ServiceException * @throws RemoteException */ public static void main(String[] args) throws ServiceException, RemoteException { // wsdl 中 address 節點的 location 屬性 String endpoint = "http://localhost:8080/myservices/services/SecurityService"; // 要調用的方法名 String method = "foo";
Service service = new Service(); Call call = (Call) service.createCall(); // 設置客戶端訪問的遠程端點 call.setTargetEndpointAddress(endpoint); // 設置用戶名和口令 call.setUsername("user1"); call.setPassword("password1");
// 調用遠程方法 try{ call.invoke(method,new Object[]{}); }catch(AxisFault e) { System.err.println(e.getFaultString()); } }
} |
Call 對象用 setUserName() 和 setPassword() 來設置用戶名和口令。
Axis 自帶的身份認證功能比較簡單,如果其安全性不能滿足應用系統的要求,可以自定義一個處理器替代 SimpleAuthenticationHandler ,在自定義處理器中採用其他的安全框架(如 Acegi http://www.acegisecurity.org/ )來保護你的 WebService 。
7 總結
從上面的實例可以看出, Axis 在 開發 WebService 方面具有如下優勢:
l 輕量級: Axis 本身只需要 Servlet 容器就可運行,因此能夠在大多數輕量級的 Web 應 用服務器上運行;另外在 Axis 中服務的實現類只要求是一個 POJO , 沒有強制要求實現特定接口或從特定父類繼承,這使得開發和測試非常容易,不需要爲提高開發效率而購置重量級的 IDE 。
l 擴展性好:在 Axis 中, 可以通過加入自定義處理器( Handler )方式來擴展 Axis 的 功能,並且這種功能的增加對業務部分可以做到透明,即業務代碼中不需要爲使用增加的功能而修改代碼。
l 可移植性強: Axis 可 以在支持 Servlet 的容器中運行,運行於其中的 WebService 也就可以在現有的大多數的 Web 應用服務器上運行,不需要對服務的實現代碼做任何的改動。