杰瑞科技汇

wsdl2java如何生成客户端代码?

什么是 wsdl2java

wsdl2javaApache CXFApache Axis2 等开源 Web Service 框架提供的一个命令行工具,它的主要功能是:

wsdl2java如何生成客户端代码?-图1
(图片来源网络,侵删)
  • 解析 WSDL 文件:读取 WSDL 文件中对 Web Service 的所有描述。
  • 生成 Java 代码:根据 WSDL 中的定义,自动创建一系列 Java 类,这些类代表了:
    • 服务接口 (Service Interface):客户端可以直接调用的方法,对应 WSDL 中的 portType
    • 服务实现类 (Service Implementation):一个客户端代理,负责将你的方法调用转换为 SOAP 消息。
    • 数据模型 (Data Models):与 WSDL 中定义的 complexTypeelement 对应的 Java Bean (POJO) 类,用于封装请求和响应数据。
    • 服务工厂 (Service Factory):一个用于获取服务接口实例的工厂类。
    • 异常类:可能抛出的 SOAPFault 等异常。

通过使用这些生成的代码,你就可以像调用本地 Java 方法一样,轻松地调用远程的 Web Service,而无需关心底层的 SOAP 消息格式、网络传输等复杂细节。


准备工作:安装 Apache CXF

wsdl2java 是 CXF 框架的一部分,你需要先下载并配置 CXF。

步骤 1:下载 CXF 访问 Apache CXF 官方下载页面,下载最新的二进制分发版(apache-cxf-<version>-bin.zip)。

步骤 2:解压并设置环境变量

wsdl2java如何生成客户端代码?-图2
(图片来源网络,侵删)
  1. 将下载的 ZIP 文件解压到一个你喜欢的目录,D:\dev\apache-cxf-3.4.3
  2. 重要:将 bin 目录添加到系统的 PATH 环境变量中,添加 D:\dev\apache-cxf-3.4.3\bin
    • 这样你就可以在任何地方直接使用 wsdl2java 命令了。

步骤 3:验证安装 打开一个新的命令行窗口(CMD 或 PowerShell),输入以下命令:

wsdl2java -version

如果能看到 CXF 的版本信息,说明安装和配置成功。


使用 wsdl2java 生成代码

基本语法

wsdl2java [options] <WSDL-File-URL-or-Path>

最简单的用法

如果你的 WSDL 文件非常简单,可以直接使用:

wsdl2http http://www.example.com/service?wsdl

这会在当前目录下生成一个 src 文件夹,里面包含所有代码。

wsdl2java如何生成客户端代码?-图3
(图片来源网络,侵删)

常用选项和最佳实践

在实际项目中,通常需要使用一些选项来控制代码的生成,使其更符合项目结构。

选项 说明 示例
-p--package 指定生成的 Java 包名,强烈推荐使用,避免生成到默认的 org.tempuri 等无意义的包中。 -p com.example.client
-d--destDir 指定生成的代码存放的根目录,通常是你项目的 src/main/java 目录。 -d ./src/main/java
-client 生成一个可以独立运行的客户端 main 方法,方便快速测试。 -client
-server 生成一个服务端骨架代码(如果你要实现服务端)。 -server
-impl 生成服务端实现类。 -impl
-all 生成客户端和服务端的所有代码。 -all
-ant 生成一个 Ant 构建文件。 -ant
-autoNameResolution 自动解决命名冲突问题(WSDL 中有同名但不同命名空间的元素)。 -autoNameResolution
-frontend 指定前端语言,默认是 jaxws(推荐)。jaxb 是旧版本。 -frontend jaxws
-exsh 禁用生成 SOAP 头。 -exsh true

推荐的完整命令示例

假设你的项目结构如下:

my-web-service-client/
├── pom.xml
└── src/
    └── main/
        └── java/

你的 WSDL 文件地址是 http://api.example.com/WeatherService?wsdl

你可以在项目根目录 my-web-service-client/ 下执行以下命令:

wsdl2java \
  -p com.example.weather.client \
  -d ./src/main/java \
  -client \
  -autoNameResolution \
  http://api.example.com/WeatherService?wsdl

命令解释

  • -p com.example.weather.client:所有生成的代码都将放在 com.example.weather.client 包及其子包下。
  • -d ./src/main/java:生成的 .java 文件会直接放到 src/main/java 目录中,符合 Maven/Gradle 的标准目录结构。
  • -client:额外生成一个可运行的客户端测试类。
  • -autoNameResolution:作为保险,防止因 WSDL 复杂导致命名冲突。
  • http://api.example.com/WeatherService?wsdl:WSDL 文件的 URL。

生成的代码结构与使用

执行完上述命令后,你的 src/main/java 目录会变成类似这样:

src/main/java/
└── com/
    └── example/
        └── weather/
            └── client/
                ├── WeatherService.java          // 服务接口 (portType)
                ├── WeatherServiceService.java   // 服务工厂 (Service)
                ├── Weather.java                 // 数据模型 (请求/响应对象)
                ├── GetWeatherRequest.java       // 数据模型 (请求对象)
                ├── GetWeatherResponse.java      // 数据模型 (响应对象)
                └── WeatherServiceClient.java    // 可运行的客户端测试类 (由 -client 生成)

如何在 Java 代码中使用生成的客户端?

  1. 获取服务工厂实例WeatherServiceService 类有一个 getWeatherServicePort() 方法,它会返回 WeatherService 接口的实例。

  2. 调用方法: 拿到 WeatherService 实例后,就可以像调用普通 Java 方法一样调用它的方法了。

