本文聚焦于Apache CXF框架与Spring的配置与集成,涵盖Java配置和XML配置两种方式。这是Apache CXF系列教程的第二篇,首篇介绍了CXF作为JAX-WS标准API实现的基础知识。
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-transports-http</artifactId>
<version>3.1.6</version>
</dependency>
最新版本可查阅apache-cxf仓库
此外,需添加Spring支持依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.25</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.25</version>
</dependency>
Spring最新版本见Spring仓库
最后,由于使用Servlet 3.0+ API替代传统web.xml
,需添加:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.1.0</version>
</dependency>
Servlet API最新版本在这里
3. 服务端组件
3.1 WebApplicationInitializer接口
WebApplicationInitializer
用于程序化配置ServletContext
。当容器检测到该类时,会自动调用其onStartup
方法初始化上下文:
public class AppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) {
// 配置实现
}
}
关键步骤分三步走:
- 创建并注册Spring应用上下文
- 添加上下文加载监听器
- 注册CXFServlet并映射URL
具体实现代码:
// 创建Spring上下文
AnnotationConfigWebApplicationContext context
= new AnnotationConfigWebApplicationContext();
context.register(ServiceConfiguration.class);
// 添加上下文监听器
container.addListener(new ContextLoaderListener(context));
// 注册CXFServlet
ServletRegistration.Dynamic dispatcher
= container.addServlet("dispatcher", new CXFServlet());
// 映射URL路径
dispatcher.addMapping("/services");
3.2 传统web.xml方式
如果偏爱XML配置,可在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 ServiceConfiguration类
核心配置类,用于定义服务端Bean:
@Configuration
public class ServiceConfiguration {
// Bean定义
}
必须声明两个关键Bean:
SpringBus
:CXF与Spring的桥梁Endpoint
:服务发布端点
@Bean
public SpringBus springBus() {
return new SpringBus();
}
@Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), new BaeldungImpl());
endpoint.publish("http://localhost:8080/services/baeldung");
return endpoint;
}
等价XML配置(需存为cxf-servlet.xml
):
<jaxws:endpoint
id="baeldung"
implementor="com.baeldung.cxf.spring.BaeldungImpl"
address="http://localhost:8080/services/baeldung" />
文件名需与servlet-name匹配(此处为cxf)
3.4 类型定义
服务实现类BaeldungImpl
:
@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;
}
}
对应的服务接口Baeldung
:
@WebService
public interface Baeldung {
String hello(String name);
String register(Student student);
}
数据传输对象Student
:
public class Student {
private String name;
// 构造器、getter/setter
}
4. 客户端Bean
客户端配置类ClientConfiguration
:
@Configuration
public class ClientConfiguration {
// Bean定义
}
通过代理工厂创建服务代理:
@Bean(name = "client")
public Object generateProxy() {
return proxyFactoryBean().create();
}
@Bean
public JaxWsProxyFactoryBean proxyFactoryBean() {
JaxWsProxyFactoryBean proxyFactory = new JaxWsProxyFactoryBean();
proxyFactory.setServiceClass(Baeldung.class);
proxyFactory.setAddress("http://localhost:8080/services/baeldung");
return proxyFactory;
}
等价XML配置:
<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. 测试用例
测试类StudentTest
关键配置:
private ApplicationContext context
= new AnnotationConfigApplicationContext(ClientConfiguration.class);
private Baeldung baeldungProxy = (Baeldung) context.getBean("client");
测试场景1:基础调用
验证hello
方法远程调用:
@Test
public void whenUsingHelloMethod_thenCorrect() {
String response = baeldungProxy.hello("John Doe");
assertEquals("Hello John Doe!", response);
}
测试场景2:对象传递
验证register
方法处理对象参数:
@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. 集成测试
6.1 WAR打包配置
POM中声明打包类型:
<packaging>war</packaging>
配置Maven WAR插件(跳过web.xml检查):
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.4.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
6.2 测试插件配置
Surefire插件(排除普通测试):
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<excludes>
<exclude>StudentTest.java</exclude>
</excludes>
</configuration>
</plugin>
集成测试Profile:
<profiles>
<profile>
<id>integration</id>
<build>
<plugins>
<!-- 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>
<!-- 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>
</plugins>
</build>
</profile>
</profiles>
执行命令:mvn -Pintegration clean install
7. 总结
本文展示了Apache CXF与Spring的深度集成能力:
- 通过Spring配置发布WebService接口
- 使用CXF代理工厂创建客户端代理
- 支持Java配置和XML配置两种方式
- 完整的服务端/客户端测试方案
所有示例代码可在GitHub项目中获取。简单粗暴地说,这套方案让WebService开发变得像配置Spring Bean一样直观,踩坑点主要在于Servlet初始化和代理工厂的配置细节。