杰瑞科技汇

Java中WebService如何实现与调用?

这是一个非常经典且重要的主题,因为 Java 在 WebService 的发展史上扮演了至关重要的角色,我会从核心概念、主流技术演进、具体实现现代替代方案四个方面来详细讲解。

Java中WebService如何实现与调用?-图1
(图片来源网络,侵删)

核心概念:什么是 WebService?

在深入 Java 技术之前,我们先要明白 WebService 是什么。

WebService 是一种跨编程语言、跨操作系统平台的远程调用技术,它使用 HTTP 协议作为传输协议,使用 XMLJSON 等格式来封装和传输数据。

它的核心目标是实现“系统间的通信”,尤其是在异构系统(比如用 Java 写的后端和用 Python 写的后端)之间。

WebService 的主要特点:

Java中WebService如何实现与调用?-图2
(图片来源网络,侵删)
  1. 跨平台:只要能发送 HTTP 请求和解析 XML/JSON,任何语言都可以调用。
  2. 跨语言:与语言无关。
  3. 基于标准:遵循一系列国际标准(如 SOAP, WSDL),保证了互操作性。
  4. 松耦合:服务调用方和服务提供方之间不需要了解对方的内部实现细节,只需要知道接口规范(WSDL)。

Java WebService 技术演进

Java 的 WebService 技术经历了几个重要的阶段,每个阶段都有其代表性的技术和框架。

JAX-WS (Java API for XML Web Services) - 目前企业级应用的主流

这是 Java 官方推出的、用于创建和消费 SOAP (Simple Object Access Protocol) WebService 的标准 API,它极大地简化了 WebService 的开发。

  • 核心思想:通过注解将一个普通的 Java 类变成一个 WebService 服务端。
  • 关键注解
    • @WebService: 将一个类标记为 WebService 服务端。
    • @WebMethod: 将一个方法标记为 WebService 的可操作方法。
    • @WebParam: 定义方法的参数。
  • 优点
    • 标准化:Java EE 的标准,实现厂商多,兼容性好。
    • 工具支持:通过 wsimport 命令可以一键根据 WSDL 文件生成客户端调用代码,非常方便。
    • 契约优先:可以先定义 WSDL 契约,然后生成服务端和客户端的“骨架代码”。
  • 缺点
    • 主要用于 SOAP 协议,相对复杂和“重量级”。
    • 配置相对繁琐。

代表实现

  • JDK 内置实现:从 JDK 1.6 开始,JAX-WS API 就被内置了,可以直接使用 Endpoint 类来发布服务。
  • Apache CXF:一个非常流行和强大的开源框架,对 JAX-WS 提供了完美的支持,功能更丰富,性能更好。
  • Metro:由 Sun (Oracle) 官方提供的实现,也包含在 GlassFish 等应用服务器中。

JAX-RS (Java API for RESTful Web Services) - 现代 Web 开发的事实标准

虽然 JAX-RS 严格来说不属于传统意义上的“WebService”(因为它通常不使用 SOAP/WSDL),但它在功能上实现了跨系统调用的核心目标,并且已经成为现代 Java Web 开发的主流,所以必须在这里重点介绍。

Java中WebService如何实现与调用?-图3
(图片来源网络,侵删)
  • 核心思想:将一个普通的 Java 类通过注解变成一个 RESTful 风格的 Web 服务(通常返回 JSON 或 XML)。
  • 关键注解
    • @Path: 定义资源的 URI 路径。
    • @GET, @POST, @PUT, @DELETE: 定义 HTTP 方法。
    • @Produces: 定义响应的媒体类型(如 MediaType.APPLICATION_JSON)。
    • @Consumes: 定义可接受的请求媒体类型。
    • @PathParam, @QueryParam, @FormParam: 用于获取路径参数、查询参数和表单数据。
  • 优点
    • 轻量级:基于 HTTP 协议,无冗余的 SOAP 信封,性能高。
    • 简单易用:学习曲线平缓,开发效率高。
    • 无状态:符合 REST 架构风格,易于缓存和扩展。
    • 与前端完美契合:现代前后端分离架构的首选。
  • 缺点
    • 没有 WSDL 这样的“标准契约”,接口文档需要通过 Swagger (OpenAPI) 等工具来生成和维护。
    • 不适合需要事务、安全等复杂 WS-* 标准的场景。

