Java WebService 完整教程
目录
- 第一部分:核心概念
- 什么是 WebService?
- 为什么需要 WebService?
- WebService 的核心标准(SOAP, WSDL, UDDI)
- 第二部分:主流技术栈
- JAX-WS (Java API for XML Web Services)
- JAX-RS (Java API for RESTful Web Services)
- 第三部分:实战 - 使用 JAX-WS 开发传统 WebService
- 环境准备
- 开发步骤(服务端)
- 开发步骤(客户端)
- 运行与测试
- 第四部分:实战 - 使用 JAX-RS 开发 RESTful WebService
- 什么是 REST?
- 环境准备(引入 Jersey 框架)
- 开发步骤(服务端)
- 运行与测试(使用 Postman)
- 第五部分:技术选型与总结
- JAX-WS vs. JAX-RS
- 总结与学习路径
第一部分:核心概念
什么是 WebService?
WebService 是一种跨编程语言、跨操作系统的远程调用技术,它允许不同的应用程序通过网络进行交互,就像调用本地方法一样调用远程服务器上的方法。

你可以把它想象成:
一个应用程序在互联网上暴露了一个“服务端点”,其他任何应用程序都可以通过标准的 HTTP 协议来访问这个端点,并请求执行某个功能,然后得到返回结果。
为什么需要 WebService?
- 系统集成:将不同技术栈(如 Java, .NET, Python)构建的系统无缝集成。
- 跨平台通信:解决了不同操作系统之间数据交换的难题。
- 复用现有功能:可以将核心业务逻辑封装成 WebService,供多个客户端复用。
- B2B 交互:企业之间通过标准化的接口进行数据交换(如订单、物流信息)。
WebService 的核心标准
-
SOAP (Simple Object Access Protocol)
- 是什么:一种基于 XML 的协议,用于在 Web 上交换结构化信息。
- 特点:
- 协议独立(通常基于 HTTP)。
- 格式严格,是自描述的 XML。
- 包含三个部分:
Envelope(信封,必须)、Header(头部,可选)、Body(正文,必须)。 - 因为 XML 格式冗长,解析复杂,性能相对较低,但安全性和可靠性高。
-
WSDL (Web Services Description Language)
(图片来源网络,侵删)- 是什么:一个基于 XML 的语言,用于描述 WebService 的功能。
- 作用:可以理解为 WebService 的“说明书”或“接口文档”,它告诉客户端:
- 服务的地址在哪里?
- 服务提供了哪些方法(操作)?
- 每个方法的输入参数是什么?
- 每个方法的返回值是什么?
- 通信需要使用什么协议(如 SOAP/HTTP)?
- 客户端通过读取 WSDL 文件,就能知道如何调用这个服务。
-
UDDI (Universal Description, Discovery, and Integration)
- 是什么:一个基于 XML 的注册中心,用于发布和发现 WebService。
- 作用:你可以把它想象成“WebService 的黄页”,企业可以将自己的 WebService 发布到 UDDI 注册中心,其他企业则可以在这里搜索到自己需要的服务。
- 现状:由于安全和商业模式等原因,UDDI 并未像预期那样普及,现在更多的是通过直接提供 WSDL 地址或 API 文档来发现服务。
第二部分:主流技术栈
Java 官方提供了两套 API 来开发 WebService,分别对应 SOAP 和 REST 风格。
JAX-WS (Java API for XML Web Services)
- 目标:用于创建和消费 SOAP 风格的 WebService。
- 特点:
- 是 Java EE 的标准组成部分,无需额外引入核心 API。
- 提供了丰富的注解(如
@WebService,@WebMethod)来快速开发。 - 能够自动生成 WSDL 文件。
- 在 Java 6 之后,JDK 自带了
wsimport工具,可以方便地根据 WSDL 生成客户端代码。
- 实现框架:JDK 内置的实现(
Metro)是主流。
JAX-RS (Java API for RESTful Web Services)
- 目标:用于创建和消费 REST 风格的 WebService。
- 特点:
- 更轻量级,基于 HTTP 协议,通常使用 JSON 或 XML 作为数据交换格式。
- 开发简单,性能高,是目前 Web 和移动端 API 开发的首选。
- 使用 HTTP 动词(GET, POST, PUT, DELETE)来操作资源。
- 使用 URL 来定位资源(如
/users/123)。
- 实现框架:JAX-RS 只是一个规范,需要第三方框架来实现,最流行的实现是 Jersey 和 RESTEasy。
第三部分:实战 - 使用 JAX-WS 开发传统 WebService
我们将创建一个简单的计算器服务,提供加法和减法功能。
环境准备
- JDK 8 或更高版本。
- 一个 IDE(如 IntelliJ IDEA 或 Eclipse)。
- 一个 Web 容器(如 Tomcat 9.x),JDK 自带的
Endpoint可以用于快速测试,但部署到 Web 容器是更标准的做法。
开发步骤(服务端)
-
创建 Maven Web 项目 在你的 IDE 中创建一个名为
jax-ws-server的 Maven Web 项目。
(图片来源网络,侵删) -
编写服务接口和实现类 JAX-WS 要求服务实现类必须有一个服务接口。
-
com.example.CalculatorService.java(接口)import javax.jws.WebMethod; import javax.jws.WebService; import javax.jws.soap.SOAPBinding; // @WebService: 将此接口标记为 WebService 接口 // @SOAPBinding: 指定 SOAP 绑定风格,默认是 DOCUMENT @WebService @SOAPBinding(style = SOAPBinding.Style.DOCUMENT) public interface CalculatorService { // @WebMethod: 将此方法标记为 WebService 的一个操作 @WebMethod int add(int a, int b); @WebMethod int subtract(int a, int b); } -
com.example.CalculatorServiceImpl.java(实现类)import javax.jws.WebService; // @WebService: 指定此实现类实现了哪个服务接口 // endpointInterface 属性是必须的,它连接了接口和实现 @WebService(endpointInterface = "com.example.CalculatorService") public class CalculatorServiceImpl implements CalculatorService { @Override public int add(int a, int b) { System.out.println("WebService add method called with a=" + a + ", b=" + b); return a + b; } @Override public int subtract(int a, int b) { System.out.println("WebService subtract method called with a=" + a + ", b=" + b); return a - b; } }
-
-
发布 WebService 你可以通过两种方式发布服务:
-
使用 JDK 内置的
Endpoint(适合快速测试) 创建一个主类com.example.Publisher:import javax.xml.ws.Endpoint; public class Publisher { public static void main(String[] args) { // 创建服务的实现实例 CalculatorService calculatorService = new CalculatorServiceImpl(); // 发布服务 // 第一个参数是服务的访问地址 // 第二个参数是服务的实现对象 Endpoint.publish("http://localhost:8080/calculator", calculatorService); System.out.println("WebService is published successfully at http://localhost:8080/calculator?wsdl"); } }运行
Publisher类,然后访问http://localhost:8080/calculator?wsdl,如果能看到一个 XML 文件(WSDL),说明发布成功。 -
部署到 Web 容器(如 Tomcat,标准做法)
- 配置
web.xml: 在src/main/webapp/WEB-INF/web.xml中添加:<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <listener> <listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class> </listener> <servlet> <servlet-name>CalculatorService</servlet-name> <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CalculatorService</servlet-name> <url-pattern>/calculator</url-pattern> </servlet-mapping> </web-app> - 创建
sun-jaxws.xml: 在src/main/webapp/WEB-INF/目录下创建sun-jaxws.xml文件:<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0"> <endpoint name="CalculatorService" implementation="com.example.CalculatorServiceImpl" url-pattern="/calculator"/> </endpoints> - 打包并部署:将项目打包成 WAR 文件,然后部署到 Tomcat,启动 Tomcat 后,访问
http://your-tomcat-host:8080/your-project-name/calculator?wsdl即可看到 WSDL。
- 配置
-
开发步骤(客户端)
使用 wsimport 工具可以自动生成客户端代码。
-
打开命令行,进入你的项目目录。
-
执行
wsimport命令:# -p: 指定生成的客户端代码包名 # -d: 指定生成.class文件的存放目录 # -keep: 保留生成的源文件 wsimport -p com.example.client -d target/generated-sources -keep http://localhost:8080/calculator?wsdl
执行后,会在
target/generated-sources目录下生成一堆客户端代码(包括接口、实现类、辅助类等)。 -
编写客户端调用代码 在
src/main/java下创建客户端类com.example.ClientTest:import com.example.client.CalculatorService; import com.example.client.CalculatorServiceImplService; public class ClientTest { public static void main(String[] args) { // 通过 WSDL 中的服务名称(<service name="...">)来创建服务实例 CalculatorServiceImplService service = new CalculatorServiceImplService(); // 从服务实例中获取服务端口(即接口) CalculatorService calculatorPort = service.getCalculatorServiceImplPort(); // 像调用本地方法一样调用远程方法 int resultAdd = calculatorPort.add(10, 5); System.out.println("10 + 5 = " + resultAdd); int resultSubtract = calculatorPort.subtract(10, 5); System.out.println("10 - 5 = " + resultSubtract); } }运行
ClientTest,如果控制台输出了正确的结果,说明客户端调用成功。
第四部分:实战 - 使用 JAX-RS 开发 RESTful WebService
我们将创建一个用户管理服务,提供查询用户列表和根据 ID 查询用户的功能。
环境准备
- JDK 8+
- IDE
- Maven
- Jersey 框架依赖:Jersey 是 JAX-RS 的一个流行实现。
开发步骤(服务端)
-
创建 Maven 项目 创建一个名为
jax-rs-server的 Maven 项目。 -
添加 Jersey 依赖 在
pom.xml中添加:<dependencies> <!-- Jersey 核心依赖 --> <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> <!-- 为了简化日志,可以加入一个日志实现,如 Logback 或 Log4j2 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.4.7</version> </dependency> </dependencies> -
配置 Web 容器(Servlet 3.0+) 创建
src/main/java/com/example/MyApplication.java,这是 Jersey 的应用入口。import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.servlet.ServletContainer; import org.glassfish.jersey.servlet.ServletProperties; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import java.util.HashSet; import java.util.Set; // 自定义的 HttpServlet,用于启动 Jersey public class MyApplication extends HttpServlet { @Override public void init(ServletConfig config) throws ServletException { super.init(config); ResourceConfig resourceConfig = new ResourceConfig(); // 注册资源类 resourceConfig.packages("com.example.resource"); // 设置 Servlet 属性 config.getServletContext().setAttribute( ServletProperties.JAXRS_APPLICATION_CLASS, resourceConfig.getClass().getName()); } }在
src/main/webapp/WEB-INF/web.xml中配置 Servlet 映射:<web-app> <servlet> <servlet-name>Jersey Web Application</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Jersey Web Application</servlet-name> <url-pattern>/api/*</url-pattern> </servlet-mapping> </web-app>注意:如果你使用的是较新的 Tomcat 版本(支持 Servlet 3.0+),可以使用无
web.xml的方式,通过@ApplicationPath注解来配置。 -
创建资源类 在
src/main/java/com/example/resource包下创建UserResource.java:import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.util.*; @Path("/users") // 定义资源的基本路径 public class UserResource { // 模拟一个数据库 private static final Map<Long, User> userDB = new HashMap<>(); static { userDB.put(1L, new User(1, "Alice")); userDB.put(2L, new User(2, "Bob")); } // @GET: 表示这是一个 HTTP GET 请求 // @Produces: 指定返回的媒体类型,这里是 JSON @GET @Produces(MediaType.APPLICATION_JSON) public List<User> getAllUsers() { System.out.println("GET /users called"); return new ArrayList<>(userDB.values()); } // @Path: 在类路径的基础上再添加子路径 // @PathParam: 从 URL 路径中获取参数 // @GET: 同样是 GET 请求 @GET @Path("/{id}") @Produces(MediaType.APPLICATION_JSON) public Response getUserById(@PathParam("id") long id) { System.out.println("GET /users/" + id + " called"); User user = userDB.get(id); if (user == null) { // 如果用户不存在,返回 404 Not Found return Response.status(Response.Status.NOT_FOUND).entity("User not found").build(); } // 如果用户存在,返回 200 OK 和用户数据 return Response.ok(user).build(); } // 一个简单的 User 类 public static class User { private long id; private String name; public User() {} public User(long id, String name) { this.id = id; this.name = name; } // Getters and Setters (必须要有,否则 JSON 序列化会失败) public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } } } -
部署和测试 将项目打包成 WAR 并部署到 Tomcat,启动 Tomcat。
运行与测试(使用 Postman 或浏览器)
-
获取所有用户:
- 方法:
GET - URL:
http://localhost:8080/your-project-name/api/users - 预期响应 (200 OK):
[ { "id": 1, "name": "Alice" }, { "id": 2, "name": "Bob" } ]
- 方法:
-
获取 ID 为 1 的用户:
- 方法:
GET - URL:
http://localhost:8080/your-project-name/api/users/1 - 预期响应 (200 OK):
{ "id": 1, "name": "Alice" }
- 方法:
-
获取 ID 为 999 的用户(不存在):
- 方法:
GET - URL:
http://localhost:8080/your-project-name/api/users/999 - 预期响应 (404 Not Found):
User not found
- 方法:
第五部分:技术选型与总结
JAX-WS vs. JAX-RS
| 特性 | JAX-WS (SOAP) | JAX-RS (REST) |
|---|---|---|
| 协议 | 基于 XML 的 SOAP 协议 | 基于 HTTP 协议 |
| 数据格式 | 严格、冗长的 XML | 轻量级的 JSON, XML, Text 等 |
| 风格 | 面向操作/方法 | 面向资源 |
| 地址 | http://host/service?wsdl |
http://host/resource/id |
| HTTP 动词 | 不直接使用 | 使用 (GET, POST, PUT, DELETE) |
| 缓存 | 不支持 | 支持 (利用 HTTP 缓存机制) |
| 性能 | 较低 (XML 解析开销大) | 较高 (JSON 解析快,无额外协议开销) |
| 安全性 | 内置 WS-Security 标准 | 通常依赖 HTTPS 和 OAuth2 等标准 |
| 适用场景 | 企业级应用集成(B2B)、银行、金融等对安全性和事务性要求高的场景 | 公开 API、Web 应用、移动后端、微服务 |
总结与学习路径
- 入门建议:直接从 JAX-RS (REST) 开始,因为它是目前业界的主流,更简单、更灵活、性能更好,掌握 RESTful API 设计是现代开发者的必备技能。
- 何时学习 JAX-WS:如果你需要维护遗留的 SOAP 系统,或者在某些特定行业(如金融、电信)的集成项目中,SOAP 仍然是强制要求时,才需要深入学习 JAX-WS。
- 学习路径:
- 掌握基础:理解 HTTP 协议(GET, POST, PUT, DELETE, 状态码)。
- 学习 REST 设计原则:理解资源、URI、无状态等概念。
- 精通 JAX-RS (Jersey):熟练使用
@Path,@GET,@POST,@Produces,@Consumes,@PathParam,@QueryParam等核心注解。 - 进阶学习:学习依赖注入(DI)、异常处理、过滤器、拦截器、以及如何与数据库(如 JPA)结合。
- 了解 JAX-WS:了解其基本概念和工作原理,知道如何开发和调用一个简单的 SOAP 服务即可。
希望这份详尽的教程能对你有所帮助!祝你学习愉快!
