运行带有嵌入式容器和 JNDI 的 Spring Boot 应用程序
Posted
技术标签:
【中文标题】运行带有嵌入式容器和 JNDI 的 Spring Boot 应用程序【英文标题】:Run a Spring Boot app with embedded container and JNDI 【发布时间】:2018-06-13 23:20:17 【问题描述】:我有一个带有 spring.datasource.jndi-name=java:/foo
属性的 Spring Boot 应用程序,它在 WildFly 下运行良好。
我想使用嵌入式容器运行相同的应用程序,即mvn spring-boot:run
,但是虽然 WildFly 在其配置中配置了 JNDI 数据源,但嵌入式容器没有,即我得到:
org.springframework.jdbc.datasource.lookup.DataSourceLookupFailureException:
Failed to look up JNDI DataSource with name 'java:/foo'
我想我必须在某处包含一个 XML 文件来为嵌入式容器配置 JNDI 数据源,但我找不到关于此的文档。我刚刚找到了有关如何在 Java 源代码中创建 JNDI 数据源的教程,但我想避免这种情况,以便同一个应用程序可以在外部和嵌入式容器中运行。
我怎样才能做到这一点?
EDIT This answer 展示了如何在 Tomcat 中创建 JNDI 上下文,这种方式会破坏在其他容器(例如 WildFly)中运行相同的应用程序。我正在寻找一个答案,它可以让应用程序在不同容器中使用相同的源运行,例如只需使用 WildFly 中配置的相同 JNDI 资源配置嵌入式容器。
【问题讨论】:
How to create JNDI context in Spring Boot with Embedded Tomcat Container的可能重复 请注意,我正在寻找仅配置解决方案,例如不涉及修改源(如果可能的话)。 将@Profile
注释添加到 TomcatEmbeddedServletContainerFactory bean 定义中,然后在通过 jvm 参数运行时启用该配置文件。这可确保当您在其他容器中部署时不会发生任何变化。嵌入式 tomcat 在运行时直接从 tomcat jars 运行,因此没有任何配置文件用于定义资源。
【参考方案1】:
@Setu 的回答对我不起作用,因为 TomcatEmbeddedServletContainerFactory
是在 ApplicationContext
刷新后创建的,但我需要 JNDI 在 ApplicationContext
刷新期间可用。
相反,我在启动我的 Spring Boot 应用程序以启用 Tomcat 命名之前设置了以下系统属性:
System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming");
System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.naming.java.javaURLContextFactory");
【讨论】:
【参考方案2】:要使应用程序也可部署在其他启用 jndi 的容器中,请执行以下操作;
-
扩展
TomcatEmbeddedServletContainerFactory
并启用jndi命名并添加资源
使用 Profile 注释创建一个配置类,该类公开扩展的 TomcatEmbeddedServletContainerFactory
bean
见下面的代码;
扩展 TomcatEmbeddedServletContainerFactory
class EmbeddedServletContainerFactory extends TomcatEmbeddedServletContainerFactory
@Override
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(Tomcat tomcat)
tomcat.enableNaming(); // This is essential. Naming is disabled by default which needs enabling
return super.getTomcatEmbeddedServletContainer(tomcat);
@Override
protected void postProcessContext(Context context)
ContextResource resource = new ContextResource();
// All the below properties you can retrieve via preferred method
resource.setName("jdbc/test");
resource.setAuth("Container");
resource.setType(DataSource.class.getName());
resource.setProperty("driverClassName", driverClass);
resource.setProperty("factory", "org.apache.commons.dbcp2.BasicDataSourceFactory");
resource.setProperty("url", dbUrl);
resource.setProperty("username", username);
resource.setProperty("password", password);
context.getNamingResources().addResource(resource);
暴露 bean 的配置类
@Profile("embedded")
@Configuration
public class EmbeddedConfig
@Bean
public TomcatEmbeddedServletContainerFactory tomcatFactory()
return new EmbeddedServletContainerFactory();
如果你不喜欢 java config 中的这个,你可以用 xml 方式做同样的事情
<beans profile="embedded">
<bean id="TomcatEmbeddedServletContainerFactory" class="EmbeddedServletContainerFactory" />
</bean>
现在您可以在 pom 中硬编码配置文件名称或通过 jvm 参数添加它;
mvn spring-boot:run -Drun.profiles=embedded
您的其他代码保持不变,并且在其他容器中的行为相同。通过 jndi 查找数据源也保持不变。此代码确保在嵌入式容器中实际上有一个绑定到该 jndi 的数据源。
【讨论】:
您将"jdbc/test"
设置为 JNDI 名称,不应该是例如"java:/jdbc/test"
?我问是因为我的 JNDI 名称是 java:/datasources/xxx
并且使用您的代码我得到了 Name [datasources/xxx] is not bound in this Context. Unable to find [datasources].
这是我公开资源的名称。我的查找代码使用带有完整 jndi 名称的 spring JndiDataSourceLookup
类,即 java:/comp/env/jdbc/test
。如果您不想更改 application.properties 文件中的查找 jndi 名称,则可以在查找之前将 resourceRef 设置为 true。
我不确定应该在哪里设置resourceRef
属性。我没有明确地进行任何查找,这一切都是由 Spring AFAIK 自动完成的。
经过进一步测试,我认为它有点复杂。我的应用程序的工作方式是在公开资源时我给出一个不带冒号 (:) 的名称 jdbc/test 并且在查找数据源时我使用 jndi 名称 java:comp/env/jdbc/test 或只是 jdbc/test 这两者工作正常。但是,如果为了测试我在名称中使用冒号公开我的资源,即 java:/jdbc/test,那么我必须在我的查找 jndi 前加上 java:/comp/env 即 java:/comp/env/java:/jdbc/test。我目前无法找到这背后的原因,但如果我这样做会更新答案。你最好的选择是现在给我们完整的 jndi 名称,看看它是否有效。以上是关于运行带有嵌入式容器和 JNDI 的 Spring Boot 应用程序的主要内容,如果未能解决你的问题,请参考以下文章
带有嵌入式 Tomcat 的 Jndi Mongodb Spring Boot,[重复]
带有嵌入式 servlet 容器的 Spring 应用程序运行错误