示例代码:

package com.example.weather.client;
public class MainApp {
    public static void main(String[] args) {
        // 1. 创建服务工厂实例
        WeatherServiceService service = new WeatherServiceService();
        // 2. 获取服务接口的代理对象
        WeatherService port = service.getWeatherServicePort();
        try {
            // 3. 创建请求数据对象
            GetWeatherRequest request = new GetWeatherRequest();
            request.setCity("Beijing");
            // 4. 调用 Web Service 方法
            GetWeatherResponse response = port.getWeather(request);
            // 5. 处理响应结果
            System.out.println("The temperature in " + request.getCity() + " is: " + response.getTemperature() + "°C");
        } catch (Exception e) {
            // 捕获可能的 SOAPFault 或其他异常
            e.printStackTrace();
            System.err.println("调用 Web Service 出错: " + e.getMessage());
        }
    }
}

常见问题与解决方案

Q1: wsdl2java 报错:java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

原因:从 Java 9 开始,JAXB (Java Architecture for XML Binding) 不再包含在标准的 JDK 中,而是作为一个可选模块,CXF 依赖它来处理 XML 和 Java 对象的转换。

解决方案: 如果你使用的是 Java 9 或更高版本,需要添加 JAXB 的依赖到你的项目中(以 Maven 为例):

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.1</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>2.3.3</version>
</dependency>

Q2: 生成的代码包名不正确,或者有重复的类名

原因:WSDL 文件中可能定义了多个 targetNamespace,或者存在命名冲突。

解决方案

  • 使用 -autoNameResolution 选项,让 CXF 自动处理。
  • 使用 -p 选项明确指定包名,并使用 或 分隔不同的命名空间映射。
    wsdl2java -p "http://tempuri.org/=com.example.client;http://schemas.datacontract.org/.../=com.example.client.model" ...

    这种方式可以更精细地控制不同命名空间下的类生成到哪个包。

Q3: WSDL 文件需要用户名和密码认证才能访问

解决方案: CXF 支持通过 HTTPConduit 来设置 HTTP 客户端认证信息,你需要编写一个简单的 Spring 配置文件,并将其与 CXF 的总线关联起来。

示例 Spring 配置文件 (cxf-client.xml):

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jaxws="http://cxf.apache.org/jaxws"
       xmlns:http="http://cxf.apache.org/transports/http/configuration"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://cxf.apache.org/jaxws
       http://cxf.apache.org/schemas/jaxws.xsd
       http://cxf.apache.org/transports/http/configuration
       http://cxf.apache.org/schemas/configuration/http-conf.xsd">
    <!-- 定义 HTTP 客户端配置 -->
    <http:conduit name="{http://api.example.com/}WeatherService.http-conduit">
        <http:client ConnectionTimeout="30000" ReceiveTimeout="30000"/>
        <http:Authorization>
            <http:UserName>your_username</http:UserName>
            <http:Password>your_password</http:Password>
        </http:Authorization>
    </http:conduit>
</beans>

然后在你的 Java 代码中加载这个配置:

import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;
import org.springframework.context.support.ClassPathXmlApplicationContext;
// ... 在获取 port 之前
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"cxf-client.xml"});
SpringBus springBus = (SpringBus) context.getBean("cxf");
// 将你的客户端总线设置为这个 SpringBus
ClientProxy.getClient(port).getBinding().get(Bus.class).setId("cxf"); // 或者其他方式关联
// 更简单的方式是,如果你的应用已经集成了 Spring,CXF 会自动发现配置。

注意:在现代应用中,更推荐使用 CXF 的 JAXRSClientFactoryJAXWSClientFactoryBean 来编程式地设置这些属性,或者将其集成到 Spring/Spring Boot 的配置中。


最佳实践

  1. 版本控制:将 wsdl2java 生成的代码纳入版本控制系统(如 Git),不要将 .java 文件排除在外,这样可以确保团队成员和部署环境使用完全一致的客户端代码。
  2. 清晰的包结构:始终使用 -p 选项指定一个有意义的包名,避免生成到默认包中。
  3. 使用构建工具集成:在 Maven 或 Gradle 项目中,使用 cxf-codegen-plugin 来生成代码,而不是手动运行命令行,这样可以更好地将代码生成过程集成到自动化构建中。
    • Maven Plugin 示例
      <plugin>
          <groupId>org.apache.cxf</groupId>
          <artifactId>cxf-codegen-plugin</artifactId>
          <version>3.4.3</version>
          <executions>
              <execution>
                  <id>generate-sources</id>
                  <phase>generate-sources</phase>
                  <configuration>
                      <sourceRoot>${project.build.directory}/generated-sources/cxf</sourceRoot>
                      <wsdlOptions>
                          <wsdlOption>
                              <wsdl>http://api.example.com/WeatherService?wsdl</wsdl>
                              <wsdlLocation>http://api.example.com/WeatherService?wsdl</wsdlLocation>
                          </wsdlOption>
                      </wsdlOptions>
                  </configuration>
                  <goals>
                      <goal>wsdl2java</goal>
                  </goals>
              </execution>
          </executions>
      </plugin>
  4. 处理异常:始终用 try-catch 块包裹 Web Service 调用,以捕获 SOAPFaultException 或其他运行时异常,这是处理服务端返回的错误信息的标准方式。
分享:
扫描分享到社交APP
上一篇
下一篇