A Guide to Apache CXF with Spring – 使用Spring的Apache CXF指南

最后修改: 2016年 7月 15日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

This tutorial focuses on configuring and using the Apache CXF framework together with Spring – either with Java or XML configuration.

本教程的重点是配置和Apache CXF框架与Spring一起使用 – 使用Java或XML配置。

It’s the second in a series on Apache CXF; the first one focused on the fundamentals of CXF as an implementation of the JAX-WS standard APIs.

这是关于Apache CXF系列的第二篇;第一篇侧重于CXF作为JAX-WS标准API的实现的基本原理。

2. Maven Dependencies

2.Maven的依赖性

Similar to the previous tutorial, the following two dependencies need to be included:

与之前的教程类似,需要包括以下两个依赖项。

<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>

For the latest versions of Apache CXF artifacts, please check out apache-cxf.

关于Apache CXF工件的最新版本,请查看apache-cxf>。

In addition, the following dependencies are necessary to support Spring:

此外,为支持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>

The latest versions of Spring artifacts can be found here.

Spring工件的最新版本可以在这里找到。

Finally, because we’ll programmatically configure the application using the Java Servlet 3.0+ API instead of a traditional web.xml deployment descriptor, we’ll need the artifact below:

最后,由于我们将使用Java Servlet 3.0+ API而不是传统的web.xml部署描述符来编程配置应用程序,我们将需要下面的工件。

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
</dependency>

This is where we can find the latest version of the Servlet API.

这个是我们可以找到Servlet API的最新版本的地方。

3. Server-Side Components

3.服务器端组件

Let’s now have a look at the components that need to be present on the server side in order to publish the web service endpoint.

现在让我们来看看为了发布Web服务端点而需要在服务器端存在的组件。

3.1. WebApplicationInitilizer Interface

3.1.WebApplicationInitilizer 接口

The WebApplicationInitializer interface is implemented to programmatically configure the ServletContext interface for the application. When present on the classpath, its onStartup method is automatically invoked by the servlet container and thereafter the ServletContext is instantiated and initialized.

WebApplicationInitializer接口的实现是为了以编程方式为应用程序配置ServletContext接口。当出现在classpath上时,它的onStartup方法会被servlet容器自动调用,此后ServletContext被实例化和初始化。

Here is how a class is defined to implement the WebApplicationInitializer interface:

下面是如何定义一个类来实现WebApplicationInitializer接口。

public class AppInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext container) {
        // Method implementation
    }
}

The onStartup() method is implemented using code snippets shown below.

onStartup()方法是用下面的代码片段实现的。

First, a Spring application context is created and configured to register a class containing configuration metadata:

首先,一个Spring应用上下文被创建并配置为注册一个包含配置元数据的类。

AnnotationConfigWebApplicationContext context 
  = new AnnotationConfigWebApplicationContext();
context.register(ServiceConfiguration.class);

The ServiceConfiguration class is annotated with the @Configuration annotation to provide bean definitions. This class is discussed in the next subsection.

ServiceConfiguration类用@Configuration注解来提供bean定义。这个类将在下一小节讨论。

The following snippet shows how the Spring application context is added to the servlet context:

下面的片段显示了Spring应用上下文是如何被添加到servlet上下文的。

container.addListener(new ContextLoaderListener(context));

The CXFServlet class, which is defined by Apache CXF, is generated and registered to handle incoming requests:

由Apache CXF定义的CXFServlet类被生成和注册以处理传入的请求。

ServletRegistration.Dynamic dispatcher 
  = container.addServlet("dispatcher", new CXFServlet());

The application context loads Spring elements defined in a configuration file. In this case, the name of the servlet is cxf, therefore the context looks for those elements in a file named cxf-servlet.xml by default.

应用程序上下文加载配置文件中定义的Spring元素。在这种情况下,servlet的名字是cxf,因此上下文默认在名为cxf-servlet.xml的文件中寻找这些元素。

Lastly, the CXF servlet is mapped to a relative URL:

最后,CXF servlet被映射到一个相对的URL。

dispatcher.addMapping("/services");

3.2. The Good Old web.xml

3.2.旧的web.xml

Alternatively, if we want to make use of a (somewhat old-fashioned) deployment descriptor rather than the WebApplicationInitilizer interface, the corresponding web.xml file should contain the following servlet definitions:

另外,如果我们想利用(有点老式的)部署描述符而不是WebApplicationInitilizer接口,相应的web.xml文件应该包含以下servlet定义。

<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. ServiceConfiguration Class

3.3.ServiceConfiguration

Let’s now have a look at the service configuration – first a basic skeleton which encloses bean definitions for the web service endpoint:

现在让我们来看看服务配置–首先是一个基本的骨架,它包含了网络服务端点的Bean定义。

