Apache Axis2 快速入门与集成示例
以 Axis2 2.0.1 实测跑通服务全链路:javac -g 编译 POJO、aar 发布与热部署、SOAP 调用、stub 生成、Maven 与 TCP 客户端调用,均附真实回显。
Apache Axis2 快速入门与集成示例
目标:从一个纯 Java 类出发,跑通 Axis2 服务端到客户端的完整链路——编写并编译 POJO 服务、打包 .aar 发布与热部署验证、curl 直接发送 SOAP 调用、wsdl2java 生成客户端 stub 直跑、Maven 工程集成、TCP 传输调用——每一步均附真实命令与回显,已在 Ubuntu 22.04.5(OpenJDK 17.0.20.1、Axis2 2.0.1 standalone)实测通过。
一、环境信息
本篇全部命令与回显均在以下环境中实测采集:
- 操作系统:Ubuntu 22.04.5 LTS,x86_64
- JDK:OpenJDK 17.0.20.1(JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64)
- 组件版本:Apache Axis2 2.0.1(官方 bin 发行包,standalone 形态)
- 安装目录:
/opt/axis2(服务仓库/opt/axis2/repository/services,配置/opt/axis2/conf/axis2.xml) - 服务端口:HTTP 8080、TCP 6060(第七节开启)
- 客户端构建:Apache Maven 3.6.3
- 示例工程目录:
/opt/lab/hello-service(服务端)、/opt/lab/hello-maven(Maven 客户端)、/opt/lab/client-repo(TCP 客户端仓库) - 执行用户:root
二、前置条件
按《Ubuntu 22.04 安装 Apache Axis2 指南》或《CentOS 8 安装 Apache Axis2 指南》完成 Axis2 2.0.1 standalone 安装,并确认基线服务可调用:
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH
curl -s "http://localhost:8080/axis2/services/Version/getVersion"
回显(实测):
<ns:getVersionResponse xmlns:ns="http://axisversion.sample"><ns:return>Hi - the Axis2 version is 2.0.1</ns:return></ns:getVersionResponse>
Maven 客户端一节另需 Maven(Ubuntu 用 apt install maven 即可,实测版本 3.6.3)。
三、发布第一个 POJO 服务
3.1 编写并编译服务类
Axis2 服务就是一个普通 Java 类(POJO),公开方法即服务操作。创建工程目录并编写服务类:
mkdir -p /opt/lab/hello-service/src /opt/lab/hello-service/build/HelloService/META-INF
cat > /opt/lab/hello-service/src/HelloService.java <<'EOF'
public class HelloService {
public String sayHello(String name) {
return "Hello, " + name + "! (from Axis2 " + org.apache.axis2.Version.getVersionText() + ")";
}
public String echo(String msg) {
return "echo: " + msg;
}
}
EOF
编译(-g 保留形参名,是本篇最关键的一个编译参数):
javac -version && javac -g -cp "/opt/axis2/lib/*" -d /opt/lab/hello-service/build/HelloService /opt/lab/hello-service/src/HelloService.java
回显(实测,编译成功无输出):
javac 17.0.20.1
-cp "/opt/axis2/lib/*" 用于解析源码里引用的 org.apache.axis2.Version(服务端 lib 下的 axis2-kernel jar 提供)。-g 的作用:Axis2 反射生成 WSDL 时用形参名作为请求元素名,缺省编译(无 -g)时形参名丢失,WSDL 参数会退化成 args0,客户端 stub 的 setter 也随之变成 setArgs0——两种编译方式的 stub 代码差异实测确认过(setName 与 setArgs0)。
3.2 编写 services.xml 并打包 .aar
服务级描述文件声明服务名、实现类与消息接收器:
cat > /opt/lab/hello-service/build/HelloService/META-INF/services.xml <<'EOF'
<service name="HelloService" scope="application">
<description>POJO demo service</description>
<parameter name="ServiceClass">HelloService</parameter>
<transports>
<transport>http</transport>
<transport>tcp</transport>
</transports>
<operation name="sayHello">
<messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</operation>
<operation name="echo">
<messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</operation>
</service>
EOF
把类与描述文件打成 .aar 包并直接放入服务仓库:
cd /opt/lab/hello-service/build/HelloService && jar cf /opt/axis2/repository/services/HelloService.aar HelloService.class META-INF/
jar tf /opt/axis2/repository/services/HelloService.aar
回显(实测):
META-INF/
META-INF/MANIFEST.MF
HelloService.class
META-INF/services.xml
.aar 的最小结构就是"编译产物 + META-INF/services.xml"(MANIFEST.MF 由 jar 命令自动生成)。<transports> 声明服务可经 HTTP 与 TCP 访问——只走 HTTP 时省略亦可,但第七节要走 TCP,这里一并声明,避免服务对 TCP 传输不可用。
3.3 部署验证与热部署行为
3.2 节打包时已直接写入仓库。 Axis2 默认 hotdeployment=true,新 .aar 落入仓库后由后台线程自动部署(systemd 托管时用 journalctl 观察日志):
journalctl -u axis2 --no-pager --since "2 minutes ago" | grep "Deploying Web service"
回显(实测):
10:05:50.925 [main] INFO org.apache.axis2.deployment.DeploymentEngine - Deploying Web service: HelloService.aar - file:/opt/axis2/repository/services/HelloService.aar
热部署对"新增"生效,对"更新"受 hotupdate=false(默认)限制——实测:覆盖同名 .aar 后服务不重新加载,须重启进程才生效。热卸载/重部署的完整行为实测(删除 .aar 约一个轮询周期后被卸载,放回后再被部署):
rm /opt/axis2/repository/services/HelloService.aar && sleep 12 && cp /tmp/HelloService.aar /opt/axis2/repository/services/HelloService.aar && sleep 12 && journalctl -u axis2 --no-pager --since "1 minute ago" | grep -E "Undeploying|Deploying Web service"
回显(实测):
10:08:10.931 [Timer-0] INFO org.apache.axis2.deployment.ServiceDeployer - Undeploying Web service: HelloService
10:08:20.935 [Timer-0] INFO org.apache.axis2.deployment.DeploymentEngine - Deploying Web service: HelloService.aar - file:/opt/axis2/repository/services/HelloService.aar
两行日志都出自 Timer-0 线程,间隔即热部署轮询周期(约 10 秒)。验证服务就绪后即可进入调用环节。
四、SOAP 调用验证
4.1 curl 直接发送 SOAP 信封
用 curl 构造 SOAP 1.1 请求调用 sayHello(namespace 与 operation 元素名来自服务的 WSDL):
curl -s -H "Content-Type: text/xml" -H "SOAPAction: urn:sayHello" -d '<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><sayHello xmlns="http://ws.apache.org/axis2"><name>Alice</name></sayHello></soapenv:Body></soapenv:Envelope>' http://localhost:8080/axis2/services/HelloService
回显(实测):
<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header/><soapenv:Body><ns:sayHelloResponse xmlns:ns="http://ws.apache.org/axis2"><ns:return>Hello, Alice! (from Axis2 2.0.1)</ns:return></ns:sayHelloResponse></soapenv:Body></soapenv:Envelope>
echo 操作同理:
curl -s -H "Content-Type: text/xml" -H "SOAPAction: urn:echo" -d '<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><echo xmlns="http://ws.apache.org/axis2"><msg>axis2-test-2026</msg></echo></soapenv:Body></soapenv:Envelope>' http://localhost:8080/axis2/services/HelloService
回显(实测):
<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header/><soapenv:Body><ns:echoResponse xmlns:ns="http://ws.apache.org/axis2"><ns:return>echo: axis2-test-2026</ns:return></ns:echoResponse></soapenv:Body></soapenv:Envelope>
回显里的 <ns:return> 即方法返回值,xmlns:ns="http://ws.apache.org/axis2" 是默认 targetNamespace。
4.2 REST GET 形态
Axis2 支持 HTTP GET 直调(参数放查询串):
curl -s "http://localhost:8080/axis2/services/HelloService/sayHello?name=Alice"
回显(实测):
<ns:sayHelloResponse xmlns:ns="http://ws.apache.org/axis2"><ns:return>Hello, Alice! (from Axis2 2.0.1)</ns:return></ns:sayHelloResponse>
该形态的参数绑定依赖形参名(元素名 <name> 与方法形参一致),因此同样要求 3.1 节的 -g 编译;查询串参数名与元素名不一致时参数绑定为 null。生产集成建议统一走 4.1 节的标准 SOAP,GET 形态适合快速冒烟。
五、零 Maven 直跑客户端
不引入构建工具,用 wsdl2java 生成 stub 后以 javac/java 直接编译运行——这也是理解 stub 依赖构成的最小方式。
5.1 生成 stub
rm -rf /opt/lab/hello-client
/opt/axis2/bin/wsdl2java.sh -uri http://localhost:8080/axis2/services/HelloService?wsdl -p demo.client -o /opt/lab/hello-client
回显(实测,省略日志噪音行):
Retrieving document at 'http://localhost:8080/axis2/services/HelloService?wsdl'.
生成的文件与结构见《Apache Axis2 常用 CLI 命令与工具详解》6.2 节(HelloServiceStub.java / HelloServiceCallbackHandler.java / build.xml)。stub 的方法名、请求类名与 setter 都由 WSDL 推导——这是 3.1 节强调 -g 的原因。
5.2 编写调用主类
mkdir -p /opt/lab/hello-client/src/demo/client
cat > /opt/lab/hello-client/src/demo/client/HelloClient.java <<'EOF'
package demo.client;
public class HelloClient {
public static void main(String[] args) throws Exception {
HelloServiceStub stub = new HelloServiceStub(
"http://localhost:8080/axis2/services/HelloService");
HelloServiceStub.SayHello req = new HelloServiceStub.SayHello();
req.setName("Alice");
System.out.println("sayHello => " + stub.sayHello(req).get_return());
HelloServiceStub.Echo req2 = new HelloServiceStub.Echo();
req2.setMsg("axis2-maven-client-2026");
System.out.println("echo => " + stub.echo(req2).get_return());
}
}
EOF
5.3 编译与运行
cd /opt/lab/hello-client && mkdir -p classes && javac -cp "/opt/axis2/lib/*" -d classes src/demo/client/*.java && java -cp "classes:/opt/axis2/lib/*" demo.client.HelloClient
回显(实测):
Note: src/demo/client/HelloServiceStub.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
SLF4J(W): No SLF4J providers were found.
SLF4J(W): Defaulting to no-operation (NOP) logger implementation
SLF4J(W): See https://www.slf4j.org/codes.html#noProviders for further details.
sayHello => Hello, Alice! (from Axis2 2.0.1)
echo => echo: axis2-maven-client-2026
结果与 4.1 节 curl 完全一致。Note 两行是 stub 内部集合用法的编译提示,SLF4J(W) 三行是未接入日志实现时的提示,均不影响调用。若 javac 报 class file has wrong version 61.0, should be 52.0,说明当前 javac 是 JDK 8(Axis2 2.0.1 的 jar 是 Java 17 字节码),用 export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 && export PATH=$JAVA_HOME/bin:$PATH 切换后重试(Ubuntu 上 java 与 javac 的 alternatives 指向可能不同步,实测踩过)。
六、Maven 客户端集成
6.1 依赖声明
创建工程并声明 Axis2 客户端最小依赖集(五个构件、版本统一 2.0.1):
mkdir -p /opt/lab/hello-maven/src/main/java/demo/client
cat > /opt/lab/hello-maven/pom.xml <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>demo</groupId>
<artifactId>hello-maven-client</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-kernel</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-adb</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transport-http</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transport-tcp</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transport-local</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
</project>
EOF
五个依赖的分工:kernel 是核心(Options/ServiceClient/stub 基类);adb 是数据绑定(stub 反序列化);三个 transport 分别覆盖 HTTP、TCP 与本地(JVM 内直调,测试用)。实测缺 transport-local 时启动报 LocalTransportSender ClassNotFound——stub 初始化会装配全部已声明传输的发送器。
6.2 客户端主类
把 5.2 节的 HelloClient.java 复制进 Maven 源码目录(stub 源码同样需要):
cp /opt/lab/hello-client/src/demo/client/HelloClient.java /opt/lab/hello-maven/src/main/java/demo/client/
cp /opt/lab/hello-client/src/demo/client/HelloServiceStub.java /opt/lab/hello-maven/src/main/java/demo/client/
cp /opt/lab/hello-client/src/demo/client/HelloServiceCallbackHandler.java /opt/lab/hello-maven/src/main/java/demo/client/
6.3 编译运行
cd /opt/lab/hello-maven && mvn -q compile exec:java -Dexec.mainClass=demo.client.HelloClient 2>&1 | grep -v "^\["
回显(实测):
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
sayHello => Hello, Alice! (from Axis2 2.0.1)
echo => echo: axis2-maven-client-2026
与 5.3 节直跑结果一致,验证两种构建方式等价。SLF4J 三行提示源于 classpath 无日志实现,加入 slf4j-simple 之类绑定即可消除,不影响功能。
七、TCP 传输客户端
Axis2 除 HTTP 外原生支持 TCP 传输(SOAP 报文直接走 TCP 流,无 HTTP 头开销)。本节把调用从 8080/HTTP 切到 6060/TCP。
7.1 服务端开启 TCP 监听
编辑 /opt/axis2/conf/axis2.xml,在 HTTP transportReceiver 之后加入 TCP 接收器(参数名是 transport.tcp.port,不是 port——实测用错参数名时 6060 根本不监听):
<transportReceiver name="tcp"
class="org.apache.axis2.transport.tcp.TCPTransportListener">
<parameter name="transport.tcp.port">6060</parameter>
</transportReceiver>
TCP 发送器 Axis2 已内置,确认存在即可:
grep -n 'transportSender name="tcp"' /opt/axis2/conf/axis2.xml
回显(实测):
<transportSender name="tcp"
修改配置后重启进程生效(systemd 托管环境 systemctl restart axis2),确认端口监听与启动日志:
journalctl -u axis2 --no-pager --since "1 minute ago" | grep "TCP server"
回显(实测):
09:59:52.071 [main] INFO org.apache.axis2.transport.tcp.TCPServer - TCP server started on port : 6060
同时确认 3.2 节 services.xml 的 <transports> 已声明 tcp(未声明的服务对 TCP 传输不可用,实测会打 WARN 且调用失败)。
7.2 准备客户端仓库
客户端需要一个独立 ConfigurationContext 仓库:放 addressing 模块、复制服务端 axis2.xml 后删掉全部 transportReceiver 段(客户端只发送不监听):
mkdir -p /opt/lab/client-repo/conf /opt/lab/client-repo/modules
cp /opt/axis2/repository/modules/addressing-2.0.1.mar /opt/lab/client-repo/modules/
cp /opt/axis2/conf/axis2.xml /opt/lab/client-repo/conf/axis2.xml
用编辑器删除 /opt/lab/client-repo/conf/axis2.xml 中所有 <transportReceiver ...>...</transportReceiver> 段(HTTP 与 TCP 两处),transportSender 段全部保留。最终结构:
find /opt/lab/client-repo -type f
回显(实测):
/opt/lab/client-repo/conf/axis2.xml
/opt/lab/client-repo/modules/addressing-2.0.1.mar
7.3 TCP 客户端代码
stub 复用 5.1 节生成物,差异只在三点:EPR 协议改为 tcp://、指定传输协议为 TCP、把 SOAP 版本显式设为 1.1:
cat > /opt/lab/hello-maven/src/main/java/demo/client/TcpHelloClient.java <<'EOF'
package demo.client;
import org.apache.axis2.Constants;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.ConfigurationContextFactory;
import org.apache.axiom.soap.SOAP11Constants;
public class TcpHelloClient {
public static void main(String[] args) throws Exception {
ConfigurationContext cc = ConfigurationContextFactory.createConfigurationContextFromFileSystem(
"/opt/lab/client-repo", "/opt/lab/client-repo/conf/axis2.xml");
HelloServiceStub stub = new HelloServiceStub(cc,
"tcp://localhost:6060/axis2/services/HelloService");
Options options = stub._getServiceClient().getOptions();
options.setTransportInProtocol(Constants.TRANSPORT_TCP);
options.setSoapVersionURI(SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI);
options.setAction("urn:sayHello");
HelloServiceStub.SayHello req = new HelloServiceStub.SayHello();
req.setName("Bob");
System.out.println("tcp sayHello => " + stub.sayHello(req).get_return());
}
}
EOF
7.4 运行
cd /opt/lab/hello-maven && mvn -q compile exec:java -Dexec.mainClass=demo.client.TcpHelloClient 2>&1 | grep -v "^\["
回显(实测):
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
tcp sayHello => Hello, Bob! (from Axis2 2.0.1)
TCP 链路调用成功。其中 SOAP 版本一行不能省:TCP 传输默认按 SOAP 1.1(contentType text/xml)解析入站报文,而 stub 默认发送 SOAP 1.2,两端不匹配时服务端报 Transport level information does not match with SOAP Message namespace URI(实测复现过);setSoapVersionURI(SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI) 把请求对齐到 1.1 后链路打通。若确需 SOAP 1.2 走 TCP,服务端 axis2.xml 的 TCP 接收器还需加 <parameter name="transport.tcp.contentType">application/soap+xml</parameter>(实测仅改客户端 contentType 无法通过)。
八、注意事项
- 服务类必须
javac -g编译:缺-g时 WSDL 参数退化为 args0,stub setter 变setArgs0,4.2 节 GET 形态参数也无法绑定(两种行为均实测确认)。 - 修改服务类重打包后:新增 .aar 热部署生效(约 10 秒轮询,Timer 线程日志);覆盖同名 .aar 不生效(hotupdate 默认 false),须重启进程。
- 更新服务后要重新执行 wsdl2java 生成 stub——stub 是按旧 WSDL 推导的静态代码,服务签名变了 stub 不同步。
- 编译客户端时 javac 版本必须 ≥17(Axis2 2.0.1 为 61.0 字节码);Ubuntu 下 java 与 javac 的 alternatives 指向可能不同步,报
wrong version 61.0, should be 52.0时先检查javac -version。 - TCP 接收器参数名是
transport.tcp.port;裸port参数实测不生效(6060 不监听)。services.xml 需<transports>声明 tcp,否则服务对 TCP 不可用。 - TCP 调用必须
setSoapVersionURI对齐 SOAP 1.1,否则报 "Transport level information does not match with SOAP Message namespace URI"。 - Maven 客户端五个依赖缺一不可,缺 transport-local 时启动即报
LocalTransportSenderClassNotFound。 - 裸 GET 服务 URL(如
/axis2/services/HelloService不带操作)返回 500 与 "Operation not found" fault 是正常行为,不代表服务故障;判活用 Version 服务或带操作的调用。
九、参考链接
- Apache Axis2 官方主页(用户指南):https://axis.apache.org/axis2/java/core/
- Axis2 快速入门(官方 Quick Start):https://axis.apache.org/axis2/java/core/docs/quickstartguide.html
- pojo 服务与 SOAP 相关配置说明(官方用户指南服务章节):https://axis.apache.org/axis2/java/core/docs/userguide.html
- TCP 传输模块说明(Axis2 传输栈):https://axis.apache.org/axis2/java/core/docs/transport_howto.html
- Apache Axis2 版本下载目录(本篇实测版本 2.0.1):https://dlcdn.apache.org/axis/axis2/java/core/