1. 概述

Java API for XML Web Services (JAX-WS) 是一套标准化的 API,用于创建和消费 SOAP(简单对象访问协议)Web 服务。

本文将带你:

  • ✅ 创建一个 SOAP Web 服务
  • ✅ 使用 JAX-WS 连接该服务

2. SOAP 协议

SOAP 是一种基于 XML 的网络消息传输规范,具有以下特点:

  • 跨平台兼容:与操作系统无关
  • 协议无关:支持 HTTP、SMTP 等多种通信协议
  • ⚠️ XML 冗余:消息体积较大,必须配合工具/框架使用

JAX-WS 作为 Java 标准的一部分,简化了 SOAP 的使用,是 Java 开发者的首选方案。

3. 开发模式对比

开发 SOAP Web 服务有两种主流模式:

3.1 自顶向下(契约优先)

  • 流程:先创建 WSDL 文件 → 生成 Java 类
  • 优势:契约稳定,代码变动不影响接口定义
  • 劣势:编写复杂 WSDL 文件门槛较高

3.2 自底向上(代码优先)

  • 流程:先写 Java 类 → 自动生成 WSDL
  • 优势:开发效率高,适合快速迭代
  • 劣势:代码修改可能导致 WSDL 变更

⚠️ 踩坑提醒:生产环境推荐自顶向下模式,确保接口稳定性;原型开发可用自底向上模式快速验证。

4. WSDL 核心结构

WSDL 是 Web 服务的契约定义文件,采用 XML 格式描述服务接口。其核心元素包括:

4.1 Definitions(根元素)

定义服务名称、命名空间等全局信息:

<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" 
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
  xmlns:tns="http://jaxws.baeldung.com/" 
  xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
  xmlns:wsp="http://www.w3.org/ns/ws-policy" 
  xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy"
  xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
  targetNamespace="http://jaxws.baeldung.com/" 
  name="EmployeeService">
  ...
</definitions>

4.2 Types(数据类型)

使用 XSD(XML Schema Definition)定义数据类型:

<types>
    <xsd:schema>
        <xsd:import namespace="http://jaxws.baeldung.com/" 
          schemaLocation = "http://localhost:8080/employeeservice?xsd=1" />
    </xsd:schema>
</types>

4.3 Messages(消息抽象)

定义输入、输出和异常消息:

<message name="getEmployee">
    <part name="parameters" element="tns:getEmployee" />
</message>
<message name="getEmployeeResponse">
    <part name="parameters" element="tns:getEmployeeResponse" />
</message>
<message name="EmployeeNotFound">
    <part name="fault" element="tns:EmployeeNotFound" />
</message>

4.4 PortType(操作集合)

描述服务操作及其关联消息:

<portType name="EmployeeService">
    <operation name="getEmployee">
        <input wsam:Action="http://jaxws.baeldung.com/EmployeeService/getEmployeeRequest" 
               message="tns:getEmployee" />
        <output wsam:Action="http://jaxws.baeldung.com/EmployeeService/getEmployeeResponse" 
                message="tns:getEmployeeResponse" />
        <fault message="tns:EmployeeNotFound" name="EmployeeNotFound" 
               wsam:Action="http://jaxws.baeldung.com/EmployeeService/getEmployee/Fault/EmployeeNotFound" />
    </operation>
</portType>

4.5 Binding(协议绑定)

指定通信协议和数据格式:

<binding name="EmployeeServiceImplPortBinding" type="tns:EmployeeService">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />
    <operation name="getEmployee">
        <soap:operation soapAction="" />
        <input><soap:body use="literal" /></input>
        <output><soap:body use="literal" /></output>
        <fault name="EmployeeNotFound">
            <soap:fault name="EmployeeNotFound" use="literal" />
        </fault>
    </operation>
</binding>

4.6 Service(服务端点)

定义服务访问地址:

<service name="EmployeeService">
    <port name="EmployeeServiceImplPort" binding="tns:EmployeeServiceImplPortBinding">
        <soap:address location="http://localhost:8080/employeeservice" />
    </port>
</service>

5. 自顶向下开发实践

5.1 创建 WSDL 文件

以下为简化版 WSDL 示例(employeeservicetopdown.wsdl):

<?xml version="1.0" encoding="UTF-8"?>
<definitions 
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:tns="http://topdown.server.jaxws.baeldung.com/"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns="http://schemas.xmlsoap.org/wsdl/"
  targetNamespace="http://topdown.server.jaxws.baeldung.com/"
  qname="EmployeeServiceTopDown">
    
    <types>
        <xsd:schema targetNamespace="http://topdown.server.jaxws.baeldung.com/">
            <xsd:element name="countEmployeesResponse" type="xsd:int"/>
        </xsd:schema>
    </types>

    <message name="countEmployees"/>
    <message name="countEmployeesResponse">
        <part name="parameters" element="tns:countEmployeesResponse"/>
    </message>
    
    <portType name="EmployeeServiceTopDown">
        <operation name="countEmployees">
            <input message="tns:countEmployees"/>
            <output message="tns:countEmployeesResponse"/>
        </operation>
    </portType>
    
    <binding name="EmployeeServiceTopDownSOAP" type="tns:EmployeeServiceTopDown">
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
        <operation name="countEmployees">
            <soap:operation soapAction="http://topdown.server.jaxws.baeldung.com/EmployeeServiceTopDown/countEmployees"/>
            <input><soap:body use="literal"/></input>
            <output><soap:body use="literal"/></output>
        </operation>
    </binding>
    
    <service name="EmployeeServiceTopDown">
        <port name="EmployeeServiceTopDownSOAP" binding="tns:EmployeeServiceTopDownSOAP">
            <soap:address location="http://localhost:8080/employeeservicetopdown"/>
        </port>
    </service>
