什么是 WebService?
我们用一个简单的比喻来理解。

想象一下,你的应用程序(比如一个电商网站)需要获取天气信息,你不可能自己建一个气象站,而是希望调用一个专门提供天气服务的“专家”来给你数据。
这个“专家”WebService。
核心定义: WebService 是一种跨编程语言、跨操作系统的远程调用技术,它允许不同的应用程序通过网络进行交互,就像调用本地方法一样简单。
关键特点:

- 跨平台/跨语言:WebService 基于 XML、SOAP 等标准,任何语言(Java, Python, C# 等)只要能理解这些标准,就能调用它。
- 基于标准协议:主要使用 HTTP/HTTPS 协议进行通信,可以轻松穿透防火墙。
- 自描述性:服务本身会提供一份描述文档(WSDL),告诉别人它提供了哪些功能、如何调用、需要什么参数、会返回什么数据。
- 松耦合:服务提供方和使用方之间不需要了解对方的内部实现细节,只需要通过 WSDL 进行约定即可。
Java WebService 的发展历程
Java WebService 技术栈经历了几代演进,了解这个历程有助于你选择合适的技术。
早期时代:JAX-RPC (Java API for XML-based RPC)
- 特点:这是 Java 早期推出的 WebService 规范,它试图将远程的 SOAP 调用“伪装”成本地方法调用,简化了开发。
- 缺点:它对 SOAP 和 WSDL 的支持比较底层和繁琐,开发体验不佳,现在已经基本被淘汰。
黄金时代:JAX-WS (Java API for XML Web Services)
- 特点:这是 JAX-RPC 的继任者,也是目前 Java 企业级应用中最主流、最成熟的 WebService 技术,它极大地简化了开发。
- 注解驱动:通过
@WebService,@WebMethod,@WebParam等注解,可以非常方便地将一个普通 Java 类发布成 WebService。 - 自动 WSDL 生成:服务发布后,服务器会自动生成 WSDL 文件。
- 内置客户端:提供了
wsimport工具,可以根据 WSDL 文件自动生成客户端调用代码。
- 注解驱动:通过
- 实现框架:主要由 JAX-WS RI (Reference Implementation, 现在是 Metro 项目) 和 Apache CXF 提供,JAX-WS 是规范,这两个是实现。
RESTful 时代:JAX-RS (Java API for RESTful Web Services)
- 特点:随着移动互联网的兴起,传统的 SOAP WebService 因其复杂、笨重而逐渐被更轻量级的 REST 架构风格取代。
- 简单:基于 HTTP 协议,使用 GET, POST, PUT, DELETE 等方法操作资源。
- 轻量:数据传输格式通常是 JSON 或 XML,比 SOAP 的 XML 包小得多。
- 无状态:服务端不保存客户端状态,易于扩展。
- 实现框架:JAX-RS 是规范,主流实现是 Jersey (来自 Oracle/GlassFish) 和 Apache CXF。
- 地位:在现代 Web 开发中,JAX-RS (RESTful API) 已经成为绝对的主流,当人们现在谈论 Java WebService 时,很多时候指的就是 RESTful Service。
新时代:Spring Web Services vs. Spring Boot
- Spring Web Services:Spring 生态中专注于契约优先 的 SOAP WebService 框架,它强调先定义 WSDL 契约,然后根据契约生成代码,非常适合企业级的、复杂的 SOAP 服务集成。
- Spring Boot:它并没有发明新的 WebService 技术,而是极大地简化了 JAX-WS 和 JAX-RS 的开发,通过自动配置和起步依赖,你只需要几行代码就能快速启动一个 WebService。
主流技术栈与代码示例
下面我们通过两个最主流的例子来展示如何开发 Java WebService。
示例 1:使用 JAX-WS 开发一个 SOAP WebService
目标:发布一个服务,根据用户名查询用户信息。
服务端开发 (Provider)

步骤:
- 创建一个 SEI (Service Endpoint Interface),即服务端接口。
- 创建一个 SIB (Service Implementation Bean),即服务端实现类,并使用
@WebService注解。 - 使用
Endpoint类发布服务。
代码:
接口 User.java
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
// @WebService 定义这是一个 WebService 接口
@WebService
// @SOAPBinding 定义 SOAP 消息风格,默认是 DOCUMENT
@SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
public interface User {
@WebMethod
String getUserInfo(String username);
}
实现 UserServiceImpl.java
import javax.jws.WebService;
// @WebService(endpointInterface = "com.example.webservice.User") 指定实现的接口
@WebService(endpointInterface = "com.example.webservice.User")
public class UserServiceImpl implements User {
@Override
public String getUserInfo(String username) {
// 模拟业务逻辑
if ("admin".equals(username)) {
return "Username: admin, Role: Administrator";
} else if ("user".equals(username)) {
return "Username: user, Role: Normal User";
} else {
return "User not found!";
}
}
}
发布服务 Publisher.java
import javax.xml.ws.Endpoint;
public class Publisher {
public static void main(String[] args) {
// 定义服务的访问地址
String address = "http://localhost:8888/ws/user";
// 创建服务实现类的实例
UserServiceImpl userServiceImpl = new UserServiceImpl();
// 发布服务
Endpoint.publish(address, userServiceImpl);
System.out.println("WebService is published at: " + address);
}
}
运行 Publisher,然后访问 http://localhost:8888/ws/user?wsdl,你就能看到自动生成的 WSDL 文件了。
客户端开发 (Consumer)
步骤:
- 使用
wsimport工具根据 WSDL 生成客户端代码。 - 生成的代码中会有一个
UserService和UserService_Service,通过它们来调用服务。
在命令行执行:
wsimport -keep -p com.example.webservice.client http://localhost:8888/ws/user?wsdl
-keep: 生成源代码。-p: 指定包名。
调用客户端 Client.java
import com.example.webservice.client.GetUserInfo;
import com.example.webservice.client.GetUserInfoResponse;
import com.example.webservice.client.UserService;
import com.example.webservice.client.UserService_Service;
import javax.xml.ws.Service;
import java.net.URL;
public class Client {
public static void main(String[] args) throws Exception {
// WSDL 文件的地址
URL wsdlUrl = new URL("http://localhost:8888/ws/user?wsdl");
// 创建 Service 实例,参数是 WSDL 中的 targetNamespace 和 QName
QName qname = new QName("http://webservice.example.com/", "UserService_Service");
Service service = Service.create(wsdlUrl, qname);
// 获取服务端口
UserService userService = service.getPort(UserService.class);
// 调用远程方法
GetUserInfo request = new GetUserInfo();
request.setUsername("admin");
GetUserInfoResponse response = userService.getUserInfo(request);
System.out.println("Response from server: " + response.getReturn());
}
}
示例 2:使用 JAX-RS (Jersey) 开发一个 RESTful WebService
目标:发布一个 RESTful API,提供 GET /api/users/{id} 接口来获取用户信息。
环境准备:需要 Jersey 的依赖,如果你使用 Maven,pom.xml 配置如下:
<dependencies>
<!-- Jersey Core -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.39</version>
</dependency>
<!-- JSON 支持 -->
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-binding</artifactId>
<version>2.39</version>
</dependency>
</dependencies>
服务端开发
步骤:
- 创建一个 Resource 类,使用
@Path定义路径。 - 使用
@GET,@POST,@PathParam,@QueryParam等注解定义方法和参数。 - 在
web.xml中配置 Jersey Servlet。
代码:
POJO User.java
public class User {
private int id;
private String name;
private String email;
// Getters and Setters
// ...
}
Resource UserResource.java
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path("/api/users") // 定义基础路径
public class UserResource {
// @GET: 处理 HTTP GET 请求
// @Path: 定义子路径
// @PathParam: 从路径中获取参数
// @Produces: 指定返回的媒体类型 (这里是 JSON)
@GET
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response getUserById(@PathParam("id") int id) {
// 模拟从数据库获取用户
User user = findUserById(id);
if (user == null) {
// 404 Not Found
return Response.status(Response.Status.NOT_FOUND).entity("User not found").build();
}
// 200 OK,并返回 JSON 格式的用户对象
return Response.ok(user).build();
}
private User findUserById(int id) {
// 模拟数据
if (id == 1) {
return new User(1, "John Doe", "john.doe@example.com");
}
return null;
}
}
配置 web.xml
<web-app>
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.example.rest</param-value> <!-- 你的 Resource 类所在的包 -->
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/rest/*</url-pattern> <!-- 所有 /rest/* 的请求都由 Jersey 处理 -->
</servlet-mapping>
</web-app>
客户端调用
你可以使用任何 HTTP 客户端(如 Postman, curl, Java 的 HttpClient)来调用。
使用 curl:
# 获取 ID 为 1 的用户
curl -X GET http://localhost:8080/your-app-name/rest/api/users/1
# 输出类似:
# {"id":1,"name":"John Doe","email":"john.doe@example.com"}
# 获取不存在的用户
curl -X GET http://localhost:8080/your-app-name/rest/api/users/2
# 输出类似:
# "User not found"
如何选择?
| 特性 | JAX-WS (SOAP) | JAX-RS (REST) |
|---|---|---|
| 通信协议 | 主要基于 HTTP/SMTP,但协议无关 | 强依赖 HTTP/HTTPS |
| 数据格式 | 严格定义的 XML (SOAP Envelope) | 灵活,通常为 JSON, XML, Text |
| 消息结构 | 强契约,WSDL 定义了消息的方方面面 | 松契约,通常由 API 文档 (如 Swagger) 描述 |
| 安全性 | 内置了强大的安全标准和机制 (WS-Security) | 通常依赖 HTTPS + OAuth/JWT 等标准 |
| 性能 | XML 解析开销大,消息体积大,性能较低 | 轻量级,JSON 解析快,性能高 |
| 适用场景 | 企业级应用集成 (B2B)、银行、金融等对安全性和事务性要求高的场景 | 移动 App 后端、Web 前后端分离、微服务架构 |
- 如果你的服务需要与不同语言、不同平台的旧系统(尤其是 .NET, PHP 等生态)进行安全、可靠的集成,并且对消息格式有严格要求,选择 JAX-WS。
- 如果你在构建现代化的 Web 或移动应用后端,追求高性能、易用性和灵活性,选择 JAX-RS (REST),这是当前的首选。
最佳实践
- 优先考虑 REST:除非你有非常充分的理由(如与遗留系统集成或特定安全要求),否则 REST 是更现代、更简单、更高效的选择。
- 使用框架:不要自己从零开始实现,使用成熟的框架如 Apache CXF 或 Jersey,它们能处理大量底层细节。
- 契约优先:对于复杂的 SOAP 服务,或者团队前后端分离开发时,强烈建议采用“契约优先”模式,先定义好 WSDL/XSD 或 API 文档(如 OpenAPI/Swagger),然后再生成服务端和客户端代码,这能保证接口的一致性。
- 完善文档:无论使用 SOAP 还是 REST,提供清晰、易于理解的 API 文档都是至关重要的,Swagger (OpenAPI) 是 REST 的事实标准。
- 版本控制:API 是会演变的,设计你的 API 时要考虑向后兼容性,或者制定清晰的版本控制策略(如
/api/v1/...)。 - 安全性:永远不要忽视安全,对输入进行校验,使用 HTTPS,并实施适当的认证和授权机制。
希望这份详细的解析能帮助你全面理解 Java WebService!
