Spring Boot3+Kotlin+Gradle环境下Apache CXF最简配置失效排查
我尝试用Spring Boot 3、Kotlin和Gradle搭建最小化的Apache CXF Web应用,但唯一的端点始终返回404。网上的CXF+Spring Boot教程大多过时,官方文档甚至停留在3.1版本(当前CXF是4.0),但我查的资料里代码看起来没问题。
SpringKotlinInterfaceTestApplication.kt
import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.datatype.joda.JodaModule import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider import jakarta.ws.rs.* import org.apache.cxf.Bus import org.apache.cxf.endpoint.Server import org.apache.cxf.jaxrs.JAXRSServerFactoryBean import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.stereotype.Component @Component @Consumes("application/json") @Produces("application/json") class PreconditionApiImpl { @GET @Path("/pingx") fun pingx(): String = "{status: OK}" } @SpringBootApplication class SpringKotlinInterfaceTestApplication fun main(args: Array<String>) { runApplication<SpringKotlinInterfaceTestApplication>(*args){} } @Configuration class RsConfig { @Bean fun getRsServer(bus: Bus, api: PreconditionApiImpl): Server = JAXRSServerFactoryBean().apply { setBus(bus) address = "/" setServiceBeans(listOf(api)) setProvider( JacksonJsonProvider().apply { setMapper(ObjectMapper().apply { registerModule(JodaModule()) setSerializationInclusion(JsonInclude.Include.NON_EMPTY) }) } ) }.create() }
build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id("org.springframework.boot") version "3.0.9" id("io.spring.dependency-management") version "1.0.15.RELEASE" val kotlinVersion = "1.9.0" kotlin("jvm") version kotlinVersion kotlin("plugin.spring") version kotlinVersion } group = "com.example" version = "0.0.1-SNAPSHOT" java { sourceCompatibility = JavaVersion.VERSION_17 } repositories { mavenCentral() } dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.springframework.boot:spring-boot-starter-actuator") implementation("org.jetbrains.kotlin:kotlin-reflect") val jacksonVersion = "2.14.3" implementation("com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-json-provider:$jacksonVersion") implementation("com.fasterxml.jackson.datatype:jackson-datatype-joda:$jacksonVersion") val cxfVersion = "4.0.2" implementation("org.apache.cxf:cxf-spring-boot-starter-jaxrs:$cxfVersion") implementation("org.apache.cxf:cxf-rt-rs-client:$cxfVersion") implementation("org.apache.cxf:cxf-rt-transports-http:$cxfVersion") implementation("org.apache.cxf:cxf-rt-frontend-jaxws:$cxfVersion") } tasks.withType<KotlinCompile> { kotlinOptions { freeCompilerArgs += "-Xjsr305=strict" jvmTarget = "17" } }
问题
向http://localhost:8080/pingx发送GET请求返回404,遗漏了什么配置?难道cxf-spring-boot-starter-jaxrs没有完成所有基础自动装配?
解决思路
1. 解决Spring Web与CXF的冲突
你引入了spring-boot-starter-web,它自带Spring MVC和Tomcat,会抢占根路径的请求处理权,导致CXF的端点无法被路由。有两种处理方式:
- 替换为基础starter:把
implementation("org.springframework.boot:spring-boot-starter-web")改成implementation("org.springframework.boot:spring-boot-starter"),去掉Spring MVC依赖,让CXF接管HTTP请求。 - 排除Spring MVC自动配置:如果需要保留Spring Web的其他功能,修改启动类:
@SpringBootApplication(exclude = [org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration::class]) class SpringKotlinInterfaceTestApplication
2. 调整CXF基础路径配置
默认情况下,CXF starter会把所有端点挂载在/services路径下。即使你在代码中设置address = "/",实际访问路径可能是http://localhost:8080/services/pingx。
可以在application.properties中添加配置,让CXF直接挂载到根路径:
cxf.path=/
3. 验证CXF Bean初始化状态
你已经引入了spring-boot-starter-actuator,可以开启beans端点查看CXF相关Bean是否正常初始化:
在application.properties中添加:
management.endpoints.web.exposure.include=beans
访问http://localhost:8080/actuator/beans,搜索org.apache.cxf相关条目,确认Server和Bus实例已成功创建。
4. 移除多余依赖
你引入的cxf-rt-frontend-jaxws是JAX-WS相关依赖,当前用的是JAX-RS,这个依赖完全多余,可以删除,避免潜在冲突。
内容的提问来源于stack exchange,提问作者StephanS

