当前位置:首页 > 技术 > 正文内容

使用WSDL生成Java WebService客户端及SoapUI测试与字符编码问题解析

访客 技术 2026年7月16日 2

概述

本文介绍如何基于WSDL文件生成Java WebService客户端代码,利用SoapUI进行可视化接口测试,并深入分析调用过程中常见的中文乱码问题及其解决方案。

环境准备与工具说明

在Java开发中,JDK自带的wsimportwsgen是处理WebService的重要命令行工具:

  • wsimport:根据WSDL描述文件生成对应的Java客户端类。
  • wsgen:从服务实现类反向生成WSDL及相关绑定文件。

这两个工具位于%JAVA_HOME%/bin目录下,无需额外依赖即可使用。

通过WSDL生成Java客户端代码

执行以下命令可自动生成客户端代码:

wsimport -encoding utf8 -p com.example.client -wsdllocation http://example.com/service.wsdl http://example.com/service.wsdl
  • -encoding utf8:指定生成的Java源文件编码格式为UTF-8。
  • -p com.example.client:设置生成类的包名。
  • -wsdllocation:指定运行时加载的WSDL地址(用于动态定位)。
  • 最后一个参数为本地或远程WSDL文件路径。

关键生成类解析

生成的代码通常包含三类核心元素:

1. 服务入口类(@WebServiceClient)

该类继承javax.xml.ws.Service,对应WSDL中的<service>节点。例如:

@WebServiceClient(name = "PatientService", targetNamespace = "http://healthcare.system", wsdlLocation = "http://his.example.com/ws/PatientService.wsdl")
public class PatientService extends Service {

    private final static URL WSDL_LOCATION;

    static {
        URL url = null;
        try {
            url = new URL("http://his.example.com/ws/PatientService.wsdl");
        } catch (MalformedURLException e) {
            throw new WebServiceException(e);
        }
        WSDL_LOCATION = url;
    }

    public PatientService() {
        super(WSDL_LOCATION, new QName("http://healthcare.system", "PatientService"));
    }

    public PatientPortType getPatientPort() {
        return super.getPort(new QName("http://healthcare.system", "PatientPort"), PatientPortType.class);
    }
}

2. 接口定义类(@WebService)

此接口对应WSDL中的<portType>,声明了可用的操作方法:

@WebService(name = "PatientPortType", targetNamespace = "http://healthcare.system")
@XmlSeeAlso({ObjectFactory.class})
public interface PatientPortType {

    @WebMethod(operationName = "getPatientInfo")
    @WebResult(name = "patientData")
    String getPatientInfo(
        @WebParam(name = "patientId") String patientId,
        @WebParam(name = "hospitalCode") String hospitalCode
    );
}

3. JAXB对象工厂(ObjectFactory)

用于XML与Java对象之间的序列化支持:

@XmlRegistry
public class ObjectFactory {}

客户端调用示例

使用生成的类发起请求非常直观:

PatientService service = new PatientService();
PatientPortType port = service.getPatientPort();
String result = port.getPatientInfo("P1001", "HOS_007");
System.out.println(result);

使用SoapUI进行可视化测试

SoapUI是一款功能强大的WebService测试工具,尤其适合调试复杂的SOAP服务。

创建SOAP项目

  1. 打开SoapUI开源版(Free Edition)。
  2. 选择"File" → "New SOAP Project"。
  3. 输入WSDL地址,如:
    http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?wsdl
  4. 点击确定后,SoapUI会自动解析所有可用操作。

查看服务结构

以天气服务为例,其WSDL中包含多个端口:

<wsdl:service name="WeatherWebService">
  <wsdl:port name="WeatherWebServiceSoap" binding="tns:WeatherWebServiceSoap">
    <soap:address location="http://www.webxml.com.cn/WebServices/WeatherWebService.asmx"/>
  </wsdl:port>
  <wsdl:port name="WeatherWebServiceSoap12" binding="tns:WeatherWebServiceSoap12">
    <soap12:address location="http://www.webxml.com.cn/WebServices/WeatherWebService.asmx"/>
  </wsdl:port>
</wsdl:service>

其中,SOAP 1.1 和 1.2 分别对应不同的协议版本。

发送请求并查看响应

  • 双击某个操作(如getSupportCity),进入请求编辑界面。
  • 填写输入参数,点击绿色运行按钮执行。
  • 右侧面板显示完整的HTTP通信内容。

底层通信原理

尽管WebService基于SOAP协议,但底层仍使用HTTP传输。请求体遵循标准SOAP封装:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
                 xmlns:web="http://WebXml.com.cn/">
   <soapenv:Header/>
   <soapenv:Body>
      <web:getSupportCity>
         <web:byProvinceName>广东</web:byProvinceName>
      </web:getSupportCity>
   </soapenv:Body>
</soapenv:Envelope>

响应同样封装在Envelope中:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <getSupportCityResponse xmlns="http://WebXml.com.cn/">
      <getSupportCityResult>
        <string>广州</string>
        <string>深圳</string>
      </getSupportCityResult>
    </getSupportCityResponse>
  </soap:Body>
</soap:Envelope>

解决中文乱码问题

在调用由ASP.NET提供的WebService时,常出现中文乱码而英文数字正常的现象。根本原因在于:

  • 服务器返回的实际字节流为UTF-8编码。
  • JVM默认使用平台编码(如ISO-8859-1)解析字节流,导致汉字被错误解码为乱码字符。

乱码复现与分析

模拟错误解码过程:

byte[] utf8Bytes = "你好啊".getBytes(StandardCharsets.UTF_8);
String wrongStr = new String(utf8Bytes, StandardCharsets.ISO_8859_1); // 错误解码
System.out.println(wrongStr); // 输出类似 "浣犲ソ鍟"

// 恢复原始字节并正确解码
byte[] recovered = wrongStr.getBytes(StandardCharsets.ISO_8859_1);
String correctStr = new String(recovered, StandardCharsets.UTF_8);
System.out.println(correctStr); // 正确输出 "你好啊"

通用修复方法

封装一个工具函数用于纠正编码:

/**
 * 将因错误解码产生的乱码字符串还原为原始UTF-8文本
 * @param corruptedStr 已经乱码的字符串
 * @return 正确解码后的字符串
 */
public static String fixEncoding(String corruptedStr) {
    if (corruptedStr == null) return null;
    byte[] bytes = corruptedStr.getBytes(StandardCharsets.ISO_8859_1);
    return new String(bytes, StandardCharsets.UTF_8);
}

深层原因探究

当使用GBK尝试解码UTF-8字节流时,由于编码单位不一致(UTF-8三字节表示一个汉字,GBK两字节),部分无法识别的字节会被替换为?(ASCII码3F)。一旦字节流被修改,即使后续重新编码也无法恢复原貌。

示例:

"一二三" UTF-8编码:E4 B8 80 E4 BA 8C E4 B8 89
误用GBK解码后:涓浜屼笁 (中间字节变为3F)
再转回UTF-8:?二三 —— 数据已永久损坏

总结

通过wsimport生成客户端是集成传统WebService系统的高效方式,避免手动构建SOAP消息的复杂性。配合SoapUI可以快速验证接口行为。对于跨语言系统集成中的编码问题,需明确传输层的真实编码格式,并在必要时进行字节级修复。建议在系统对接初期即确认双方的字符编码规范,防止后期数据污染。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。