JAX-WS简介

2025/04/08

1. 概述

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

在本文中,我们将创建一个SOAP Web服务并使用JAX-WS连接到它。

2. SOAP

SOAP是一种通过网络发送消息的XML规范,SOAP消息独立于任何操作系统,并且可以使用多种通信协议,包括HTTP和SMTP。

SOAP是XML密集型的,因此最好与工具/框架一起使用。JAX-WS是一个简化SOAP使用的框架,它是标准Java的一部分。

3. 自上而下与自下而上

构建SOAP Web服务有两种方法,我们可以采用自上而下的方法,也可以采用自下而上的方法。

在自上而下(契约优先)方法中,创建WSDL文档,并从WSDL生成必要的Java类。在自下而上(契约最后)方法中,编写Java类,并从Java类生成WSDL。

编写WSDL文件可能非常困难,具体取决于你的Web服务的复杂程度,这使得自下而上的方法成为一种更简单的选择。另一方面,由于你的WSDL是从Java类生成的,因此代码中的任何更改都可能导致WSDL发生变化,自上而下的方法并非如此。

在本文中,我们将介绍这两种方法。

4. Web服务定义语言(WSDL)

WSDL是可用服务的契约定义,它是输入/输出消息以及如何调用Web服务的规范,它与语言无关,并以XML定义。

让我们看一下WSDL文档的主要元素。

4.1 定义

definitions元素是所有WSDL文档的根元素,它定义服务的名称、命名空间等:

<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
             xmlns:tns="http://jaxws.tuyucheng.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.tuyucheng.com/"
             name="EmployeeService">
    ...
</definitions>

4.2 类型

types元素定义Web服务使用的数据类型,WSDL使用XSD(XML模式定义)作为类型系统,有助于实现互操作性:

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

4.3 消息

message元素提供了正在传输的数据的抽象定义,每个message元素描述服务方法的输入或输出以及可能出现的异常:

<definitions ...>
    ...
    <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>
    ...
</definitions>

4.4 操作和端口类型

portType元素描述了可以执行的每个操作以及所涉及的所有message元素。例如,getEmployee操作指定了请求的输入、输出以及Web服务操作可能引发的故障异常:

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

4.5 绑定

binding元素为每个portType提供协议和数据格式的详细信息:

<definitions ...>
    ...
    <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>
    ...
</definitions>

4.6 服务和端口

service元素定义了web服务支持的端口,service中的port元素定义了服务的名称、绑定和地址:

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

5. 自上而下(契约优先)方法

让我们从自上而下的方法开始,创建一个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.tuyucheng.com/"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns="http://schemas.xmlsoap.org/wsdl/"
        targetNamespace="http://topdown.server.jaxws.tuyucheng.com/"
        qname="EmployeeServiceTopDown">
    <types>
        <xsd:schema
                targetNamespace="http://topdown.server.jaxws.tuyucheng.com/">
            <xsd:element name="countEmployeesResponse" type="xsd:int"/>
        </xsd:schema>
    </types>

    <message name="countEmployees">
    </message>
    <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.tuyucheng.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.1 从WSDL生成Web服务源文件

有多种方法可以从WSDL文档生成Web服务源文件。

一种方法是使用wsimport工具,它是JDK的一部分(位于$JAVA_HOME/bin)。

从命令提示符中:

wsimport -s . -p cn.tuyucheng.taketoday.jaxws.server.topdown employeeservicetopdown.wsdl

使用的命令行选项:-p指定目标包,-s指定放置生成的源文件的位置。

对于更高版本的JDK,我们可以使用MojoHaus的jaxws-maven-plugin,如此处所述。

另外,org.jvnet.jaxb2的maven-jaxb2-plugin也可以派上用场,详情请参阅在Spring中调用SOAP Web服务