代表实现

  • Jersey:由 Oracle 赞助的 JAX-RS 的参考实现。
  • RESTEasy:JBoss (Red Hat) 提供的 JAX-RS 实现。
  • Apache CXF:同样支持 JAX-RS,并且可以和它的 JAX-WS 功能无缝集成。

早期技术 (了解即可)

在 JAX-WS 之前,Java 社区还有一些早期的 WebService 技术。

  1. JAX-RPC (Java API for XML-based RPC)

    JAX-WS 的前身,现在已经过时,被 JAX-WS 完全取代。

  2. Axis / Axis2 (来自 Apache)

    在 JAX-WS 标准普及之前,Axis 是 Java 领域最流行的 WebService 框架,Axis2 是其重构后的版本,功能强大,但配置和使用比 JAX-WS 复杂,现在仍有项目在使用,但新项目已很少选择。


具体实现示例

下面我们通过代码来感受一下 JAX-WS 和 JAX-RS 的开发方式。

示例1:JAX-WS 服务端与客户端

服务端 (JAX-WS)

我们创建一个简单的服务,提供一个根据用户名查询用户信息的功能。

import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.WebParam;
// @WebService 注解将这个类声明为一个 WebService
@WebService
public class UserService {
    // @WebMethod 注解将这个方法暴露为 WebService 的一个操作
    @WebMethod
    public User getUserInfo(@WebParam(name = "username") String username) {
        System.out.println("服务端收到请求,查询用户: " + username);
        // 模拟数据库查询
        if ("admin".equals(username)) {
            return new User("admin", "管理员", "admin@example.com");
        } else if ("zhangsan".equals(username)) {
            return new User("zhangsan", "张三", "zhangsan@example.com");
        }
        return null;
    }
}
// 一个简单的 POJO,作为数据载体
class User {
    private String username;
    private String fullName;
    private String email;
    // 构造方法、getter 和 setter
    public User(String username, String fullName, String email) {
        this.username = username;
        this.fullName = fullName;
        this.email = email;
    }
    // ... 省略 getter 和 setter ...
    @Override
    public String toString() {
        return "User{" + "username='" + username + '\'' + ", fullName='" + fullName + '\'' + ", email='" + email + '\'' + '}';
    }
}

发布服务:你可以写一个 main 方法来发布这个服务。

import javax.xml.ws.Endpoint;
public class Publisher {
    public static void main(String[] args) {
        // 创建服务实例
        UserService userService = new UserService();
        // 发布服务,地址为 http://localhost:8080/userService
        String address = "http://localhost:8080/userService";
        Endpoint.publish(address, userService);
        System.out.println("WebService 已发布在: " + address);
    }
}

运行后,访问 http://localhost:8080/userService?wsdl,你就能看到服务的 WSDL 契约文件。

客户端 (JAX-WS)

客户端的开发有两种方式:

  • 使用 wsimport 工具生成代码 (推荐)

    在命令行中执行:

    wsimport -s . http://localhost:8080/userService?wsdl

    这会在当前目录下生成一堆 Java 文件(服务端接口、Service 类等),然后你就可以像调用本地方法一样调用远程服务了。

    // wsimport 生成的代码
    public class Client {
        public static void main(String[] args) {
            // 创建服务视图
            UserService_Service service = new UserService_Service();
            // 获取服务端点
            UserService userServicePort = service.getUserServicePort();
            // 调用远程方法
            User user = userServicePort.getUserInfo("admin");
            System.out.println("客户端收到响应: " + user);
        }
    }
  • 使用 Dispatch API (更灵活,适合动态调用)

    import javax.xml.namespace.QName;
    import javax.xml.ws.Dispatch;
    import javax.xml.ws.Service;
    import javax.xml.transform.stream.StreamSource;
    import java.net.URL;
    public class DynamicClient {
        public static void main(String[] args) throws Exception {
            URL wsdlUrl = new URL("http://localhost:8080/userService?wsdl");
            QName serviceName = new QName("http://default/", "UserService_Service");
            Service service = Service.create(wsdlUrl, serviceName);
            // 使用 Dispatch API,直接操作 XML 消息
            Dispatch<StreamSource> dispatch = service.createDispatch(
                new QName("http://default/", "UserService"), 
                StreamSource.class, 
                Service.Mode.PAYLOAD
            );
            // 构造 SOAP 请求体 (简化版)
            String request = "<ns2:getUserInfo xmlns:ns2=\"http://default/\"><username>zhangsan</username></ns2:getUserInfo>";
            // 发送请求
            StreamSource response = dispatch.invoke(new StreamSource(new StringReader(request)));
            // 解析响应 XML...
            System.out.println("收到动态响应: " + response);
        }
    }

