Руководство по Apache CXF с Spring

Руководство по Apache CXF с Spring

1. обзор

В этом руководстве основное внимание уделяется настройке иusing the Apache CXF framework together with Spring - с конфигурацией Java или XML.

Это второй выпуск из серии об Apache CXF; the first one сосредоточен на основах CXF как реализации стандартных API JAX-WS.

2. Maven Зависимости

Как и в предыдущем уроке, необходимо включить следующие две зависимости:


    org.apache.cxf
    cxf-rt-frontend-jaxws
    3.1.6


    org.apache.cxf
    cxf-rt-transports-http
    3.1.6

Последние версии артефактов Apache CXF см. Вapache-cxf.

Кроме того, для поддержки Spring необходимы следующие зависимости:


    org.springframework
    spring-context
    4.3.1.RELEASE


    org.springframework
    spring-webmvc
    4.3.1.RELEASE

Последние версии артефактов Spring можно найтиhere.

Наконец, поскольку мы программно настроим приложение, используя API Java Servlet 3.0+ вместо традиционного дескриптора развертыванияweb.xml, нам понадобится артефакт, указанный ниже:


    javax.servlet
    javax.servlet-api
    3.1.0

This - это место, где мы можем найти последнюю версию 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 должен содержать следующие определения сервлетов:


    cxf
    org.apache.cxf.transport.servlet.CXFServlet
    1

    
    cxf
    /services/*

3.3. ServiceConfiguration Класс

Теперь давайте посмотрим на конфигурацию службы - сначала на базовый скелет, который включает определения bean-компонентов для конечной точки веб-службы:

@Configuration
public class ServiceConfiguration {
    // Bean definitions
}

Первый требуемый bean-компонент - этоSpringBus, который предоставляет расширения для Apache CXF для работы со Spring Framework:

@Bean
public SpringBus springBus() {
    return new SpringBus();
}

КомпонентEnpointImpl также должен быть создан с использованием bean-компонентаSpringBus и веб-службыimplementor. Этот компонент используется для публикации конечной точки по указанному HTTP-адресу:

@Bean
public Endpoint endpoint() {
    EndpointImpl endpoint = new EndpointImpl(springBus(), new exampleImpl());
    endpoint.publish("http://localhost:8080/services/example");
    return endpoint;
}

КлассexampleImpl используется для реализации интерфейса веб-службы. Его определение дано в следующем подразделе.

В качестве альтернативы мы также можем объявить конечную точку сервера в файле конфигурации XML. В частности, файлcxf-servlet.xml ниже работает с дескриптором развертыванияweb.xml, как было определено в подразделе 3.1, и описывает точно такую ​​же конечную точку:

Обратите внимание, что файл конфигурации XML назван в честь имени сервлета, определенного в дескрипторе развертывания, то естьcxf.

3.4. Определения типов

Далее - вот определениеimplementor, которое уже упоминалось в предыдущем подразделе:

@WebService(endpointInterface = "com.example.cxf.spring.example")
public class exampleImpl implements example {
    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;
    }
}

Этот класс обеспечивает реализацию интерфейса конечной точкиexample, который Apache CXF будет включать в опубликованные метаданные WSDL:

@WebService
public interface example {
    String hello(String name);
    String register(Student student);
}

И интерфейс конечной точки, иimplementor используют класс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();
}

Компонентclient представляет собой прокси для веб-службыexample. Он создается путем вызова методаcreate на bean-компонентеJaxWsProxyFactoryBean, фабрике по созданию прокси JAX-WS.

ОбъектJaxWsProxyFactoryBean создается и настраивается следующим способом:

@Bean
public JaxWsProxyFactoryBean proxyFactoryBean() {
    JaxWsProxyFactoryBean proxyFactory = new JaxWsProxyFactoryBean();
    proxyFactory.setServiceClass(example.class);
    proxyFactory.setAddress("http://localhost:8080/services/example");
    return proxyFactory;
}

СвойствоserviceClass фабрики обозначает интерфейс веб-службы, а свойствоaddress указывает URL-адрес, по которому прокси-сервер может выполнять удаленные вызовы.

Также для bean-компонентов Spring на стороне клиента можно вернуться к файлу конфигурации XML. Следующие элементы объявляют те же компоненты, что и те, которые мы только что программно настроили выше:



    
    

5. Тестовые случаи

В этом разделе описываются тестовые примеры, используемые для иллюстрации поддержки Apache CXF для Spring. Тестовые примеры определены в классе с именемStudentTest.

Во-первых, нам нужно загрузить контекст приложения Spring из вышеупомянутого класса конфигурацииServiceConfiguration и кэшировать его в полеcontext:

private ApplicationContext context
  = new AnnotationConfigApplicationContext(ClientConfiguration.class);

Затем прокси для интерфейса конечной точки службы объявляется и загружается из контекста приложения:

private example exampleProxy = (example) context.getBean("client");

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

В первом тестовом случае мы доказываем, что когда методhello вызывается локально на прокси, ответ в точности совпадает с тем, что конечная точкаimplementor возвращает из удаленной веб-службы:

@Test
public void whenUsingHelloMethod_thenCorrect() {
    String response = exampleProxy.hello("John Doe");
    assertEquals("Hello John Doe!", response);
}

Во втором тестовом примере студенты регистрируют, например, курсы, локально вызывая методregister на прокси-сервере, который, в свою очередь, вызывает веб-службу. Затем этот удаленный сервис рассчитает числа студентов и вернет их вызывающему абоненту. Следующий фрагмент кода подтверждает то, что мы ожидаем:

@Test
public void whenUsingRegisterMethod_thenCorrect() {
    Student student1 = new Student("Adam");
    Student student2 = new Student("Eve");
    String student1Response = exampleProxy.register(student1);
    String student2Response = exampleProxy.register(student2);

    assertEquals("Adam is registered student number 1", student1Response);
    assertEquals("Eve is registered student number 2", student2Response);
}

6. Интеграционное тестирование

Для развертывания в виде веб-приложения на сервере фрагменты кода в этом руководстве должны быть сначала упакованы в файл WAR. Этого можно добиться, объявив свойствоpackaging в файле POM:

war

Задание на упаковку выполняется плагином Maven WAR:


    maven-war-plugin
    2.6
    
        false
    

Этот плагин упаковывает скомпилированный исходный код в файл WAR. Поскольку мы настраиваем контекст сервлета с использованием кода Java, традиционный дескриптор развертыванияweb.xml может не существовать. В результате для свойстваfailOnMissingWebXml необходимо установить значениеfalse, чтобы избежать сбоя при выполнении плагина.

Мы можем следить заthis link для самой последней версии плагина Maven WAR.

Чтобы проиллюстрировать работу веб-службы, мы создаем интеграционный тест. Этот тест сначала генерирует файл WAR и запускает встроенный сервер, затем заставляет клиентов вызывать веб-службу, проверяет последующие ответы и, наконец, останавливает сервер.

Следующие плагины должны быть включены в файл Maven POM. Для более подробной информации, пожалуйста, посетитеthis Integration Testing tutorial.

Вот плагин Maven Surefire:


    maven-surefire-plugin
    2.19.1
    
        
            StudentTest.java
        
    

Последнюю версию этого плагина можно найтиhere.

Разделprofile сidintegration объявлен для облегчения теста интеграции:


   
      integration
      
         
            ...
         
      
   

Плагин Maven Cargo включен в профильintegration:


    org.codehaus.cargo
    cargo-maven2-plugin
    1.5.0
    
        
            jetty9x
            embedded
        
        
            
                localhost
                8080
            
        
    
    
        
            start-server
            pre-integration-test
            
                start
            
        
        
            stop-server
            post-integration-test
            
                stop
            
        
    

Обратите внимание, что свойства конфигурацииcargo.hostname иcargo.servlet.port включены просто для ясности. Эти свойства конфигурации могут быть оставлены без какого-либо влияния на приложение, поскольку их значения совпадают со значениями по умолчанию. Этот плагин запускает сервер, ожидает подключения и, наконец, останавливает сервер для освобождения системных ресурсов.

This link позволяет нам проверить последнюю версию плагина Maven Cargo.

Плагин Maven Surefire снова объявляется в профилеintegration, чтобы переопределить его конфигурацию в основном разделеbuild и выполнить тестовые примеры, описанные в предыдущем разделе:


    maven-surefire-plugin
    2.19.1
    
        
            integration-test
            
                test
            
            
                
                    none
                
            
        
    

Теперь весь процесс можно запустить с помощью команды:mvn -Pintegration clean install.

7. Заключение

Это руководство иллюстрирует поддержку Apache CXF для Spring. В частности, было показано, как веб-служба может публиковаться с использованием файла конфигурации Spring, и как клиент может взаимодействовать с этой службой через прокси-сервер, созданный фабрикой прокси-серверов Apache CXF, который был объявлен в другом файле конфигурации.

Реализацию всех этих примеров и фрагментов кода можно найти вthe linked GitHub project.