«1. Обзор

Java API для веб-служб XML (JAX-WS) — это стандартизированный API для создания и использования веб-служб SOAP (Simple Object Access Protocol).

В этой статье мы создадим веб-службу SOAP и подключимся к ней с помощью JAX-WS.

2. SOAP

SOAP — это спецификация XML для отправки сообщений по сети. Сообщения SOAP не зависят от какой-либо операционной системы и могут использовать различные протоколы связи, включая HTTP и SMTP.

SOAP тяжеловесен для XML, поэтому его лучше всего использовать с инструментами/фреймворками. JAX-WS — это фреймворк, упрощающий использование SOAP. Это часть стандартной Java.

3. Сверху вниз и снизу вверх

Существует два способа создания веб-сервисов SOAP. Мы можем использовать подход «сверху вниз» или подход «снизу вверх».

При нисходящем подходе (сначала контракт) создается документ WSDL, а необходимые классы Java генерируются из WSDL. При подходе «снизу вверх» (последний контракт) классы Java записываются, а WSDL генерируется из классов Java.

Написание файла WSDL может быть довольно сложным в зависимости от сложности вашего веб-сервиса. Это делает подход «снизу вверх» более простым вариантом. С другой стороны, поскольку ваш WSDL генерируется из классов Java, любое изменение в коде может привести к изменению WSDL. Это не относится к нисходящему подходу.

В этой статье мы рассмотрим оба подхода.

4. Язык определения веб-сервисов (WSDL)

WSDL — это контрактное определение доступных сервисов. Это спецификация сообщений ввода/вывода и того, как вызывать веб-службу. Он не зависит от языка и определен в XML.

Давайте рассмотрим основные элементы документа WSDL.

4.1. Определения

Элемент определения является корневым элементом всех документов WSDL. Он определяет имя, пространство имен и т.д. сервиса и, как видите, может быть довольно просторным:

<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 определяет типы данных, используемые веб-службой. WSDL использует XSD (определение схемы XML) в качестве системы типов, которая способствует взаимодействию:

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

4.3. Сообщения

Элемент сообщения обеспечивает абстрактное определение передаваемых данных. Каждый элемент сообщения описывает ввод или вывод метода службы и возможные исключения:

<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 описывает каждую операцию, которая может быть выполнена, и все задействованные элементы сообщения. Например, операция getEmployee определяет входные данные запроса, выходные данные и возможное исключение ошибки, выдаваемое операцией веб-службы:

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

4.5. Привязки

Элемент привязки предоставляет подробную информацию о протоколе и формате данных для каждого типа порта:

<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. Службы и порты

Элемент службы определяет порты, поддерживаемые веб-службой. Элемент port в сервисе определяет имя, привязку и адрес сервиса:

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

5. Подход «сверху вниз» (сначала контракт)

Давайте начнем с подхода «сверху вниз», создав файл WSDL. служащийservicetopdown.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>
    <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.1. Создание исходных файлов веб-службы из WSDL

Существует несколько способов создания исходных файлов веб-службы из документа WSDL.

Один из способов — использовать инструмент wsimport, который является частью JDK (в $JAVA_HOME/bin) до JDK 8.

Из командной строки:

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

Используемые параметры командной строки: -p указывает целевой пакет. -s указывает, куда поместить сгенерированные исходные файлы.

Для более поздних версий JDK мы можем использовать jaxws-maven-plugin от MojoHaus, как описано здесь.

В качестве альтернативы может пригодиться плагин org.jvnet.jaxb2 maven-jaxb2-plugin, как подробно описано в разделе Вызов веб-службы SOAP в Spring.

Сгенерированные файлы:

    EmployeeServiceTopDown.java — интерфейс конечной точки службы (SEI), который содержит определения методов ObjectFactory.java — содержит фабричные методы для программного создания экземпляров классов, производных от схемы, EmployeeServiceTopDown_Service.java — « — это класс поставщика услуг, который может использоваться клиентом JAX-WS

«5.2. Интерфейс конечной точки веб-службы

Инструмент wsimport сгенерировал интерфейс конечной точки веб-службы EmployeeServiceTopDown. Он объявляет методы веб-сервиса:

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

5.3. Реализация веб-службы

Инструмент wsimport создал структуру веб-службы. Мы должны создать реализацию веб-сервиса:

@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. Подход «снизу вверх» (последний контракт)

В подходе «снизу вверх» мы должны создать как интерфейс конечной точки, так и классы реализации. . WSDL создается из классов при публикации веб-службы.

Давайте создадим веб-сервис, который будет выполнять простые CRUD-операции с данными сотрудников.

6.1. Класс модели

Класс модели Employee:

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

    // standard getters and setters
}

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 означает, что это интерфейс веб-службы. @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);
    }

    @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. Публикация конечных точек веб-службы

Чтобы опубликовать веб-службы (сверху вниз и снизу вверх), нам нужно передать адрес и экземпляр реализации веб-службы в метод publish() класса 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());
    }
}

Теперь мы можем запустить EmployeeServicePublisher для запуска веб-службы. Чтобы использовать функции CDI, веб-службы можно развернуть в виде файла WAR на серверах приложений, таких как WildFly или GlassFish.

8. Клиент удаленной веб-службы

Давайте теперь создадим клиент JAX-WS для удаленного подключения к веб-службе EmployeeService.

8.1. Генерация клиентских артефактов

Для генерации клиентских артефактов JAX-WS мы снова можем использовать инструмент wsimport:

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

Сгенерированный класс EmployeeService_Service инкапсулирует логику для получения порта сервера с использованием URL и QName.

8.2. Подключение к веб-службе

Клиент веб-службы использует сгенерированный сервис EmployeeService_Service для подключения к серверу и удаленного вызова веб-службы:

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. Заключение

Эта статья представляет собой краткое введение в веб-службы SOAP. с помощью JAX-WS.

Мы использовали как восходящий, так и нисходящий подходы к созданию веб-служб SOAP с использованием JAX-WS API. Мы также написали клиент JAX-WS, который может удаленно подключаться к серверу и выполнять вызовы веб-службы.

Полный исходный код доступен на GitHub.