示例2:JAX-RS (Jersey) 服务端与客户端

服务端 (JAX-RS)

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
// @Path 定义了这个资源的 URI 基础路径
@Path("/api/users")
public class UserResource {
    // @GET 表示这是一个处理 GET 请求的方法
    // @Path("info/{username}") 定义了子路径
    // @PathParam("username") 将路径中的 {username} 绑定到方法参数
    // @Produces(MediaType.APPLICATION_JSON) 指定返回的数据类型是 JSON
    @GET
    @Path("info/{username}")
    @Produces(MediaType.APPLICATION_JSON)
    public User getUser(@PathParam("username") String username) {
        System.out.println("REST 服务收到请求,查询用户: " + username);
        if ("admin".equals(username)) {
            return new User("admin", "管理员", "admin@example.com");
        }
        return null;
    }
}

发布服务:通常需要运行在一个 Web 容器(如 Tomcat, Jetty)或应用服务器(如 Jersey 自带的 Grizzly)中,配置一个 web.xml 或使用 Jersey 的 ResourceConfig

客户端 (JAX-RS)

同样可以使用 Jersey 客户端 API 来调用。

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 RestClient {
    public static void main(String[] args) {
        // 创建客户端
        Client client = ClientBuilder.newClient();
        // 定义目标资源地址
        WebTarget target = client.target("http://localhost:8080/my-app/api/users/info/admin");
        // 发送 GET 请求,并期望返回 JSON
        Response response = target.request(MediaType.APPLICATION_JSON).get();
        // 检查响应状态
        if (response.getStatus() == 200) {
            // 将响应体直接映射为 User 对象
            User user = response.readEntity(User.class);
            System.out.println("REST 客户端收到响应: " + user);
        } else {
            System.out.println("请求失败,状态码: " + response.getStatus());
        }
        // 关闭客户端
        client.close();
    }
}

如何选择?现代趋势

特性 JAX-WS (SOAP) JAX-RS (REST)
协议 SOAP (基于 XML) HTTP (通常用 JSON)
风格 面向操作 面向资源
复杂度 高,信封、头、WS-* 标准 低,简单直接
性能 较低 较高
标准化 高 (WSDL, SOAP, WS-Security 等) 较低 (依赖 OpenAPI/Swagger 文档)
安全性 内置标准安全机制 (WS-Security) 通常依赖 HTTPS 和 OAuth/JWT
适用场景 企业级应用、金融、电信等对事务、安全、可靠性要求极高的场景 现代 Web 应用、微服务、前后端分离架构、移动端 API
发展趋势 传统行业存量维护 绝对主流,新项目的首选

总结与建议:

  1. 新项目首选 JAX-RS:除非你有非常特殊的需求(比如必须和某个旧的 SOAP 系统深度集成,或者银行等强监管行业要求使用 WS-Security),否则对于所有新的 Java Web 服务开发,都应该毫不犹豫地选择 JAX-RS,它与微服务、云原生、前后端分离等现代开发理念完美契合。

  2. JAX-WS 仍有价值:JAX-WS 并没有完全消亡,它仍然在许多大型企业(特别是银行、保险、电信)的核心系统中扮演着重要角色,维护这些系统的存量代码需要掌握 JAX-WS。

  3. 框架选择

    • 做 JAX-RS,Spring Boot + Spring Web MVC 是目前最强大的组合(虽然 Spring MVC 不完全等同于 JAX-RS,但其 RESTful 开发模式已成为事实标准)。
    • 如果想用纯 JAX-RS 规范,JerseyRESTEasy 都是优秀的选择。
    • 做 JAX-WS,Apache CXF 是功能最全面、生态最好的选择。
  4. 未来展望:随着 gRPC、GraphQL 等新技术的兴起,RESTful API 也面临挑战,但就目前而言,JAX-RS (REST) 仍然是 Java 生态中构建跨服务 API 的基石,了解这些技术的演进脉络,能帮助你更好地做出技术选型。

分享:
扫描分享到社交APP
上一篇
下一篇