添加JMS资源时遭遇javax.naming.NamingException问题求助
我正将遗留功能迁移至独立Spring Boot应用,通过编程方式添加DataSource资源已成功,但以类似方式添加JMS资源时,持续遭遇javax.naming.NamingException: Could not create resource instance错误。
我的代码如下:
TomcatConfig.java [...] public TomcatServletWebServerFactory servletContainer() { return new TomcatServletWebServerFactory() { @Override protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) { tomcat.enableNaming(); return super.getTomcatWebServer(tomcat); } @Override protected void postProcessContext(Context context) { // JMS ConnectionFactory ContextResource jmsConnection = new ContextResource(); jmsConnection.setName("jms/jmsConnection"); jmsConnection.setType("javax.jms.ConnectionFactory"); jmsConnection.setProperty("resourceAdapter", "openejb/Resource/JmsResourceAdapter"); context.getNamingResources().addResource(jmsConnection); } }; } @Bean(destroyMethod="") public ConnectionFactory jndiConnectionFactory() throws IllegalArgumentException, NamingException { JndiObjectFactoryBean bean = new JndiObjectFactoryBean(); bean.setJndiName("java:comp/env/jms/jmsConnection"); bean.setProxyInterface(ConnectionFactory.class); bean.setLookupOnStartup(true); bean.afterPropertiesSet(); // NamingException here!! return (ConnectionFactory) bean.getObject(); }
我是现代化改造领域的新手,恳请各位指导。是否是因为Tomcat不支持EJB导致的?
我尝试通过编程方式创建ContextResource,但无法成功绑定/查找,无法实例化jndiConnectionFactory Bean,堆栈信息参考如下:
NamingException during context initialization: javax.naming.NamingException: Could not create resource instance javax.naming.NamingException: Could not create resource instance at org.apache.naming.factory.FactoryBase.getObjectInstance(FactoryBase.java:98) at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:321) at org.apache.naming.NamingContext.lookup(NamingContext.java:864) at org.apache.naming.NamingContext.lookup(NamingContext.java:158) at org.apache.naming.NamingContext.lookup(NamingContext.java:850) at org.apache.naming.NamingContext.lookup(NamingContext.java:172) at com......CustomContextListener.contextInitialized(CustomContextListener.java:25) at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4462) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:4914) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1332) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1322) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75) at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134) at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:866) at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:794) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1332) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1322) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75) at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134) at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:866) at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:248) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171) at org.apache.catalina.core.StandardService.startInternal(StandardService.java:433) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171) at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:921) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171) at org.apache.catalina.startup.Tomcat.start(Tomcat.java:489) at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:123) at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:104) at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:481) at com.............configuration.TomcatConfiguration$1.getTomcatWebServer(TomcatConfiguration.java:31) at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:211) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:184) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:162) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:585) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:409) at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1289)
你的问题核心是Tomcat本身不自带JMS资源适配器(Resource Adapter),你配置的openejb/Resource/JmsResourceAdapter是OpenEJB的组件,而Tomcat作为轻量级Servlet容器,没有集成EJB/JMS相关的容器支持,所以无法实例化这个资源。以下是几种可行的解决方式:
1. 改用Spring Boot原生JMS配置(推荐)
放弃通过JNDI绑定OpenEJB的JMS资源,直接使用Spring Boot集成主流JMS Broker(如ActiveMQ、Artemis)的方式,代码更简洁且适配Spring生态:
首先添加对应依赖(以ActiveMQ为例):
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-activemq</artifactId> </dependency>
然后直接配置ConnectionFactory Bean:
@Bean public ConnectionFactory connectionFactory() { ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(); factory.setBrokerURL("tcp://localhost:61616"); factory.setUserName("admin"); factory.setPassword("admin"); return factory; }
2. 保留JNDI方式适配遗留代码
如果必须通过JNDI查找来兼容遗留代码,可通过以下两种方式实现:
方式一:使用Spring的JNDI模拟上下文
通过SimpleNamingContextBuilder在Spring启动时手动绑定JMS资源到JNDI上下文,无需依赖Tomcat的JNDI:
@PostConstruct public void initJndi() throws Exception { SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder(); ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616"); builder.bind("java:comp/env/jms/jmsConnection", factory); builder.activate(); }
方式二:配置Tomcat兼容的JNDI资源
如果一定要用Tomcat的原生JNDI,需要:
- 引入对应JMS Broker的资源适配器(如ActiveMQ的
activemq-rar包)到项目依赖中 - 修改编程式配置,使用Tomcat能识别的JMS工厂类:
@Override protected void postProcessContext(Context context) { ContextResource jmsConnection = new ContextResource(); jmsConnection.setName("jms/jmsConnection"); jmsConnection.setType("javax.jms.ConnectionFactory"); // 使用ActiveMQ的JNDI工厂类 jmsConnection.setProperty("factory", "org.apache.activemq.jndi.ActiveMQInitialContextFactory"); jmsConnection.setProperty("brokerURL", "tcp://localhost:61616"); context.getNamingResources().addResource(jmsConnection); }
3. 关键排查要点
- 确保项目已引入对应JMS Broker的依赖,避免缺失核心类
- Tomcat不支持EJB容器,不要混用OpenEJB、WildFly等EJB容器的资源配置
- 调整JNDI查找时机:将
JndiObjectFactoryBean的setLookupOnStartup(false)改为延迟查找,或通过@DependsOn注解确保Tomcat上下文初始化完成后再执行查找
内容的提问来源于stack exchange,提问作者onogram

