JAX-WS в Java: мощный инструмент для создания SOAP веб-сервисов
Для кого эта статья:
- Разработчики на Java, заинтересованные в освоении JAX-WS и SOAP-веб-сервисов
- Студенты и начинающие специалисты, изучающие веб-разработку и интеграционные технологии
Профессионалы, работающие в области разработки корпоративных систем и интеграции с устаревшими решениями
Технология JAX-WS открывает мощные возможности для создания интегрированных систем, позволяя Java-приложениям бесшовно общаться через SOAP-протокол. Многие разработчики избегают SOAP из-за кажущейся сложности, но на практике — это структурированный и надёжный способ обмена данными. Погрузимся в мир JAX-WS, где аннотации превращают обычные Java-классы в полноценные веб-сервисы, а автоматическая генерация WSDL экономит часы ручной работы. Готовы разобраться в этой технологии раз и навсегда? 🚀
Изучаете Java и хотите овладеть веб-сервисами на профессиональном уровне? Курс Java-разработки от Skypro включает не только фундаментальные основы JAX-WS, но и практику построения реальных проектов с использованием SOAP и REST-архитектур. Наши студенты создают рабочие веб-сервисы уже на 4 месяце обучения, а к выпуску имеют в портфолио полноценные распределенные системы. Станьте экспертом в интеграционных решениях!
Основы JAX-WS: архитектура и принципы работы
JAX-WS (Java API for XML Web Services) — это стандарт Java, обеспечивающий инструментарий для разработки веб-сервисов на основе XML. Технология входит в состав Jakarta EE (ранее Java EE) и представляет собой эволюцию предыдущего стандарта JAX-RPC.
Архитектурно JAX-WS основан на принципе "контракт-первый" (contract-first) или "реализация-первая" (implementation-first), позволяя разработчикам выбрать наиболее удобный подход. Основная цель JAX-WS — упростить процесс создания и использования веб-сервисов путем автоматизации большинства низкоуровневых задач.
Алексей Петров, Java-архитектор
Когда мне поручили интеграцию нашей системы с унаследованным банковским API через SOAP, я внутренне содрогнулся. Мой предыдущий опыт работы с XML-RPC оставил неприятные воспоминания о бесконечных XML-маппингах. Однако, переход на JAX-WS кардинально изменил ситуацию. Достаточно было разметить Java-интерфейс нужными аннотациями, и фреймворк автоматически сгенерировал всю WSDL-документацию, а также взял на себя сериализацию/десериализацию объектов. Что раньше занимало неделю работы, теперь делалось за пару часов. Особенно приятно было видеть удивление системных аналитиков, когда я доложил о готовности интеграции спустя всего день после получения требований.
Ключевые компоненты архитектуры JAX-WS включают:
- Endpoint — точка доступа, опубликованная сервером, доступная клиентам по URL
- Service — абстракция на стороне клиента, представляющая удаленный сервис
- Port — конкретная реализация сервиса
- WSDL — описание сервиса на языке XML, определяющее интерфейс
- SOAP — протокол обмена сообщениями, основанный на XML
Работа JAX-WS строится на концепции "контракта" — формализованного описания интерфейса сервиса, выраженного в WSDL-документе. Этот документ содержит полную информацию о доступных операциях, типах данных и протоколе взаимодействия.
| Компонент | Назначение | API/Интерфейс |
|---|---|---|
| Service Provider | Реализация бизнес-логики сервиса | @WebService, @WebMethod |
| Service Requester | Клиент, потребляющий сервис | Service, Dispatch |
| JAXB | Преобразование Java-объектов в XML и обратно | @XmlType, @XmlElement |
| Handler Framework | Обработка SOAP-сообщений | SOAPHandler, LogicalHandler |
| Metro Stack | Реализация спецификации, включая WS-* расширения | WSEndpoint, WSBinding |
JAX-WS использует два ключевых механизма:
- Аннотации: Позволяют превратить обычный Java-класс в веб-сервис без необходимости писать конфигурационные файлы
- Кодогенерация: Автоматически создает код клиента и/или сервера на основе WSDL-описания
Процесс взаимодействия в JAX-WS можно описать следующим образом: клиент формирует запрос, который преобразуется в SOAP-сообщение и передаётся по сети; сервер принимает сообщение, преобразует его в Java-объекты, обрабатывает и формирует ответ, который также передаётся клиенту в виде SOAP-сообщения.
Важным аспектом является поддержка JAX-WS стандартов WS-* (например, WS-Security, WS-ReliableMessaging), что делает его полноценным решением для корпоративных интеграционных задач. 💼

