Apache Axis 是經典的 Java Web Service 框架,雖然已停止維護,但在大量存量系統中仍廣泛使用。本文詳細記錄基於 Axis 1.4 的 WebService 發布與調用全流程:環境搭建、服務端開發、WSDD 配置、客戶端生成、測試驗證,並解決 .NET 互調兼容性問題。
axis-1.4/webapps/axis 目錄完整複製到 tomcat/webapps/ 下https://localhost:8080/axis/ 驗證部署成功// 接口定義
package org.calculateservice.service;
public interface ICalculate {
int add(int x, int y);
int sub(int x, int y);
}
// 實現類
package org.calculateservice.service;
public class CalculateImp implements ICalculate {
@Override
public int add(int x, int y) {
return x + y;
}
@Override
public int sub(int x, int y) {
return x - y;
}
}
放置於 WEB-INF/deploy.wsdd,詳細定義服務元數據:
<deployment xmlns="https://xml.apache.org/axis/wsdd/"
xmlns:java="https://xml.apache.org/axis/wsdd/providers/java">
<service name="Calculate" provider="java:RPC" use="literal">
<!-- WSDL 元數據配置 -->
<parameter name="wsdlTargetNamespace" value="https://web.webservice.CalculateService.org" />
<parameter name="wsdlServiceElement" value="Calculate" />
<parameter name="wsdlServicePort" value="CalculateService" />
<parameter name="wsdlPortType" value="ICalculate" />
<!-- 實現類與作用域 -->
<parameter name="className" value="org.calculateservice.service.CalculateImp" />
<parameter name="scope" value="Request" /> <!-- Request/Session/Application -->
<!-- 允許暴露的方法 -->
<parameter name="allowedMethods" value="add sub" />
<parameter name="typeMappingVersion" value="1.2" />
<!-- 方法級別詳細定義(可選,Axis 可自動推導) -->
<operation name="add" qname="operNS:add"
xmlns:operNS="https://web.webservice.CalculateService.org"
returnQName="addReturn" returnType="rtns:int"
xmlns:rtns="https://www.w3.org/2001/XMLSchema"
soapAction="">
<parameter qname="x" type="tns:int" xmlns:tns="https://www.w3.org/2001/XMLSchema" />
<parameter qname="y" type="tns:int" xmlns:tns="https://www.w3.org/2001/XMLSchema" />
</operation>
<operation name="sub" qname="operNS:sub"
xmlns:operNS="https://web.webservice.CalculateService.org"
returnQName="subReturn" returnType="rtns:int"
xmlns:rtns="https://www.w3.org/2001/XMLSchema"
soapAction="">
<parameter qname="x" type="tns:int" xmlns:tns="https://www.w3.org/2001/XMLSchema" />
<parameter qname="y" type="tns:int" xmlns:tns="https://www.w3.org/2001/XMLSchema" />
</operation>
</service>
</deployment>
Axis 默認要求 SOAPAction Header,.NET 客戶端可能不發送。自定義 Servlet 兼容此場景:
package org.calculateservice.core;
import org.apache.axis.transport.http.AxisServlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
public class AxisServlet extends org.apache.axis.transport.http.AxisServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 兼容無 SOAPAction 的請求(.NET 客戶端常見)
String soapAction = req.getHeader("SOAPAction");
if (soapAction == null || soapAction.equals("\"\"")) {
// 從 SOAP Body 推導操作名
// 實際項目中可根據具體需求實現邏輯
}
super.doPost(req, resp);
}
}
在 web.xml 中替換默認 AxisServlet:
<servlet>
<servlet-name>AxisServlet</servlet-name>
<servlet-class>org.calculateservice.core.AxisServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
編寫 generate-server-config.bat:
@echo off
set AXIS_HOME=..\..
set CLASSPATH=%AXIS_HOME%\lib\axis.jar;%AXIS_HOME%\lib\commons-discovery-0.2.jar;
%AXIS_HOME%\lib\commons-logging-1.0.4.jar;%AXIS_HOME%\lib\jaxrpc.jar;
%AXIS_HOME%\lib\saaj.jar;%AXIS_HOME%\lib\log4j-1.2.8.jar;
%AXIS_HOME%\lib\wsdl4j-1.5.1.jar;%CLASSPATH%
java org.apache.axis.utils.AdminServer deploy.wsdd
pause
運行後生成 WEB-INF/server-config.wsdd,服務正式發布。訪問 https://localhost:8080/你的項目/servlet/AxisServlet?wsdl 查看 WSDL。
編寫 wsdl2java.bat:
@echo off
set AXIS_HOME=..\..
set CLASSPATH=%AXIS_HOME%\lib\axis.jar;%AXIS_HOME%\lib\commons-discovery-0.2.jar;
%AXIS_HOME%\lib\commons-logging-1.0.4.jar;%AXIS_HOME%\lib\jaxrpc.jar;
%AXIS_HOME%\lib\saaj.jar;%AXIS_HOME%\lib\log4j-1.2.8.jar;
%AXIS_HOME%\lib\wsdl4j-1.5.1.jar;%CLASSPATH%
java org.apache.axis.wsdl.WSDL2Java -o ./src -p com.client.proxy
https://localhost:8080/你的項目/servlet/AxisServlet?wsdl
pause
生成代碼結構:
com.client.proxy/
├── Calculate.java // Service 接口
├── CalculateService.java // PortType 接口
├── CalculateServiceLocator.java // 工廠類(核心調用入口)
├── CalculateSoapBindingStub.java // 具體存根實現
└── deploy.wsdd / undeploy.wsdd // 客戶端部署描述(可忽略)
package com.client.test;
import com.client.proxy.Calculate;
import com.client.proxy.CalculateServiceLocator;
import javax.xml.rpc.ServiceException;
import java.rmi.RemoteException;
public class TestClient {
public static void main(String[] args) throws ServiceException, RemoteException {
// 1. 獲取 Locator 工廠
Calculate calculate = new CalculateLocator();
// 2. 獲取服務端口(可修改端點地址)
CalculateService service = calculate.getCalculateService();
// service._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, "https://新地址/servlet/AxisServlet");
// 3. 調用業務方法
int result = service.add(1, 2);
System.out.println("1 + 2 = " + result); // 輸出: 1 + 2 = 3
int result2 = service.sub(5, 3);
System.out.println("5 - 3 = " + result2); // 輸出: 5 - 3 = 2
}
}
WebContent/
├── WEB-INF/
│ ├── web.xml // Servlet 配置(含自定義 AxisServlet)
│ ├── deploy.wsdd // 服務部署描述
│ ├── server-config.wsdd // 生成的運行時配置
│ └── lib/ // Axis 依賴 JAR 包
└── src/
├── org.calculateservice.service/ // 服務接口與實現
│ ├── ICalculate.java
│ └── CalculateImp.java
├── org.calculateservice.core/ // 自定義 AxisServlet
│ └── AxisServlet.java
└── com.client.proxy/ // WSDL2Java 生成的客戶端代碼
├── Calculate.java
├── CalculateService.java
├── CalculateServiceLocator.java
└── CalculateSoapBindingStub.java
| 問題 | 原因 | 解決方案 |
|---|---|---|
| .NET 客戶端調用失敗 | .NET 默認不發送 SOAPAction Header | 自定義 AxisServlet 兼容無 SOAPAction 請求 |
| WSDL 命名空間混亂 | deploy.wsdd 未顯式指定 targetNamespace | 在 deploy.wsdd 中完整配置 wsdlTargetNamespace 等參數 |
| 客戶端地址硬編碼 | CalculateLocator 中 endpoint 寫死 | 運行時動態設置:stub._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, newUrl) |
| 複雜類型序列化失敗 | 缺少類型映射註冊 | 在 deploy.wsdd 中添加 <typeMapping> 或使用 JAXB 註解 |
| 中文亂碼 | 編碼不一致 | 統一 UTF-8:Tomcat connectorURIEncoding、Axis 配置、SOAP Message 編碼 |
Axis 1.4 於 2006 年發布,2010 年停止維护,存在安全漏洞、性能差、不支持現代標準(SOAP 1.2、WS-Security、REST)等問題。新項目建議採用:
本文作為 Axis 存量系統維護的參考文檔,完整記錄了從環境搭建到客戶端調用的全鏈路實踐。理解 Axis 的工作原理,有助於遷移時對接舊系統接口契約。