我们将从最核心、最主流的两种技术栈入手:

- JAX-WS (Java API for XML Web Services):Java 官方标准,用于创建基于 SOAP (Simple Object Access Protocol) 协议的 WebService,这是目前企业级应用中最常用的技术。
- JAX-RS (Java API for RESTful Web Services):Java 官方标准,用于创建基于 REST (Representational State Transfer) 架构风格的 WebService,现在非常流行,尤其适用于移动端和前后端分离的项目。
核心概念:SOAP vs. REST
在开始之前,理解这两种风格的区别非常重要:
| 特性 | SOAP (Simple Object Access Protocol) | REST (Representational State Transfer) |
|---|---|---|
| 协议 | 严格基于 XML,必须通过 SOAP 协议在 HTTP 上传输。 | 基于 HTTP 协议本身,可以使用 JSON、XML、Text 等多种数据格式。 |
| 标准 | 标准严格,有 WSDL (Web Services Description Language) 文件来定义服务接口。 | 无严格标准,更偏向一种架构风格,依赖 URI 来标识资源。 |
| 数据格式 | 主要是 XML。 | 主要是 JSON,也支持 XML、HTML 等。 |
| 使用场景 | 企业级应用、金融、电信等对安全性、事务性要求高的场景。 | 公开 API、移动应用、前后端分离项目。 |
| 复杂度 | 较高,配置和解析 XML 较为繁琐。 | 较低,更轻量、更易于理解和使用。 |
JAX-WS 客户端 (针对 SOAP WebService)
JAX-WS 是 Java 平台的标准 API,我们通常不需要直接引入第三方库,因为它们已经包含在 JDK 和 Java EE 中。
创建客户端的几种方式
使用 wsimport 工具生成客户端代码(最经典、最常用)
这是最传统和可靠的方法。wsimport 是 JDK 自带的一个命令行工具,它会根据 WSDL 文件自动生成一系列 Java 类(客户端存根/骨架),这些类封装了与服务器通信的细节。

