【问题标题】:Run a Spring Boot app with embedded container and JNDI运行带有嵌入式容器和 JNDI 的 Spring Boot 应用程序
【发布时间】: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 资源配置嵌入式容器。

【问题讨论】:

  • 请注意,我正在寻找仅配置解决方案,例如不涉及修改源(如果可能的话)。
  • @Profile 注释添加到 TomcatEmbeddedServletContainerFactory bean 定义中,然后在通过 jvm 参数运行时启用该配置文件。这可确保当您在其他容器中部署时不会发生任何变化。嵌入式 tomcat 在运行时直接从 tomcat jars 运行,因此没有任何配置文件用于定义资源。

标签: java maven spring-boot spring-data jndi


【解决方案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 的容器中,请执行以下操作;

    1. 扩展TomcatEmbeddedServletContainerFactory并启用jndi命名并添加资源
    2. 使用 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 名称,看看它是否有效。
    猜你喜欢
    • 2019-06-30
    • 2017-01-20
    • 2015-04-18
    • 2017-10-21
    • 2015-03-23
    • 2018-08-08
    • 2014-12-24
    • 2017-05-28
    • 2015-07-31
    相关资源
    最近更新 更多