OSGI中多个CXF Bundle绑定同一地址及多WAR暴露JAXRS端点问题
看起来你在OSGi环境下遇到了多个CXF JAX-RS Web Bundle共享同一HTTP地址的问题,结合你给出的web.xml配置(用的是Spring OSGi的Web上下文),我给你整理两个可行的解决方案,都是OSGi生态里常用的做法:
方案1:利用OSGi Http Service做子路径映射
默认情况下,每个WAR(Web Bundle)会独占一个Web上下文路径,直接绑定同一地址肯定会冲突。我们可以改用OSGi Http Service来为每个Bundle的CXF Servlet注册到同一根路径的不同子路径下,这样就能实现“同一根地址,不同API子路径”的效果。
具体操作:
调整你的Spring配置,替换web.xml的上下文设置:
你当前的web.xml是让每个WAR作为独立Web应用运行,我们需要改成通过Spring OSGi的OsgiServletRegistrationBean来注册CXF Servlet,而不是依赖Web容器的上下文。可以简化web.xml,甚至去掉大部分配置,把CXF端点的配置移到Spring XML里:示例Spring XML配置:
<!-- 定义CXF JAX-RS Servlet --> <bean id="jaxrsServlet" class="org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet"> <property name="bus" ref="cxf"/> <!-- 这里可以添加全局的提供者,比如JSON序列化器 --> </bean> <!-- 注册Servlet到OSGi Http Service,指定子路径 --> <bean class="org.springframework.osgi.web.servlet.OsgiServletRegistrationBean"> <property name="servlet" ref="jaxrsServlet"/> <property name="alias" value="/api/service1"/> <!-- 每个Bundle用不同的子路径,比如/service2、/service3 --> <property name="contextAttributes"> <map> <!-- 指定当前Bundle的JAX-RS服务类 --> <entry key="jaxrs.serviceClasses" value="com.your.package.Service1Impl"/> </map> </property> </bean> <!-- 定义CXF Bus --> <bean id="cxf" class="org.apache.cxf.bus.CXFBusImpl"/>确保Bundle依赖正确:
在每个业务Bundle的MANIFEST.MF里添加对OSGi Http Service和Spring OSGi Web的依赖:Require-Bundle: org.apache.felix.http.api, org.springframework.osgi.web, org.apache.cxf.bundle
这样配置后,所有Bundle的CXF端点都会挂载到同一根地址(比如http://localhost:8080/api)下的不同子路径,不会再出现上下文冲突。
方案2:共享CXF Bus,统一注册所有端点
如果希望所有端点都通过同一个CXF Servlet对外暴露,共享Bus的配置(比如拦截器、全局提供者),可以创建一个专门的Bundle来管理主CXF Bus和根路径Servlet,然后让各个业务Bundle向这个Bus注册自己的端点。
具体操作:
创建共享CXF Bus的基础Bundle:
这个Bundle负责初始化CXF Bus,并注册主Servlet到根路径/api,同时把Bus暴露为OSGi服务:示例Blueprint配置(用Blueprint更贴合OSGi标准,当然Spring也可以):
<!-- 创建并暴露CXF Bus作为OSGi服务 --> <bean id="cxfBus" class="org.apache.cxf.bus.CXFBusImpl"/> <service ref="cxfBus" interface="org.apache.cxf.Bus"/> <!-- 注册主JAX-RS Servlet到根路径 --> <bean id="mainJaxrsServlet" class="org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet"> <property name="bus" ref="cxfBus"/> </bean> <bean class="org.springframework.osgi.web.servlet.OsgiServletRegistrationBean"> <property name="servlet" ref="mainJaxrsServlet"/> <property name="alias" value="/api"/> <!-- 统一的根路径 --> </bean>业务Bundle注册端点到共享Bus:
每个业务Bundle通过OSGi服务引用获取共享的CXF Bus,然后用CXF的jaxrs:server标签注册自己的端点,指定子路径:示例Spring配置:
<!-- 引用OSGi环境中的共享CXF Bus --> <reference id="cxfBus" interface="org.apache.cxf.Bus"/> <!-- 注册当前Bundle的JAX-RS端点,子路径为/service1 --> <jaxrs:server address="/service1" bus="cxfBus"> <jaxrs:serviceBeans> <ref bean="service1Impl"/> </jaxrs:serviceBeans> <jaxrs:providers> <!-- 当前端点的自定义提供者 --> </jaxrs:providers> </jaxrs:server> <!-- 业务服务实现类 --> <bean id="service1Impl" class="com.your.package.Service1Impl"/>
这种方式下,所有端点都会通过http://localhost:8080/api/service1、http://localhost:8080/api/service2这样的路径对外提供服务,而且共享同一个CXF Bus的配置,维护起来更方便。
关键注意点
- 不要设置重复的Web上下文路径:每个Web Bundle的
MANIFEST.MF里的Web-ContextPath属性不要重复,最好干脆不设置,交给OsgiServletRegistrationBean来管理路径。 - 版本匹配很重要:确保CXF、Spring OSGi(或Blueprint)和OSGi Http Service的版本兼容,比如CXF 3.x建议搭配Spring 4.x或者Blueprint 1.0以上版本。
- 子路径必须唯一:不管用哪种方案,每个端点的子路径都得是唯一的,否则后注册的端点会覆盖先注册的。
内容的提问来源于stack exchange,提问作者karolkpl

