Java 开发 WebService 主要有两种主流技术栈:
- JAX-WS (Java API for XML Web Services):这是 Java 官方、标准的 WebService 开发技术,它比较成熟,广泛应用于传统的 Java EE(Jakarta EE)项目中,对于简单的 POJO(普通 Java 对象)非常友好,可以快速上手。
- JAX-RS (Java API for RESTful Web Services):这是用于开发 RESTful 风格 WebService 的 API,随着移动互联网和前后端分离架构的流行,RESTful API 已成为主流,JAX-RS 通常与 JSON 数据格式配合使用,更轻量、更灵活。
下面我将分别对这两种技术进行详细的讲解,并提供完整的开发示例。
第一部分:使用 JAX-WS 开发传统 WebService
JAX-WS 的核心思想是,你只需要编写一个普通的 Java 接口和它的实现类,然后通过一些注解,JAX-WS 框架就能帮你自动将它们转换成可以通过 SOAP 协议访问的 WebService。
核心概念和注解
@WebService: 用于在接口或实现类上,标记它是一个 WebService。- 在接口上使用,表示这是一个服务端点接口。
- 在实现类上使用,表示这是一个服务端点实现。
@WebMethod: 用于在接口的方法上,标记这个方法是一个可以被远程调用的 WebService 操作,如果不加,默认所有 public 方法都会被暴露。@WebParam: 用于在方法的参数上,指定参数的名称(在 WSDL 文件中会体现)。@WebResult: 用于在方法的返回值上,指定返回值的名称。
开发步骤(以原生 JAX-WS 为例)
环境准备: 你需要 JDK 1.6 或更高版本,因为 JAX-WS 已经被集成到了 JDK 中。
示例:创建一个简单的计算器 WebService
创建服务端点接口
// src/com/example/Calculator.java
package com.example;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
// @WebService: 标记这是一个WebService接口
// name: 指定WebService的名称
// targetNamespace: 指定命名空间,通常是接口包名的反序
// @SOAPBinding: 指定SOAP消息的风格,默认是 DOCUMENT
@WebService(name = "Calculator", targetNamespace = "http://example.com/")
@SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
public interface Calculator {
// @WebMethod: 标记这是一个可以被远程调用的方法
// @WebParam: 指定参数名
// @WebResult: 指定返回值名
@WebMethod
@WebResult(name = "sumResult")
public int add(
@WebParam(name = "a") int a,
@WebParam(name = "b") int b);
@WebMethod
@WebResult(name = "diffResult")
public int subtract(
@WebParam(name = "a") int a,
@WebParam(name = "b") int b);
}
创建服务端点实现类
// src/com/example/CalculatorImpl.java
package com.example;
import javax.jws.WebService;
// @WebService: 标记这是一个WebService的实现类
// endpointInterface: 指定它实现了哪个接口
@WebService(endpointInterface = "com.example.Calculator")
public class CalculatorImpl implements Calculator {
@Override
public int add(int a, int b) {
return a + b;
}
@Override
public int subtract(int a, int b) {
return a - b;
}
}
发布 WebService
你需要一个主类来启动一个内嵌的 HTTP 服务器,并将你的实现类发布出去。
// src/com/example/CalculatorPublisher.java
package com.example;
import javax.xml.ws.Endpoint;
// Endpoint 是 JAX-WS 提供的用于发布 WebService 的工具类
public class CalculatorPublisher {
public static void main(String[] args) {
// 定义WebService的发布地址
String address = "http://localhost:8888/calculator";
// 发布WebService
// 第一个参数是访问地址,第二个参数是WebService的实现类实例
Endpoint.publish(address, new CalculatorImpl());
System.out.println("WebService is published at: " + address);
}
}
运行和测试
- 运行:直接运行
CalculatorPublisher的main方法。 - 验证 WSDL:在浏览器中访问
http://localhost:8888/calculator?wsdl,如果能看到一堆 XML 内容,说明你的 WebService 发布成功了,这就是 WSDL (Web Services Description Language) 文件,它描述了如何调用你的服务。
创建客户端调用
JDK 提供了 wsimport 工具,可以根据 WSDL 文件自动生成客户端调用代码。
-
生成客户端代码: 打开命令行,进入你的项目目录(或者任意目录),执行以下命令:
wsimport -keep -p com.example.client http://localhost:8888/calculator?wsdl
-keep: 生成源代码。-p com.example.client: 指定生成代码的包名。- 后面是 WSDL 文件的地址。
-
使用生成的客户端代码:
wsimport会生成一堆文件,最重要的是Calculator和CalculatorService这两个类。 你可以写一个测试类来调用服务:// src/com/example/client/CalculatorClient.java package com.example.client; public class CalculatorClient { public static void main(String[] args) { // 创建服务实例,通过wsdlLocation指定wsdl地址 CalculatorService service = new CalculatorService(); // 获取服务端点,portName需要从生成的wsdl文件中查看<port name="CalculatorPort">...</port> Calculator calculator = service.getCalculatorPort(); // 调用远程方法 int sum = calculator.add(10, 20); System.out.println("10 + 20 = " + sum); int diff = calculator.subtract(50, 25); System.out.println("50 - 25 = " + diff); } }运行
CalculatorClient,你将看到控制台输出正确的结果。
第二部分:使用 JAX-RS 开发 RESTful WebService
RESTful WebService 更符合现代 Web 应用的设计理念,它使用标准的 HTTP 方法(GET, POST, PUT, DELETE)来操作资源,数据格式通常是轻量级的 JSON 或 XML。
核心概念和注解
@Path: 用于类或方法上,定义 URL 的路径。@GET,@POST,@PUT,@DELETE: 用于方法上,定义 HTTP 请求方法。 *.@Produces: 用于方法上,定义该方法能返回的媒体类型(如application/json,application/xml)。@Consumes: 用于方法上,定义该方法能接受的请求体媒体类型。 *.@QueryParam: 用于获取 URL 中的查询参数(?key=value)。@PathParam: 用于获取 URL 路径中的参数(/users/{id})。@FormParam: 用于获取表单提交的参数。@BeanParam: 用于将多个参数封装到一个对象中。
开发步骤(以 Jersey 框架为例)
虽然 JAX-RS 是 Java 标准,但你需要一个实现框架,最流行的实现是 Jersey 和 RESTEasy,这里我们以 Jersey 为例。
环境准备: 你需要一个构建工具(如 Maven 或 Gradle)来管理 Jersey 的依赖。
Maven 依赖 (pom.xml)
<dependencies>
<!-- Jersey 核心依赖 -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</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>
<!-- Lombok (可选,简化代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
</dependencies>
示例:创建一个用户管理 RESTful API
创建一个实体类
// src/com/example/model/User.java
package com.example.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data // Lombok: 自动生成 getter, setter, toString 等
@NoArgsConstructor
@AllArgsConstructor
public class User {
private int id;
private String name;
private String email;
}
创建资源类
这是 JAX-RS 的核心,相当于 JAX-WS 中的 SEI 和 SEI 实现的结合体。
// src/com/example/resource/UserResource.java
package com.example.resource;
import com.example.model.User;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Path("/users") // 定义基础路径
public class UserResource {
// 模拟一个数据库
private List<User> users = new ArrayList<>();
private int currentId = 1;
// GET /users - 获取所有用户
@GET
@Produces(MediaType.APPLICATION_JSON) // 声明返回 JSON 格式
public List<User> getAllUsers() {
return users;
}
// GET /users/{id} - 根据 ID 获取单个用户
@GET
@Path("/{id}") // 定义路径参数
@Produces(MediaType.APPLICATION_JSON)
public Response getUserById(@PathParam("id") int id) {
Optional<User> userOptional = users.stream()
.filter(u -> u.getId() == id)
.findFirst();
if (userOptional.isPresent()) {
return Response.ok(userOptional.get()).build(); // 200 OK
} else {
return Response.status(Response.Status.NOT_FOUND).build(); // 404 Not Found
}
}
// POST /users - 创建一个新用户
@POST
@Consumes(MediaType.APPLICATION_JSON) // 声明接收 JSON 格式的请求体
@Produces(MediaType.APPLICATION_JSON)
public Response createUser(User user) {
user.setId(currentId++);
users.add(user);
// 返回 201 Created,并在 Location 头中给出新资源的 URI
return Response.status(Response.Status.CREATED)
.entity(user)
.build();
}
// PUT /users/{id} - 更新一个用户
@PUT
@Path("/{id}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response updateUser(@PathParam("id") int id, User updatedUser) {
for (int i = 0; i < users.size(); i++) {
User user = users.get(i);
if (user.getId() == id) {
updatedUser.setId(id); // 确保ID不被修改
users.set(i, updatedUser);
return Response.ok(updatedUser).build(); // 200 OK
}
}
return Response.status(Response.Status.NOT_FOUND).build(); // 404 Not Found
}
// DELETE /users/{id} - 删除一个用户
@DELETE
@Path("/{id}")
public Response deleteUser(@PathParam("id") int id) {
boolean removed = users.removeIf(u -> u.getId() == id);
if (removed) {
return Response.noContent().build(); // 204 No Content
} else {
return Response.status(Response.Status.NOT_FOUND).build(); // 404 Not Found
}
}
}
创建应用程序启动类
// src/com/example/MyApplication.java
package com.example;
import com.example.resource.UserResource;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.grizzly.http.server.HttpServer;
public class MyApplication {
public static void main(String[] args) {
// 创建 ResourceConfig 并注册我们的资源类
ResourceConfig resourceConfig = new ResourceConfig(UserResource.class);
// 创建并启动 HTTP 服务器
HttpServer server = org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory
.createHttpServer(java.net.URI.create("http://localhost:8080/"), resourceConfig);
System.out.println("Server started at http://localhost:8080/");
try {
server.start();
// 保持服务器运行
Thread.currentThread().join();
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行和测试
-
运行:直接运行
MyApplication的main方法。 -
测试:你可以使用 Postman、curl 或浏览器来测试这些 API。
- 创建用户 (POST):
curl -X POST -H "Content-Type: application/json" -d '{"name":"Alice","email":"alice@example.com"}' http://localhost:8080/users - 获取所有用户 (GET):
curl http://localhost:8080/users
- 获取单个用户 (GET):
curl http://localhost:8080/users/1
- 更新用户 (PUT):
curl -X PUT -H "Content-Type: application/json" -d '{"name":"Alice Smith","email":"alice.smith@example.com"}' http://localhost:8080/users/1 - 删除用户 (DELETE):
curl -X DELETE http://localhost:8080/users/1
- 创建用户 (POST):
总结与对比
| 特性 | JAX-WS (SOAP) | JAX-RS (REST) |
|---|---|---|
| 协议 | 强制使用 SOAP 协议(基于 XML) | 使用标准 HTTP/HTTPS 协议 |
| 数据格式 | 主要是 XML | 主要是 JSON,也支持 XML 等 |
| 风格 | 面向操作,有严格的契约(WSDL) | 面向资源,更灵活,无状态 |
| 地址空间 | 动态地址,通常需要 WSDL 发现 | 静态、可预测的 URL 地址 |
| 标准化 | Java EE 标准的一部分 | Java EE 标准的一部分 |
| 适用场景 | 企业级应用集成(B2B)、需要高安全性、事务支持的场景 | Web 应用、移动应用后端、前后端分离项目 |
| 学习曲线 | 相对较陡,概念较多(WSDL, SOAP, Binding 等) | 相对平缓,更贴近 Web 开发者习惯 |
如何选择?
- 如果你的项目是传统的企业内部系统,需要和遗留系统集成,或者对安全性、事务性有非常高的要求,可以考虑 JAX-WS。
- 如果你在开发新的 Web 应用、移动 App 后端 API,或者采用前后端分离架构,JAX-RS 是不二之选,它是目前业界的主流,更轻量、更易于理解和维护。
在现代 Spring Boot 框架中,开发 RESTful API 通常会直接使用 Spring MVC 的 @RestController,其设计理念与 JAX-RS 非常相似,但配置更简单,生态更强大,但对于理解 Java 原生 WebService JAX-WS 和 JAX-RS 仍然是重要的基础。