@Configuration
public class ServiceConfiguration {
    // Bean definitions
}

The first required bean is the SpringBus – which supplies extensions for Apache CXF to work with the Spring Framework:

第一个需要的Bean是SpringBus–它为Apache CXF提供扩展,以便与Spring框架一起工作。

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

An EnpointImpl bean also needs to be created using the SpringBus bean and a web service implementor. This bean is used to publish the endpoint at the given HTTP address:

还需要使用SpringBus Bean和Web服务implementor创建一个EnpointImpl Bean。这个Bean被用来在给定的HTTP地址上发布端点。

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

The BaeldungImpl class is used to implement the web service interface. Its definition is given in the next subsection.

BaeldungImpl类用于实现网络服务接口。它的定义将在下一小节给出。

Alternatively, we may also declare the server endpoint in an XML configuration file. Specifically, the cxf-servlet.xml file below works with the web.xml deployment descriptor as was defined in subsection 3.1 and describes the exact same endpoint:

另外,我们也可以在一个XML配置文件中声明服务器端点。具体来说,下面的cxf-servlet.xml文件与3.1小节中定义的web.xml部署描述符一起工作,描述的端点完全相同。

<jaxws:endpoint
  id="baeldung"
  implementor="com.baeldung.cxf.spring.BaeldungImpl"
  address="http://localhost:8080/services/baeldung" />

Note that the XML configuration file is named after the servlet name defined in the deployment descriptor, which is cxf.

请注意,XML配置文件是以部署描述符中定义的Servlet名称命名的,即cxf

3.4. Type Definitions

3.4.类型定义

Next – here is the definition of the implementor that has already been mentioned in the preceding subsection:

接下来–这里是上一小节已经提到的implementor的定义。

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

This class provides an implementation for the Baeldung endpoint interface that Apache CXF will include in the published WSDL metadata:

这个类提供了对Baeldung端点接口的实现,Apache CXF将在发布的WSDL元数据中包含这个接口。

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

Both the endpoint interface as well as the implementor make use of the Student class, which is defined as follows:

端点接口和实现器都使用了Student类,其定义如下。

public class Student {
    private String name;

    // constructors, getters and setters
}

4. Client-Side Beans

4.客户方BeanBean

To take advantage of the Spring Framework, we declare a bean in a @Configuration annotated class:

为了利用Spring框架的优势,我们在@Configuration注解的类中声明一个bean。

@Configuration
public class ClientConfiguration {
    // Bean definitions
}

A bean with the name of client is defined:

定义了一个名字为client的bean。

@Bean(name = "client")
public Object generateProxy() {
    return proxyFactoryBean().create();
}

The client bean represents a proxy for the Baeldung web service. It is created by an invocation to the create method on a JaxWsProxyFactoryBean bean, a factory for the creation of JAX-WS proxies.

client bean 代表 Baeldung Web 服务的代理。它是通过调用JaxWsProxyFactoryBean Bean上的create方法来创建的,该Bean是创建JAX-WS代理的工厂。

The JaxWsProxyFactoryBean object is created and configured by the following method:

JaxWsProxyFactoryBean对象是通过以下方法创建和配置的。

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

The factory’s serviceClass property denotes the web service interface, while the address property indicates the URL address for the proxy to make remote invocations.

工厂的serviceClass属性表示Web服务接口,而address属性表示用于代理进行远程调用的URL地址。

Also for the Spring beans on the client side one may revert to an XML configuration file. The following elements declare the same beans as the ones we just have programmatically configured above:

同样,对于客户端的Spring Bean,我们可以恢复到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.baeldung.cxf.spring.Baeldung" />
    <property name="address" value="http://localhost:8080/services/baeldung" />
</bean>

5. Test Cases

5.测试案例

This section describes test cases used to illustrate Apache CXF support for Spring. The test cases are defined in a class named StudentTest.

本节描述了用于说明Apache CXF对Spring支持的测试用例。这些测试用例被定义在一个名为StudentTest的类中。

First, we need to load a Spring application context from the aforementioned ServiceConfiguration configuration class and cache it in the context field:

首先,我们需要从前面提到的ServiceConfiguration配置类中加载一个Spring应用上下文,并将其缓存在context字段。

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

Next, a proxy for the service endpoint interface is declared and loaded from the application context:

接下来,服务端点接口的代理被声明并从应用环境中加载。

private Baeldung baeldungProxy = (Baeldung) context.getBean("client");

This Baeldung proxy will be used in test cases described below.

这个Baeldung代理将被用于下面描述的测试案例。

In the first test case, we prove that when the hello method is locally invoked on the proxy, the response is exactly the same as what the endpoint implementor returns from the remote web service:

