1. Обзор
В этом руководстве основное внимание уделяется настройке и использованию среды Apache CXF вместе со Spring — либо с конфигурацией Java, либо с XML.
Это вторая статья из серии об Apache CXF; первый был посвящен основам CXF как реализации стандартных API-интерфейсов JAX-WS.
2. Зависимости Maven
Как и в предыдущем руководстве, необходимо включить следующие две зависимости:
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.1.6</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>3.1.6</version>
</dependency>
Последние версии артефактов Apache CXF см. на странице apache-cxf .
Кроме того, для поддержки Spring необходимы следующие зависимости:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.1.RELEASE</version>
</dependency>
Последние версии артефактов Spring можно найти здесь .
Наконец, поскольку мы будем программно настраивать приложение, используя API Java Servlet 3.0+ вместо традиционного дескриптора развертывания web.xml
, нам понадобится следующий артефакт:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
Здесь мы можем найти последнюю версию Servlet API.
3. Серверные компоненты
Давайте теперь посмотрим на компоненты, которые должны присутствовать на стороне сервера, чтобы опубликовать конечную точку веб-службы.
3.1. Интерфейс WebApplicationInitilizer
Интерфейс WebApplicationInitializer
реализован для программной настройки интерфейса ServletContext
для приложения. При наличии в пути к классам его метод onStartup
автоматически вызывается контейнером сервлета, после чего создается и инициализируется ServletContext .
Вот как определяется класс для реализации интерфейса WebApplicationInitializer
:
public class AppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) {
// Method implementation
}
}
Метод onStartup()
реализован с использованием фрагментов кода, показанных ниже.
Во-первых, контекст приложения Spring создается и настраивается для регистрации класса, содержащего метаданные конфигурации:
AnnotationConfigWebApplicationContext context
= new AnnotationConfigWebApplicationContext();
context.register(ServiceConfiguration.class);
Класс ServiceConfiguration
помечен аннотацией @Configuration
для предоставления определений bean-компонентов. Этот класс обсуждается в следующем подразделе.
В следующем фрагменте показано, как контекст приложения Spring добавляется в контекст сервлета:
container.addListener(new ContextLoaderListener(context));
Класс CXFServlet
, определенный Apache CXF, создается и регистрируется для обработки входящих запросов:
ServletRegistration.Dynamic dispatcher
= container.addServlet("dispatcher", new CXFServlet());
Контекст приложения загружает элементы Spring, определенные в файле конфигурации. В этом случае имя сервлета — cxf
, поэтому контекст ищет эти элементы в файле с именем cxf-servlet.xml
по умолчанию.
Наконец, сервлет CXF сопоставляется с относительным URL-адресом:
dispatcher.addMapping("/services");
3.2. Старый добрый web.xml
В качестве альтернативы, если мы хотим использовать (несколько старомодный) дескриптор развертывания, а не интерфейс WebApplicationInitilizer
, соответствующий файл web.xml
должен содержать следующие определения сервлетов:
<servlet>
<servlet-name>cxf</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>cxf</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
3.3. Класс конфигурации службы
Давайте теперь посмотрим на конфигурацию сервиса — сначала базовый скелет, который включает в себя определения bean-компонентов для конечной точки веб-сервиса:
@Configuration
public class ServiceConfiguration {
// Bean definitions
}
Первым необходимым bean-компонентом является SpringBus
, который предоставляет расширения для Apache CXF для работы с Spring Framework:
@Bean
public SpringBus springBus() {
return new SpringBus();
}
Также необходимо создать bean-компонент EnpointImpl с помощью bean -компонента
SpringBus и средства
реализации
веб-службы . Этот bean-компонент используется для публикации конечной точки по заданному HTTP-адресу:
@Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), new ForEachImpl());
endpoint.publish("http://localhost:8080/services/foreach");
return endpoint;
}
Класс ForEachImpl
используется для реализации интерфейса веб-службы. Его определение дано в следующем подразделе.
В качестве альтернативы мы также можем объявить конечную точку сервера в файле конфигурации XML. В частности, приведенный ниже файл cxf-servlet.xml
работает с дескриптором развертывания web.xml
, как это было определено в подразделе 3.1, и описывает точно такую же конечную точку:
<jaxws:endpoint
id="foreach"
implementor="com.foreach.cxf.spring.ForEachImpl"
address="http://localhost:8080/services/foreach" />
Обратите внимание, что файл конфигурации XML назван в честь имени сервлета, определенного в дескрипторе развертывания, то есть cxf
.
3.4. Определения типов
Далее — вот определение реализатора
, которое уже упоминалось в предыдущем подразделе:
@WebService(endpointInterface = "com.foreach.cxf.spring.ForEach")
public class ForEachImpl implements ForEach {
private int counter;
public String hello(String name) {
return "Hello " + name + "!";
}
public String register(Student student) {
counter++;
return student.getName() + " is registered student number " + counter;
}
}
Этот класс предоставляет реализацию интерфейса конечной точки ForEach
, которую Apache CXF включит в опубликованные метаданные WSDL:
@WebService
public interface ForEach {
String hello(String name);
String register(Student student);
}
И интерфейс конечной точки, и средство реализации
используют класс Student
, который определяется следующим образом:
public class Student {
private String name;
// constructors, getters and setters
}
4. Клиентские компоненты
Чтобы воспользоваться преимуществами Spring Framework, мы объявляем bean-компонент в аннотированном классе @Configuration
:
@Configuration
public class ClientConfiguration {
// Bean definitions
}
Определен bean-компонент с именем client
:
@Bean(name = "client")
public Object generateProxy() {
return proxyFactoryBean().create();
}
Клиентский компонент представляет собой прокси для веб
-службы ForEach .
Он создается вызовом метода create в bean-компоненте
JaxWsProxyFactoryBean
, фабрике для создания прокси-серверов JAX-WS.
``
Объект JaxWsProxyFactoryBean
создается и настраивается следующим методом:
@Bean
public JaxWsProxyFactoryBean proxyFactoryBean() {
JaxWsProxyFactoryBean proxyFactory = new JaxWsProxyFactoryBean();
proxyFactory.setServiceClass(ForEach.class);
proxyFactory.setAddress("http://localhost:8080/services/foreach");
return proxyFactory;
}
Свойство serviceClass
фабрики обозначает интерфейс веб-службы, а свойство address
указывает URL-адрес прокси-сервера для удаленных вызовов.
Также для компонентов Spring на стороне клиента можно вернуться к файлу конфигурации XML. Следующие элементы объявляют те же bean-компоненты, что и те, которые мы программно настроили выше:
<bean id="client" factory-bean="clientFactory" factory-method="create" />
<bean id="clientFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
<property name="serviceClass" value="com.foreach.cxf.spring.ForEach" />
<property name="address" value="http://localhost:8080/services/foreach" />
</bean>
5. Тестовые случаи
В этом разделе описываются тестовые примеры, используемые для иллюстрации поддержки Apache CXF для Spring. Тестовые случаи определены в классе StudentTest
.
Во-первых, нам нужно загрузить контекст приложения Spring из вышеупомянутого класса конфигурации ServiceConfiguration и кэшировать его в поле
контекста
:
private ApplicationContext context
= new AnnotationConfigApplicationContext(ClientConfiguration.class);
Затем объявляется прокси для интерфейса конечной точки службы и загружается из контекста приложения:
private ForEach foreachProxy = (ForEach) context.getBean("client");
Этот прокси-сервер ForEach
будет использоваться в тестовых примерах, описанных ниже.
В первом тестовом примере мы доказываем, что когда метод hello
вызывается локально на прокси-сервере, ответ точно такой же, как ответ, возвращаемый разработчиком конечной точки из
удаленной веб-службы:
@Test
public void whenUsingHelloMethod_thenCorrect() {
String response = foreachProxy.hello("John Doe");
assertEquals("Hello John Doe!", response);
}
Во втором примере студенты регистрируются на курсы ForEach, локально вызывая метод register
на прокси-сервере, который, в свою очередь, вызывает веб-службу. Затем эта удаленная служба рассчитает номера учащихся и вернет их вызывающему абоненту. Следующий фрагмент кода подтверждает наши ожидания:
@Test
public void whenUsingRegisterMethod_thenCorrect() {
Student student1 = new Student("Adam");
Student student2 = new Student("Eve");
String student1Response = foreachProxy.register(student1);
String student2Response = foreachProxy.register(student2);
assertEquals("Adam is registered student number 1", student1Response);
assertEquals("Eve is registered student number 2", student2Response);
}
6. Интеграционное тестирование
Для развертывания в качестве веб-приложения на сервере фрагменты кода в этом руководстве необходимо сначала упаковать в файл WAR. Этого можно добиться, объявив свойство упаковки
в файле POM:
<packaging>war</packaging>
Работа по упаковке реализована плагином Maven WAR:
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
Этот плагин упаковывает скомпилированный исходный код в файл WAR. Поскольку мы настраиваем контекст сервлета с помощью кода Java, традиционный дескриптор развертывания web.xml
не обязательно должен существовать. В результате для свойства failOnMissingWebXml
должно быть установлено значение false
, чтобы избежать сбоя при выполнении подключаемого модуля.
Мы можем перейти по этой ссылке для получения самой последней версии плагина Maven WAR.
Чтобы проиллюстрировать работу веб-сервиса, мы создаем интеграционный тест. Этот тест сначала создает файл WAR и запускает встроенный сервер, затем заставляет клиентов вызывать веб-службу, проверяет последующие ответы и, наконец, останавливает сервер.
Следующие плагины должны быть включены в файл Maven POM. Для получения более подробной информации ознакомьтесь с этим руководством по интеграционному тестированию .
Вот плагин Maven Surefire:
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<excludes>
<exclude>StudentTest.java</exclude>
</excludes>
</configuration>
</plugin>
Последнюю версию этого плагина можно найти здесь .
Раздел профиля
с идентификатором
интеграции объявлен для облегчения интеграционного
теста:
<profiles>
<profile>
<id>integration</id>
<build>
<plugins>
...
</plugins>
</build>
</profile>
</profiles>
Плагин Maven Cargo включен в профиль интеграции :
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>1.5.0</version>
<configuration>
<container>
<containerId>jetty9x</containerId>
<type>embedded</type>
</container>
<configuration>
<properties>
<cargo.hostname>localhost</cargo.hostname>
<cargo.servlet.port>8080</cargo.servlet.port>
</properties>
</configuration>
</configuration>
<executions>
<execution>
<id>start-server</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
</execution>
<execution>
<id>stop-server</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
Обратите внимание, что свойства конфигурации cargo.hostname
и cargo.servlet.port
включены просто для ясности. Эти свойства конфигурации могут быть опущены без какого-либо влияния на приложение, поскольку их значения совпадают со значениями по умолчанию. Этот плагин запускает сервер, ждет соединений и, наконец, останавливает сервер, чтобы освободить системные ресурсы.
Эта ссылка позволяет нам проверить последнюю версию плагина Maven Cargo.
Плагин Maven Surefire объявляется снова в профиле интеграции
, чтобы переопределить его конфигурацию в основном разделе сборки
и выполнить тестовые примеры, описанные в предыдущем разделе:
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<executions>
<execution>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<excludes>
<exclude>none</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
Теперь весь процесс можно запустить командой: mvn -Pintegration clean install
.
7. Заключение
В этом руководстве показана поддержка Apache CXF для Spring. В частности, было показано, как веб-служба может быть опубликована с использованием файла конфигурации Spring, и как клиент может взаимодействовать с этой службой через прокси-сервер, созданный фабрикой прокси-серверов Apache CXF, который был объявлен в другом файле конфигурации.
Реализацию всех этих примеров и фрагментов кода можно найти в связанном проекте GitHub .