生成的文件:

  • EmployeeServiceTopDown.java:是包含方法定义的服务端点接口(SEI)
  • ObjectFactory.java:包含工厂方法,用于以编程方式创建模式派生类的实例
  • EmployeeServiceTopDown_Service.java:是JAX-WS客户端可以使用的服务提供者类

5.2 Web服务端点接口

wsimport工具已生成Web服务端点接口EmployeeServiceTopDown,它声明了以下Web服务方法:

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

5.3 Web服务实现

wsimport工具已创建Web服务的结构,我们必须创建Web服务的实现:

@WebService(
        name = "EmployeeServiceTopDown",
        endpointInterface = "cn.tuyucheng.taketoday.jaxws.server.topdown.EmployeeServiceTopDown",
        targetNamespace = "http://topdown.server.jaxws.tuyucheng.com/")
public class EmployeeServiceTopDownImpl
        implements EmployeeServiceTopDown {

    @Inject
    private EmployeeRepository employeeRepositoryImpl;

    @WebMethod
    public int countEmployees() {
        return employeeRepositoryImpl.count();
    }
}

6. 自下而上(契约最后)方法

在自下而上的方法中,我们必须创建端点接口和实现类。发布Web服务时,将从这些类生成WSDL。

让我们创建一个Web服务,对Employee数据执行简单的CRUD操作。

6.1 模型类

Employee模型类:

public class Employee {
    private int id;
    private String firstName;

    // standard getters and setters
}

6.2 Web服务端点接口

声明Web服务方法的Web服务端点接口:

@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);

    // ...
}

此接口定义了Web服务的抽象契约,使用的注解:

  • @WebService表示这是一个Web服务接口
  • @WebMethod用于定制Web服务操作
  • @WebResult用于自定义表示返回值的XML元素名称

6.3 Web服务实现

Web服务端点接口的实现类:

@WebService(endpointInterface = "cn.tuyucheng.taketoday.jaxws.EmployeeService")
public class EmployeeServiceImpl implements EmployeeService {

    @Inject
    private EmployeeRepository employeeRepositoryImpl;

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

    @WebMethod
    public Employee updateEmployee(int id, String name) {
        return employeeRepositoryImpl.updateEmployee(id, name);
    }

    @WebMethod
    public boolean deleteEmployee(int id) {
        return employeeRepositoryImpl.deleteEmployee(id);
    }

    @WebMethod
    public Employee addEmployee(int id, String name) {
        return employeeRepositoryImpl.addEmployee(id, name);
    }

    // ...
}

7. 发布Web服务端点

要发布Web服务(自上而下和自下而上),我们需要将Web服务实现的地址和实例传递给javax.xml.ws.Endpoint类的publish()方法:

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());
    }
}

现在我们可以运行EmployeeServicePublisher来启动Web服务,要利用CDI功能,可以将Web服务作为WAR文件部署到WildFly或GlassFish等应用服务器。

8. 远程Web服务客户端

现在让我们创建一个JAX-WS客户端来远程连接到EmployeeService Web服务。

8.1 生成客户端工件

为了生成JAX-WS客户端工件,我们可以再次使用wsimport工具:

wsimport -keep -p cn.tuyucheng.taketoday.jaxws.client http://localhost:8080/employeeservice?wsdl

生成的EmployeeService_Service类封装了使用URL和QName获取服务器端口的逻辑。

8.2 连接到Web服务

Web服务客户端使用生成的EmployeeService_Service连接到服务器并远程进行Web服务调用:

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

        EmployeeService_Service employeeService_Service = new EmployeeService_Service(url);
        EmployeeService employeeServiceProxy = employeeService_Service.getEmployeeServiceImplPort();

        List<Employee> allEmployees = employeeServiceProxy.getAllEmployees();
    }
}

9. 总结

本文简要介绍了使用JAX-WS的SOAP Web服务。

我们采用了自下而上和自上而下的方法来使用JAX-WS API创建SOAP Web服务,还编写了一个可以远程连接到服务器并进行Web服务调用的JAX-WS客户端。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章