步骤:
-
获取 WSDL 文件 任何一个 SOAP WebService 都会提供一个 WSDL 文件,它描述了服务的所有接口、方法、参数和返回值,通常是一个 URL,
http://www.example.com/service?wsdl。 -
运行
wsimport命令 打开命令行(CMD 或 PowerShell),执行以下命令:# 基本语法 wsimport -p <生成的包名> -keep <wsdl文件的URL或路径> # 示例 # 假设 WSDL 地址是:http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl # 我们想把生成的代码放在 com.example.weather 包下,并保留生成的源文件 wsimport -p com.example.weather -keep http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl
参数解释:
-p: 指定生成的 Java 类的包名。-keep: 生成.java源文件,方便查看和调试。-d: 指定编译后的.class文件存放目录。-s: 指定.java源文件存放目录(如果与-d不同)。<wsdl_url>: WSDL 文件的地址。
-
使用生成的代码 执行命令后,会在你指定的包下生成一堆类,其中最重要的是:
WeatherSoap(或类似名字):这是服务的接口,包含了 WSDL 中定义的所有方法。WeatherSoapService(或类似名字):这是服务的工厂类,用于获取WeatherSoap接口的实例。
Java 代码示例:
import com.example.weather.Weather; import com.example.weather.WeatherSoap; import com.example.weather.WeatherSoapService; public class JaxWsClientExample { public static void main(String[] args) { try { // 1. 创建服务工厂实例 WeatherSoapService weatherService = new WeatherSoapService(); // 2. 从工厂获取服务接口的代理对象 WeatherSoap weatherSoap = weatherService.getWeatherSoap(); // 3. 调用接口方法,就像调用本地方法一样 // 假设 WSDL 中定义了一个 GetCityWeatherByZIP 方法 String zipCode = "10001"; Weather weatherInfo = weatherSoap.GetCityWeatherByZIP(zipCode); // 4. 处理返回结果 if (weatherInfo != null) { System.out.println("城市: " + weatherInfo.getCity()); System.out.println("温度: " + weatherInfo.getTemperature()); System.out.println("天气状况: " + weatherInfo.getConditions()); } else { System.out.println("未能获取天气信息。"); } } catch (Exception e) { e.printStackTrace(); } } }
使用动态调用(无需生成代码)
如果你不想生成一堆静态代码,或者 WSDL 地址是动态的,可以使用 Dispatch API 或 Provider API 进行动态调用。
示例代码 (使用 Dispatch API):
import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;
import java.net.URL;
public class DynamicJaxWsClient {
public static void main(String[] args) throws Exception {
// 1. 定义 WSDL 地址和目标命名空间
String wsdlUrl = "http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl";
String namespaceUri = "http://ws.cdyne.com/WeatherWS/";
String serviceName = "Weather";
String portName = "WeatherSoap";
// 2. 创建 Service 实例
URL url = new URL(wsdlUrl);
QName qname = new QName(namespaceUri, serviceName);
Service service = Service.create(url, qname);
// 3. 创建 Dispatch 实例
// Dispatch 可以是 PAYLOAD 或 MESSAGE 级别
Dispatch<SOAPMessage> dispatch = service.createDispatch(
new QName(namespaceUri, portName),
SOAPMessage.class,
Service.Mode.PAYLOAD);
// 4. 创建 SOAP 请求消息
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage request = mf.createMessage();
SOAPEnvelope envelope = request.getSOAPPart().getEnvelope();
SOAPBody body = envelope.getBody();
// 创建请求体内容 (根据具体服务的 SOAP 格式)
// 这里以 GetCityWeatherByZIP 为例
QName bodyName = new QName(namespaceUri, "GetCityWeatherByZIP");
body.addBodyElement(bodyName).addAttribute(new QName("ZIP"), "10001");
// 5. 发送请求并获取响应
System.out.println("发送请求...");
SOAPMessage response = dispatch.invoke(request);
// 6. 处理响应
System.out.println("收到响应:");
response.writeTo(System.out);
}
}
JAX-RS 客户端 (针对 REST WebService)
JAX-RS 本身是服务端规范,但其客户端 API 非常强大,最常用的实现是 Jersey 和 Apache CXF。
准备工作 (以 Jersey 为例)
你需要添加 Jersey 客户端的依赖,如果你使用 Maven,在 pom.xml 中添加:
<dependencies>
<!-- Jersey 核心客户端依赖 -->
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.39.1</version> <!-- 使用最新版本 -->
</dependency>
<!-- 支持 JSON 格式 -->
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>2.39.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-binding</artifactId>
<version>2.39.1</version>
</dependency>
</dependencies>
创建和使用客户端
JAX-RS 客户端的核心是 Client 和 WebTarget 对象。
步骤:
- 创建
Client实例:客户端的入口。 - 创建
WebTarget实例:指定目标资源的 URI。 - 构建请求:通过
WebTarget的方法(如get(),post(),put(),delete())来指定 HTTP 方法和可选的实体(请求体)。 - 发送请求并获取响应:调用
invoke()或response()方法。 - 处理响应:从响应对象中获取状态码、头部和实体(响应体)。
示例代码 (调用一个获取用户信息的 REST API):
假设有一个 API GET http://example.com/api/users/1,返回 JSON 格式的用户信息:{"id": 1, "name": "John Doe", "email": "john.doe@example.com"}。
定义与 JSON 对应的 Java 实体类 (POJO):
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement // 用于 Jersey 的 JSON 映射
public class User {
private int id;
private String name;
private String email;
// 必须有无参构造函数
public User() {}
// Getters and Setters
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
'}';
}
}
编写客户端代码:
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
public class JaxRsClientExample {
public static void main(String[] args) {
// API 的基础 URL
String apiBaseUri = "http://example.com/api";
// 1. 创建 Client 实例
Client client = ClientBuilder.newClient();
try {
// 2. 创建 WebTarget,指向特定的资源
WebTarget target = client.target(apiBaseUri).path("users").path("1");
// 3. 发送 GET 请求,并期望返回 JSON 格式的数据
// 并将其直接映射为 User 对象
User user = target
.request(MediaType.APPLICATION_JSON) // 设置 Accept 头
.get(User.class); // 直接获取并映射为 User 对象
// 4. 处理结果
System.out.println("成功获取用户信息:");
System.out.println(user);
// --- 另一种方式:使用 Response 对象获取更多信息 ---
System.out.println("\n使用 Response 对象获取信息:");
Response response = target.request().get();
if (response.getStatus() == 200) {
User userFromResponse = response.readEntity(User.class);
System.out.println("从 Response 中获取的用户: " + userFromResponse);
} else {
System.out.println("请求失败,状态码: " + response.getStatus());
}
// 关闭响应,释放资源
response.close();
} finally {
// 5. 关闭 Client,释放资源
client.close();
}
}
}
实用工具与框架
除了标准的 JAX-WS/JAX-RS,还有一些强大的第三方库可以简化开发:
-
Spring WebFlux
WebClient(推荐)-
简介:Spring 5 引入的响应式、非阻塞式 HTTP 客户端,是 Spring Boot 3.x 中进行 HTTP 调用的首选,它非常强大、灵活,并且与 Spring 生态系统无缝集成。
-
优点:链式调用、支持异步/响应式、类型安全、易于测试。
-
示例:
// 在 Spring Boot 应用中 @RestController public class MyController { private final WebClient webClient; public MyController(WebClient.Builder webClientBuilder) { this.webClient = webClientBuilder.baseUrl("http://example.com/api").build(); } @GetMapping("/fetch-user") public Mono<User> fetchUser() { return this.webClient.get() .uri("/users/1") .retrieve() // 发出请求并获取响应 .bodyToMono(User.class); // 将响应体映射为 Mono<User> } }
-
-
Apache HttpClient
- 简介:一个非常成熟和强大的底层 HTTP 客户端库,它不特定于 WebService,可以用于任何 HTTP 通信。
- 优点:高度可配置、性能优秀、功能全面。
- 缺点:API 相对繁琐,需要手动处理序列化/反序列化(如使用 Jackson/Gson)。
总结与如何选择
| 场景 | 推荐技术 | 理由 |
|---|---|---|
| 企业内部系统,需要强事务、高安全性的 SOAP 服务 | JAX-WS (wsimport) |
标准稳定,与 Java EE/Jakarta EE 深度集成。 |
| 调用公开的 REST API,返回 JSON | JAX-RS (Jersey/CXF) 或 Spring WebClient |
JAX-RS 是标准,WebClient 是 Spring 生态下的现代、更强大的选择。 |
| 需要最大灵活性和对底层 HTTP 协议的完全控制 | Apache HttpClient | 提供了最底层的访问,可以定制任何细节。 |
| 在 Spring Boot 项目中进行 HTTP 调用 | Spring WebClient |
与项目技术栈完美融合,代码更简洁,支持异步编程。 |
对于初学者,建议:
- 如果是 SOAP,直接掌握
wsimport的使用流程。 - 如果是 REST,优先学习 Spring
WebClient,如果项目不基于 Spring,再学习 JAX-RS (Jersey)。