</definitions>

5.2 生成 Java 代码

使用 JDK 内置的 wsimport 工具生成服务端代码:

wsimport -s . -p com.baeldung.jaxws.server.topdown employeeservicetopdown.wsdl

关键参数说明:

  • -p:指定目标包名
  • -s:指定源码输出目录

💡 替代方案:JDK 9+ 可使用 jaxws-maven-pluginmaven-jaxb2-plugin

生成文件清单:

  • EmployeeServiceTopDown.java:服务接口(SEI)
  • ObjectFactory.java:对象工厂类
  • EmployeeServiceTopDown_Service.java:服务提供者类

5.3 实现服务接口

生成的接口示例:

@WebService(
  name = "EmployeeServiceTopDown", 
  targetNamespace = "http://topdown.server.jaxws.baeldung.com/")
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
@XmlSeeAlso({ObjectFactory.class})
public interface EmployeeServiceTopDown {
    @WebMethod(
      action = "http://topdown.server.jaxws.baeldung.com/EmployeeServiceTopDown/countEmployees")
    @WebResult(
      name = "countEmployeesResponse", 
      targetNamespace = "http://topdown.server.jaxws.baeldung.com/", 
      partName = "parameters")
    public int countEmployees();
}

实现类示例:

@WebService(
  name = "EmployeeServiceTopDown", 
  endpointInterface = "com.baeldung.jaxws.server.topdown.EmployeeServiceTopDown",
  targetNamespace = "http://topdown.server.jaxws.baeldung.com/")
public class EmployeeServiceTopDownImpl implements EmployeeServiceTopDown {
 
    @Inject 
    private EmployeeRepository employeeRepositoryImpl;
 
    @WebMethod
    public int countEmployees() {
        return employeeRepositoryImpl.count();
    }
}

6. 自底向上开发实践

6.1 创建模型类

public class Employee {
    private int id;
    private String firstName;
    // 标准 getter/setter
}

6.2 定义服务接口

@WebService
public interface EmployeeService {
    @WebMethod Employee getEmployee(int id);
    @WebMethod Employee updateEmployee(int id, String name);
    @WebMethod boolean deleteEmployee(int id);
    @WebMethod Employee addEmployee(int id, String name);
}

注解说明:

  • @WebService:标记为 Web 服务接口
  • @WebMethod:声明服务操作
  • @WebResult:自定义返回值 XML 元素名

6.3 实现服务类

@WebService(endpointInterface = "com.baeldung.jaxws.EmployeeService")
public class EmployeeServiceImpl implements EmployeeService {
 
    @Inject 
    private EmployeeRepository employeeRepositoryImpl;

    @WebMethod
    public Employee getEmployee(int id) {
        return employeeRepositoryImpl.getEmployee(id);
    }

    // 其他方法实现...
}

7. 发布服务端点

通过 javax.xml.ws.Endpoint 发布服务:

public class EmployeeServicePublisher {
    public static void main(String[] args) {
        // 发布自顶向下服务
        Endpoint.publish(
          "http://localhost:8080/employeeservicetopdown", 
           new EmployeeServiceTopDownImpl());

        // 发布自底向上服务
        Endpoint.publish("http://localhost:8080/employeeservice", 
          new EmployeeServiceImpl());
    }
}

💡 生产建议:将服务打包为 WAR 部署到 WildFly/GlassFish 等应用服务器,以支持 CDI 等企业特性。

8. 远程客户端调用

8.1 生成客户端代码

使用 wsimport 生成客户端存根:

wsimport -keep -p com.baeldung.jaxws.client http://localhost:8080/employeeservice?wsdl

8.2 调用远程服务

public class EmployeeServiceClient {
    public static void main(String[] args) throws Exception {
        URL url = new URL("http://localhost:8080/employeeservice?wsdl");

        // 创建服务代理
        EmployeeService_Service service = new EmployeeService_Service(url);
        EmployeeService proxy = service.getEmployeeServiceImplPort();

        // 调用远程方法
        List<Employee> allEmployees = proxy.getAllEmployees();
    }
}

9. 总结

本文系统介绍了 JAX-WS 开发 SOAP Web 服务的核心知识:

  • 两种开发模式:自顶向下(契约优先)vs 自底向上(代码优先)
  • WSDL 结构解析:六大核心元素详解
  • 服务发布与调用:从开发到部署全流程
  • 客户端集成:远程服务调用实践

⚠️ 简单粗暴建议:新项目优先选择 RESTful 风格(如 JAX-RS),SOAP 仅用于需要强契约的企业级集成场景。

完整源码请参考 GitHub 仓库


原始标题:Introduction to JAX-WS