运行带有嵌入式容器和 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 应用程序运行错误

JNDI在Spring和tomcat下的使用

JNDI 解决了 Spring 中 ApplicationContext 没有解决的哪个问题?

带有 bean 的 Spring-Core JNDI 配置

spring-boot 应用程序的外部配置