在第一个测试案例中,我们证明了当hello方法在代理上被本地调用时,响应与端点implementor从远程Web服务返回的内容完全相同。

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

In the second test case, students register for Baeldung courses by locally invoking the register method on the proxy, which in turn calls the web service. That remote service will then calculate the student numbers and return them to the caller. The following code snippet confirms what we expect:

在第二个测试案例中,学生通过本地调用代理上的register方法来注册Baeldung课程,而代理又调用网络服务。然后,该远程服务将计算出学生人数并将其返回给调用者。下面的代码片断证实了我们的期望。

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

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

6. Integration Testing

6.集成测试

In order to be deployed as a web application on a server, code snippets in this tutorial need to be packaged into a WAR file first. This can be achieved by declaring the packaging property in the POM file:

为了在服务器上作为网络应用部署,本教程中的代码片段需要先打包成一个WAR文件。这可以通过在POM文件中声明packaging属性来实现。

<packaging>war</packaging>

The packaging job is implemented by the Maven WAR plugin:

包装工作由Maven WAR插件实现。

<plugin>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.6</version>
    <configuration>
        <failOnMissingWebXml>false</failOnMissingWebXml>
    </configuration>
</plugin>

This plugin packages the compiled source code into a WAR file. Since we configure the servlet context using Java code, the traditional web.xml deployment descriptor does not need to be existent. As the result, the failOnMissingWebXml property must be set to false to avoid failure when the plugin is executed.

这个插件将编译后的源代码打包成一个WAR文件。由于我们使用Java代码配置servlet上下文,传统的web.xml部署描述符就不需要存在了。因此,failOnMissingWebXml属性必须设置为false,以避免在执行该插件时失败。

We can follow this link for the most recent version of the Maven WAR plugin.

我们可以按照这个链接获取Maven WAR插件的最新版本。

In order to illustrate operations of the web service, we create an integration test. This test first generates a WAR file and starts an embedded server, then makes clients invoke the web service, verifies subsequent responses and finally stops the server.

为了说明网络服务的操作,我们创建了一个集成测试。这个测试首先生成一个WAR文件并启动一个嵌入式服务器,然后让客户调用网络服务,验证随后的响应,最后停止服务器。

The following plugins need to be included in the Maven POM file. For more details, please check out this Integration Testing tutorial.

以下插件需要包含在Maven POM文件中。更多细节,请查看这个集成测试教程

Here is the Maven Surefire plugin:

这里是Maven Surefire插件。

<plugin>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.22.2</version>
    <configuration>
        <excludes>
            <exclude>StudentTest.java</exclude>
        </excludes>
    </configuration>
</plugin>

The latest version of this plugin can be found here.

该插件的最新版本可以在这里找到。

A profile section with the id of integration is declared to facilitate the integration test:

声明一个profile部分,其idintegration,以方便集成测试。

<profiles>
   <profile>
      <id>integration</id>
      <build>
         <plugins>
            ...
         </plugins>
      </build>
   </profile>
</profiles>

The Maven Cargo plugin is included in the integration profile:

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>

Note that the cargo.hostname and cargo.servlet.port configuration properties are merely included for the sake of clarity. These configuration properties may be left out without any impact on the application since their values are the same as the default values. This plugin starts the server, waits for connections and finally stops the server to release system resources.

请注意,cargo.hostnamecargo.servlet.port配置属性只是为了清晰起见而包括在内。这些配置属性可以不包括在内,但对应用程序没有任何影响,因为它们的值与默认值相同。这个插件启动服务器,等待连接,最后停止服务器以释放系统资源。

This link allows us to check out the latest version of the Maven Cargo plugin.

这个链接让我们可以查看最新版本的Maven Cargo插件。

The Maven Surefire plugin is declared again, within the integration profile, to override its configuration in the main build section and to execute test cases described in the previous section:

integration配置文件中再次声明Maven Surefire插件,以覆盖其在主build部分的配置,并执行上节所述的测试用例。

<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>

Now the entire process can be run by the command: mvn -Pintegration clean install.

现在整个过程可以通过命令来运行。mvn -Pintegration clean install

7. Conclusion

7.结论

This tutorial illustrated Apache CXF support for Spring. In particular, it has been shown how a web service may be published using a Spring configuration file, and how a client may interact with that service through a proxy created by an Apache CXF proxy factory, which was declared in another configuration file.

本教程说明了Apache CXF对Spring的支持。特别是,它展示了如何使用Spring配置文件发布Web服务,以及客户端如何通过Apache CXF代理工厂创建的代理与该服务交互,该代理工厂在另一个配置文件中声明。

The implementation of all these examples and code snippets can be found in the linked GitHub project.

所有这些例子和代码片断的实现都可以在链接的GitHub项目中找到。