Создание SOAP веб-сервиса с JAX-WS аннотациями
Создание веб-сервиса с JAX-WS начинается с определения интерфейса и его реализации с использованием специальных аннотаций. Эти аннотации определяют, какие методы будут доступны как веб-сервисные операции, и как они должны быть представлены в WSDL-документе.
Рассмотрим процесс создания простого веб-сервиса для управления каталогом продуктов:
- Определение сервисного интерфейса
- Реализация бизнес-логики
- Настройка параметров сервиса с помощью аннотаций
- Создание объектов передачи данных
Сначала определим интерфейс сервиса:
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.WebParam;
@WebService(name = "ProductCatalog", targetNamespace = "http://products.example.org/")
public interface ProductCatalogService {
@WebMethod(operationName = "findProductById")
Product getProduct(@WebParam(name = "productId") String id);
@WebMethod
List<Product> searchProducts(@WebParam(name = "keywords") String keywords);
@WebMethod
boolean addProduct(@WebParam(name = "product") Product product);
@WebMethod
boolean updateProduct(@WebParam(name = "product") Product product);
}
Далее, реализуем этот интерфейс:
import javax.jws.WebService;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@WebService(
serviceName = "ProductCatalogService",
portName = "ProductCatalogPort",
endpointInterface = "org.example.products.ProductCatalogService",
targetNamespace = "http://products.example.org/"
)
public class ProductCatalogServiceImpl implements ProductCatalogService {
private Map<String, Product> products = new HashMap<>();
@Override
public Product getProduct(String id) {
return products.get(id);
}
@Override
public List<Product> searchProducts(String keywords) {
if (keywords == null || keywords.trim().isEmpty()) {
return new ArrayList<>(products.values());
}
String[] terms = keywords.toLowerCase().split("\\s+");
return products.values().stream()
.filter(p -> matchesSearch(p, terms))
.collect(Collectors.toList());
}
private boolean matchesSearch(Product p, String[] terms) {
String productText = (p.getName() + " " + p.getDescription()).toLowerCase();
for (String term : terms) {
if (productText.contains(term)) {
return true;
}
}
return false;
}
@Override
public boolean addProduct(Product product) {
if (products.containsKey(product.getId())) {
return false;
}
products.put(product.getId(), product);
return true;
}
@Override
public boolean updateProduct(Product product) {
if (!products.containsKey(product.getId())) {
return false;
}
products.put(product.getId(), product);
return true;
}
}
Теперь определим класс объекта передачи данных (DTO):
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Product", propOrder = {
"id",
"name",
"description",
"price",
"stockQuantity"
})
public class Product {
@XmlElement(required = true)
private String id;
@XmlElement(required = true)
private String name;
@XmlElement
private String description;
@XmlElement
private double price;
@XmlElement
private int stockQuantity;
// Геттеры и сеттеры
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public double getPrice() { return price; }
public void setPrice(double price) { this.price = price; }
public int getStockQuantity() { return stockQuantity; }
public void setStockQuantity(int stockQuantity) { this.stockQuantity = stockQuantity; }
}
Ключевые аннотации JAX-WS, которые использовались в примере:
| Аннотация | Назначение | Уровень применения |
|---|---|---|
| @WebService | Определяет класс как веб-сервис | Класс, интерфейс |
| @WebMethod | Объявляет метод как операцию веб-сервиса | Метод |
| @WebParam | Настраивает параметры метода | Параметр метода |
| @XmlAccessorType | Указывает, как JAXB должен получать доступ к полям/свойствам | Класс |
| @XmlType | Сопоставляет класс с XML-схемой типа | Класс |
| @XmlElement | Сопоставляет поле или свойство с элементом XML | Поле, метод |
Обратите внимание на атрибуты аннотаций, они позволяют тонко настроить генерацию WSDL-документа:
- name: определяет имя сервиса или операции в WSDL
- targetNamespace: задает пространство имен XML для сервиса
- serviceName: имя элемента
<service>в WSDL - portName: имя элемента
<port>в WSDL - endpointInterface: указывает полное имя интерфейса сервиса
При использовании подхода "реализация-первая", JAX-WS автоматически генерирует WSDL из аннотированных классов. Альтернативный подход — "контракт-первый", когда WSDL создается вручную, а классы генерируются из него с помощью инструмента wsimport. 📝
Развертывание и тестирование веб-сервисов JAX-WS
После создания сервиса необходимо его развернуть и протестировать. JAX-WS предоставляет несколько способов публикации сервисов, от простых автономных решений до полноценного развертывания в корпоративных серверах приложений.
Марина Соколова, DevOps-инженер
В нашей компании мы перешли от монолитного приложения к микросервисной архитектуре. Одной из проблем стала интеграция новых микросервисов с унаследованной системой, использовавшей SOAP. Вместо переписывания всего старого кода, мы решили создать JAX-WS адаптеры. Первоначальное развертывание было проблематичным — мы пытались использовать сложную конфигурацию в контейнерах Docker. Но затем мы обнаружили, что можно запускать Endpoint.publish() прямо в коде Spring Boot приложения, без необходимости отдельного контейнера сервлетов. Это сократило время сборки и развертывания с 15 минут до 2, а количество конфигурационных файлов — с десятков до буквально пары строк в application.properties. Кроме того, это позволило нам использовать встроенные инструменты мониторинга и трассировки Spring Boot.
Рассмотрим основные способы развертывания JAX-WS сервисов:
1. Автономное развертывание
Самый простой способ опубликовать JAX-WS сервис — использовать класс Endpoint из пакета javax.xml.ws. Этот метод идеально подходит для тестирования или простых приложений:
import javax.xml.ws.Endpoint;
public class ProductCatalogPublisher {
public static void main(String[] args) {
// Создаем экземпляр нашей реализации сервиса
ProductCatalogServiceImpl catalogService = new ProductCatalogServiceImpl();
// Публикуем сервис по указанному адресу
String address = "http://localhost:8080/productcatalog";
Endpoint endpoint = Endpoint.publish(address, catalogService);
System.out.println("Service published at: " + address);
System.out.println("WSDL available at: " + address + "?wsdl");
// Для остановки сервиса в консольном приложении
System.out.println("Press any key to stop the service...");
try {
System.in.read();
} catch (Exception e) {
e.printStackTrace();
}
endpoint.stop();
System.out.println("Service stopped.");
}
}
2. Развертывание на сервере приложений
Для продакшн-среды рекомендуется развертывание на полноценном сервере приложений (Tomcat, GlassFish, WildFly и др.). В этом случае сервис упаковывается в WAR-файл с соответствующей конфигурацией.
Структура WAR-файла для JAX-WS сервиса:
myservice.war/
├── META-INF/
│ └── MANIFEST.MF
├── WEB-INF/
│ ├── web.xml
│ ├── classes/
│ │ └── org/
│ │ └── example/
│ │ ├── products/
│ │ │ ├── Product.class
│ │ │ ├── ProductCatalogService.class
│ │ │ └── ProductCatalogServiceImpl.class
│ │ └── ... (другие классы)
│ └── lib/
│ └── ... (зависимости, если не предоставляются сервером)
└── ... (статические ресурсы, если есть)
Пример web.xml для JAX-WS сервиса:
<?xml version="1.0" encoding="UTF-8"?>
<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">
<display-name>Product Catalog Service</display-name>
<listener>
<listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
</listener>
<servlet>
<servlet-name>ProductCatalogService</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>ProductCatalogService</servlet-name>
<url-pattern>/productcatalog</url-pattern>
</servlet-mapping>
</web-app>
Также необходим файл sun-jaxws.xml в WEB-INF:
<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0">
<endpoint
name="ProductCatalogService"
implementation="org.example.products.ProductCatalogServiceImpl"
url-pattern="/productcatalog"
/>
</endpoints>
3. Spring Boot интеграция
Для современных приложений на Spring Boot можно интегрировать JAX-WS через библиотеку cxf-spring-boot-starter:
<!-- pom.xml (фрагмент) -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>3.5.0</version>
</dependency>
// WebServiceConfig.java
@Configuration
@EnableWs
public class WebServiceConfig {
@Bean
public Endpoint productCatalogEndpoint() {
EndpointImpl endpoint = new EndpointImpl(
bus, new ProductCatalogServiceImpl());
endpoint.publish("/productcatalog");
return endpoint;
}
}
Тестирование веб-сервисов
После развертывания необходимо убедиться в работоспособности сервиса. Существует несколько подходов к тестированию JAX-WS сервисов:
- Проверка WSDL-документа — открыть URL с параметром ?wsdl в браузере и убедиться, что WSDL-документ генерируется корректно
- Инструменты тестирования веб-сервисов — использовать специализированные инструменты, такие как SoapUI или Postman
- Автоматическое тестирование — создание юнит-тестов с использованием клиентской библиотеки JAX-WS
Пример тестирования с помощью SoapUI:
- Создайте новый SOAP проект в SoapUI, указав URL WSDL (например, http://localhost:8080/productcatalog?wsdl)
- SoapUI автоматически создаст тестовые запросы для всех операций сервиса
- Заполните параметры запроса и нажмите кнопку "Execute"
- Проверьте полученный ответ
Пример автоматического тестирования с JUnit:
@Test
public void testGetProduct() {
// Создаем клиента веб-сервиса
URL wsdlUrl = new URL("http://localhost:8080/productcatalog?wsdl");
QName serviceName = new QName("http://products.example.org/", "ProductCatalogService");
ProductCatalogService service = new ProductCatalogService(wsdlUrl, serviceName);
ProductCatalog port = service.getProductCatalogPort();
// Подготавливаем тестовые данные
Product testProduct = new Product();
testProduct.setId("test123");
testProduct.setName("Test Product");
testProduct.setPrice(19.99);
// Добавляем продукт и проверяем результат
boolean addResult = port.addProduct(testProduct);
assertTrue("Failed to add product", addResult);
// Получаем продукт и проверяем данные
Product retrievedProduct = port.getProduct("test123");
assertNotNull("Product not found", retrievedProduct);
assertEquals("Product ID mismatch", "test123", retrievedProduct.getId());
assertEquals("Product name mismatch", "Test Product", retrievedProduct.getName());
assertEquals("Product price mismatch", 19.99, retrievedProduct.getPrice(), 0.001);
}
Такой многоуровневый подход к тестированию гарантирует надежность сервиса перед выпуском в продакшн-среду. 🧪
Разработка клиентского приложения для вызова сервиса
Создание клиента для JAX-WS сервиса можно реализовать несколькими способами, в зависимости от требований проекта и предпочтений разработчика. Каждый подход имеет свои преимущества и особенности.
1. Генерация клиентских классов с помощью wsimport
Инструмент wsimport, входящий в состав JDK до версии 8 (а затем перенесенный в отдельный модуль), позволяет автоматически создать Java-классы на основе WSDL-документа:
wsimport -keep -p org.example.client http://localhost:8080/productcatalog?wsdl
Параметры команды:
- -keep — сохранить генерируемые Java-исходники
- -p — указать пакет для генерируемых классов
После генерации вы получите набор классов для работы с сервисом:
// Использование сгенерированных классов
import org.example.client.*;
public class ProductCatalogClient {
public static void main(String[] args) {
// Создание сервиса
ProductCatalogService service = new ProductCatalogService();
// Получение порта для доступа к операциям
ProductCatalog catalog = service.getProductCatalogPort();
// Вызов операции сервиса
Product product = catalog.getProduct("123");
System.out.println("Product name: " + product.getName());
// Поиск продуктов
List<Product> searchResults = catalog.searchProducts("laptop");
System.out.println("Found " + searchResults.size() + " products");
// Добавление нового продукта
Product newProduct = new Product();
newProduct.setId("456");
newProduct.setName("New Smartphone");
newProduct.setDescription("Latest model with advanced features");
newProduct.setPrice(599.99);
newProduct.setStockQuantity(100);
boolean success = catalog.addProduct(newProduct);
System.out.println("Product added: " + success);
}
}
2. Динамический клиент без генерации кода
JAX-WS позволяет создать клиента без предварительной генерации классов, используя динамический прокси:
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import java.net.URL;
public class DynamicClient {
public static void main(String[] args) throws Exception {
URL wsdlUrl = new URL("http://localhost:8080/productcatalog?wsdl");
// QName для сервиса и порта
QName serviceQName = new QName("http://products.example.org/", "ProductCatalogService");
QName portQName = new QName("http://products.example.org/", "ProductCatalogPort");
// Создание сервиса
Service service = Service.create(wsdlUrl, serviceQName);
// Получение прокси-объекта для интерфейса
ProductCatalogService catalog = service.getPort(portQName,
ProductCatalogService.class);
// Теперь можно вызывать методы
Product product = catalog.getProduct("123");
System.out.println("Product: " + product.getName());
}
}
Этот подход требует, чтобы интерфейс ProductCatalogService был определен вручную и соответствовал контракту сервиса.
3. Использование Dispatch для низкоуровневого доступа
Для случаев, когда требуется полный контроль над SOAP-сообщениями, можно использовать класс Dispatch:
import javax.xml.namespace.QName;
import javax.xml.soap.*;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;
import java.net.URL;
public class LowLevelClient {
public static void main(String[] args) throws Exception {
// Настройка сервиса
URL wsdlUrl = new URL("http://localhost:8080/productcatalog?wsdl");
QName serviceQName = new QName("http://products.example.org/", "ProductCatalogService");
QName portQName = new QName("http://products.example.org/", "ProductCatalogPort");
Service service = Service.create(wsdlUrl, serviceQName);
// Создание Dispatch для работы с SOAP-сообщениями
Dispatch<SOAPMessage> dispatch = service.createDispatch(
portQName, SOAPMessage.class, Service.Mode.MESSAGE);
// Создание SOAP-сообщения
MessageFactory factory = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
SOAPMessage message = factory.createMessage();
SOAPPart part = message.getSOAPPart();
SOAPEnvelope envelope = part.getEnvelope();
SOAPBody body = envelope.getBody();
// Создание элемента запроса
QName getProductQName = new QName("http://products.example.org/", "findProductById");
SOAPElement operation = body.addChildElement(getProductQName);
// Добавление параметра
QName productIdQName = new QName("productId");
SOAPElement param = operation.addChildElement(productIdQName);
param.addTextNode("123");
// Отправка сообщения и получение ответа
SOAPMessage response = dispatch.invoke(message);
// Обработка ответа
SOAPBody responseBody = response.getSOAPBody();
// Извлечение данных из ответа
// ...
System.out.println("Response received.");
}
}
4. Spring Boot и Spring WebServices
Для Spring Boot приложений удобно использовать Spring WS для создания клиентов:
@Configuration
public class WebServiceConfig {
@Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("org.example.client");
return marshaller;
}
@Bean
public ProductCatalogClient productCatalogClient(Jaxb2Marshaller marshaller) {
ProductCatalogClient client = new ProductCatalogClient();
client.setDefaultUri("http://localhost:8080/productcatalog");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
}
public class ProductCatalogClient extends WebServiceGatewaySupport {
public Product getProduct(String id) {
GetProductRequest request = new GetProductRequest();
request.setProductId(id);
GetProductResponse response = (GetProductResponse) getWebServiceTemplate()
.marshalSendAndReceive(request);
return response.getProduct();
}
// Другие методы для работы с сервисом
}
| Подход | Преимущества | Недостатки | Рекомендуется для |
|---|---|---|---|
| wsimport | Типобезопасность, автоматическая генерация, простота использования | Генерируемый код может быть избыточным, требует запуска дополнительного инструмента | Большинства случаев, особенно при работе с внешними сервисами |
| Динамический прокси | Нет необходимости в генерации кода, более гибкий | Требует ручного создания интерфейса, менее типобезопасный | Быстрого прототипирования, внутренних сервисов |
| Dispatch | Полный контроль над сообщениями, поддержка сложных сценариев | Сложный в использовании, требует знания XML/SOAP | Специфических случаев, требующих доступа к заголовкам SOAP |
| Spring WS | Интеграция со Spring, поддержка шаблонов и AOP | Зависимость от Spring, дополнительный уровень абстракции | Spring Boot приложений, корпоративных систем |
Независимо от выбранного подхода, важно учитывать обработку ошибок, таймауты и другие аспекты надежного взаимодействия с удаленными сервисами. Для этого JAX-WS предоставляет механизм обработчиков (handlers) и поддержку различных WS-* расширений. 🔄
Практические сценарии использования JAX-WS в Java
JAX-WS находит применение в широком спектре сценариев, от традиционных корпоративных интеграций до специализированных задач. Рассмотрим наиболее распространенные и практически полезные случаи использования.
1. Интеграция с корпоративными системами
Один из наиболее распространенных сценариев — интеграция с устаревшими корпоративными системами, которые поддерживают только SOAP:
@WebService
public interface ERPIntegrationService {
@WebMethod
OrderStatus submitOrder(@WebParam(name = "order") PurchaseOrder order);
@WebMethod
InventoryStatus checkInventory(@WebParam(name = "productId") String productId);
@WebMethod
CustomerInfo getCustomerDetails(@WebParam(name = "customerId") String customerId);
}
// Реализация, которая взаимодействует с ERP через проприетарный API
@WebService(endpointInterface = "com.example.erp.ERPIntegrationService")
public class ERPIntegrationServiceImpl implements ERPIntegrationService {
private ERPConnector erpConnector;
public ERPIntegrationServiceImpl() {
this.erpConnector = new ERPConnector("config/erp-connection.xml");
}
@Override
public OrderStatus submitOrder(PurchaseOrder order) {
// Преобразование в формат ERP
com.erp.vendor.Order erpOrder = convertToERPOrder(order);
// Отправка в ERP
String erpReference = erpConnector.submitOrder(erpOrder);
// Возврат статуса
OrderStatus status = new OrderStatus();
status.setAccepted(true);
status.setOrderReference(erpReference);
status.setEstimatedDelivery(calculateDeliveryDate());
return status;
}
// Другие методы...
}
2. Обеспечение безопасного обмена данными
JAX-WS поддерживает WS-Security, что позволяет реализовать защищенный обмен данными:
// Настройка безопасности на стороне сервера
@WebService
@SecurityDomain("other")
@RolesAllowed({"admin", "manager"})
public class SecureFinancialService implements FinancialOperations {
@WebMethod
@RolesAllowed("admin")
public TransactionResult transferFunds(
@WebParam(name = "sourceAccount") String sourceAccount,
@WebParam(name = "targetAccount") String targetAccount,
@WebParam(name = "amount") BigDecimal amount) {
// Логика перевода средств
// ...
}
@WebMethod
@RolesAllowed({"admin", "manager"})
public AccountBalance checkBalance(
@WebParam(name = "accountNumber") String accountNumber) {
// Проверка баланса
// ...
}
}
// Настройка клиента с безопасностью
public class SecureClient {
public static void main(String[] args) throws Exception {
// Создание фабрики контекста безопасности
LoginContext lc = new LoginContext("client-login", new UsernamePasswordCallbackHandler("admin", "password"));
lc.login();
try {
Subject.doAs(lc.getSubject(), new PrivilegedExceptionAction<Void>() {
public Void run() throws Exception {
// Создание клиента веб-сервиса
URL wsdlUrl = new URL("https://secure-server:8443/financial?wsdl");
FinancialService service = new FinancialService(wsdlUrl);
FinancialOperations port = service.getFinancialOperationsPort();
// Настройка SSL
BindingProvider bp = (BindingProvider) port;
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"https://secure-server:8443/financial");
// Вызов защищенного метода
AccountBalance balance = port.checkBalance("1234567890");
System.out.println("Balance: " + balance.getAmount());
return null;
}
});
} finally {
lc.logout();
}
}
}
3. Обработка асинхронных запросов
JAX-WS поддерживает асинхронные вызовы, что особенно полезно для долговременных операций:
// Клиентский код для асинхронного вызова
public class AsyncClient {
public static void main(String[] args) throws Exception {
URL wsdlUrl = new URL("http://localhost:8080/reports?wsdl");
ReportService service = new ReportService(wsdlUrl);
// Получение порта
ReportGenerator asyncPort = service.getReportGeneratorPort();
// Создание обработчика асинхронного ответа
AsyncHandler<GenerateReportResponse> handler = new AsyncHandler<GenerateReportResponse>() {
@Override
public void handleResponse(Response<GenerateReportResponse> res) {
try {
GenerateReportResponse response = res.get();
Report report = response.getReturn();
System.out.println("Report received asynchronously: " + report.getTitle());
// Обработка отчета
} catch (Exception e) {
e.printStackTrace();
}
}
};
// Асинхронный вызов с обработчиком
ReportCriteria criteria = new ReportCriteria();
criteria.setReportType("ANNUAL_SALES");
criteria.setYear(2023);
Future<?> response = asyncPort.generateReportAsync(criteria, handler);
System.out.println("Request sent, continuing with other operations...");
// Продолжение выполнения других задач, пока отчет генерируется
for (int i = 0; i < 5; i++) {
System.out.println("Doing other work...");
Thread.sleep(1000);
}
// Ожидание завершения асинхронной операции (если нужно)
if (!response.isDone()) {
System.out.println("Waiting for report to complete...");
response.get(); // Блокирующий вызов
}
}
}
4. Взаимодействие между разными платформами
JAX-WS обеспечивает интероперабельность между Java и другими платформами, такими как .NET:
// Java-сервис, который будет вызываться из .NET
@WebService(targetNamespace = "http://interop.example.org/")
public class InteropService {
@WebMethod
public CustomerData getCustomerById(@WebParam(name = "customerId") String id) {
CustomerData customer = new CustomerData();
customer.setId(id);
customer.setName("John Doe");
customer.setEmail("john.doe@example.com");
return customer;
}
@WebMethod
public ProductDetails[] getCompatibleProducts(
@WebParam(name = "platform") String platform) {
// Возвращаем список продуктов, совместимых с указанной платформой
// ...
}
}
// Класс передачи данных, адаптированный для интероперабельности
@XmlType(namespace = "http://interop.example.org/")
public class CustomerData {
private String id;
private String name;
private String email;
// Стандартные геттеры и сеттеры...
}
5. Расширение функциональности с использованием обработчиков
Обработчики позволяют реализовать сквозную функциональность, такую как логирование, мониторинг или трансформацию сообщений:
// SOAP-обработчик для логирования запросов и ответов
@SoapMessageHandlers({
@SoapMessageHandler(LoggingHandler.class)
})
@WebService(endpointInterface = "com.example.analytics.AnalyticsService")
public class AnalyticsServiceImpl implements AnalyticsService {
// Реализация сервиса...
}
// Реализация обработчика
public class LoggingHandler implements SOAPHandler<SOAPMessageContext> {
@Override
public boolean handleMessage(SOAPMessageContext context) {
Boolean outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
try {
SOAPMessage message = context.getMessage();
ByteArrayOutputStream out = new ByteArrayOutputStream();
message.writeTo(out);
System.out.println("SOAP " + (outbound ? "Response:" : "Request:"));
System.out.println(out.toString());
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
@Override
public boolean handleFault(SOAPMessageContext context) {
// Логирование ошибок
return true;
}
@Override
public void close(MessageContext context) {
// Освобождение ресурсов
}
@Override
public Set<QName> getHeaders() {
// Заголовки, которые нас интересуют
return null;
}
}
Преимущества и недостатки JAX-WS в различных сценариях:
- Преимущества:
- Высокая интероперабельность между разными платформами и языками
- Стандартизация на уровне WS-* спецификаций
- Мощные возможности по безопасности, транзакционности и надежности
Хорошая поддержка в корпоративных средах
- Недостатки:
- Более сложная настройка по сравнению с REST
- Больший размер сообщений из-за XML-обертки
- Менее производительный в типичных сценариях
- Крутая кривая обучения для начинающих
Несмотря на рост популярности REST-сервисов, JAX-WS остается ценным инструментом в арсенале Java-разработчика, особенно для корпоративных интеграционных проектов, требующих высокого уровня стандартизации, безопасности и надежности доставки сообщений. 🏢
JAX-WS предоставляет мощный инструментарий для создания полнофункциональных веб-сервисов в экосистеме Java. Овладев этой технологией, вы получаете возможность интегрировать практически любые системы, от современных микросервисов до устаревших корпоративных решений. Главная сила JAX-WS заключается в стандартизации, безопасности и гибкости — качествах, которые остаются критически важными для серьезных промышленных проектов. Возможно, в мире доминируют REST API, но знание SOAP и JAX-WS по-прежнему открывает двери в сферу корпоративной интеграции, банковских систем и государственных проектов.