如何在SOAP插件应用中编程式添加jaxws:endpoint(loadBus/动态config-location)
Apache CXF 动态添加Endpoint解决方案
背景
当前Web服务从Axis迁移至Apache CXF,插件模块的Endpoint硬编码在servicesbeans.xml中,运行正常,但需要在应用启动时动态添加Endpoint,以下针对两种方案给出具体实现:
方案一:多配置文件加载(含编程式添加)
1. 多config-location的直接配置
CXFServlet支持通过逗号分隔多个配置文件路径,直接在web.xml中修改config-location参数即可:
<servlet> <servlet-name>ProvisioningApiCXFServlet</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> <init-param> <param-name>config-location</param-name> <param-value>/WEB-INF/servicesbeans.xml,/WEB-INF/servicesbeans_2.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
注意:路径需确保在WEB-INF下可访问,若路径包含空格需转义,或使用相对路径。
2. 编程式动态添加配置文件
如果需要在应用启动时动态指定插件生成的配置文件,可通过自定义ServletContextListener实现:
import org.apache.cxf.transport.servlet.CXFServlet; import org.springframework.context.support.ClassPathXmlApplicationContext; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class CxfConfigLoaderListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { ServletContext context = sce.getServletContext(); // 获取CXFServlet实例 CXFServlet cxfServlet = (CXFServlet) context.getAttribute("ProvisioningApiCXFServlet"); // 动态加载额外配置文件 String[] additionalConfigs = {"/WEB-INF/dynamic_servicesbeans.xml"}; // 基于已有Bus创建上下文,合并配置 ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(additionalConfigs, cxfServlet.getBus().getApplicationContext()); ctx.refresh(); } @Override public void contextDestroyed(ServletContextEvent sce) { // 清理资源 } }
在web.xml中注册监听器:
<listener> <listener-class>com.yourpackage.CxfConfigLoaderListener</listener-class> </listener>
方案二:基于Bus的编程式创建Endpoint
无需依赖XML配置,直接通过Bus动态注册Endpoint:
1. 获取CXFServlet的Bus实例并创建Endpoint
在Tomcat环境中,通过ServletContext获取CXFServlet实例,进而拿到Bus并创建Endpoint:
import org.apache.cxf.Bus; import org.apache.cxf.jaxws.EndpointImpl; import org.apache.cxf.transport.servlet.CXFServlet; import com.smarttrust.jam.dpadmin.request.CardHandler; import org.apache.cxf.feature.LoggingFeature; import javax.xml.namespace.QName; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import java.util.ArrayList; import java.util.List; public class DynamicEndpointRegisterListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { ServletContext context = sce.getServletContext(); // 通过Servlet名称获取CXFServlet实例 CXFServlet cxfServlet = (CXFServlet) context.getAttribute("ProvisioningApiCXFServlet"); Bus bus = cxfServlet.getBus(); // 创建Endpoint实现类实例 CardHandler implementor = new CardHandler(); // 添加LoggingFeature List<Object> features = new ArrayList<>(); features.add(new LoggingFeature()); // 动态创建并发布Endpoint EndpointImpl endpoint = new EndpointImpl(bus, implementor); endpoint.setEndpointName(new QName("http://your.namespace.com", "CardHandlerPort")); endpoint.setFeatures(features); endpoint.publish("/CardHandlerService"); } @Override public void contextDestroyed(ServletContextEvent sce) { // 可选:销毁Endpoint // EndpointImpl endpoint = ...; // endpoint.stop(); } }
2. 注册监听器
在web.xml中添加监听器配置,确保在CXFServlet启动后执行:
<listener> <listener-class>com.yourpackage.DynamicEndpointRegisterListener</listener-class> </listener>
注意:可通过调整
load-on-startup值控制CXFServlet与监听器的加载顺序,确保CXFServlet先初始化。
关键注意事项
- 未使用Spring时,需确保CXFServlet的Bus已完成初始化,避免空指针异常。
- 动态添加的Endpoint实现类需被类加载器正确加载(插件需提前部署到类路径)。
- 多配置文件加载时,重复的Bean ID会发生覆盖,需注意配置文件间的冲突。
内容的提问来源于stack exchange,提问作者user2734915
相关产品推荐
相关产品推荐

