Java 使用嵌入式容器和JNDI运行Spring启动应用程序

Java 使用嵌入式容器和JNDI运行Spring启动应用程序,java,maven,spring-boot,spring-data,jndi,Java,Maven,Spring Boot,Spring Data,Jndi,我有一个带有Spring.datasource.jndi name=java:/foo属性的Spring启动应用程序,它在WildFly下运行良好 我希望使用嵌入式容器运行同一个应用程序,即mvn spring boot:run,但尽管WildFly在其配置中配置了JNDI数据源,但嵌入式容器没有,即,我获得: org.springframework.jdbc.datasource.lookup.DataSourceLookupFailureException: Failed to look u

我有一个带有
Spring.datasource.jndi name=java:/foo
属性的Spring启动应用程序,它在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展示了如何在Tomcat中创建JNDI上下文,从而打破在其他容器(例如WildFly)中运行同一应用的局面。我正在寻找一个答案,让应用程序在不同的容器中使用相同的源运行,例如,仅使用WildFly中配置的相同JNDI资源配置嵌入式容器。

要使应用程序也可以部署在其他支持JNDI的容器中,请执行以下操作:

  • 扩展TomcatEmbeddedServletContainerFactory,启用jndi命名并添加资源
  • 使用概要文件注释创建一个配置类,该类公开扩展的
    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配置中这样做,您可以用xml方式这样做

    <beans profile="embedded">
        <bean id="TomcatEmbeddedServletContainerFactory" class="EmbeddedServletContainerFactory" />
    </bean>
    

    其他代码保持不变,在其他容器中的行为也相同。通过jndi查找数据源也保持不变。这段代码确保在嵌入式容器中实际上有一个绑定到该jndi的数据源

    要使应用程序也可部署在其他支持jndi的容器中,请执行以下操作:

  • 扩展TomcatEmbeddedServletContainerFactory,启用jndi命名并添加资源
  • 使用概要文件注释创建一个配置类,该类公开扩展的
    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配置中这样做,您可以用xml方式这样做

    <beans profile="embedded">
        <bean id="TomcatEmbeddedServletContainerFactory" class="EmbeddedServletContainerFactory" />
    </bean>
    

    其他代码保持不变,在其他容器中的行为也相同。通过jndi查找数据源也保持不变。这段代码确保在嵌入式容器中实际上有一个绑定到该jndi的数据源

    @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");
    

    @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");
    

    注意:我正在寻找一个仅配置的解决方案,例如,不涉及修改源(如果可能的话)。将
    @Profile
    注释添加到TomcateMbeddedServletContainerFactorybean定义中,然后在通过jvm参数运行时只启用该概要文件。这确保在其他容器中部署时不会发生任何更改。嵌入式tomcat在运行时直接从tomcat JAR运行,因此没有要定义的资源的任何配置文件。注意:我正在寻找一个仅配置的解决方案,例如,不涉及修改源(如果可能的话)。将
    @Profile
    注释添加到TomcatEmbeddedServletContainerFactory bean定义中,然后在通过jvm参数运行时仅启用该概要文件。这确保在其他容器中部署时不会发生任何更改。嵌入式tomcat在运行时直接从tomcat JAR运行,因此没有要定义的资源的任何配置文件。您将
    “jdbc/test”
    设置为JNDI名称,是否应该是例如
    “java:/jdbc/test”
    ?我之所以这样问是因为我的JNDI名称是
    java:/datasources/xxx
    ,而您的代码中我得到的
    name[datasources/xxx]在这个上下文中没有绑定。找不到[数据源]。
    这是我公开资源时使用的名称。我的查找代码使用带有完整jndi名称的spring
    JndiDataSourceLookup
    类,即
    java:/comp/env/jdbc/test
    。如果不想在application.properties文件中更改查找jndi名称,则可以在查找之前将resourceRef设置为true。我不确定应在何处设置
    resourceRef
    属性。我没有显式地做任何查找,它都是由SpringAfaik自动完成的。进一步测试后,我认为它有点复杂。我的应用程序的工作方式是,在公开资源时,我给它起了一个不带冒号(:)的名称jdbc/test,在查找数据源时,我使用jndi名称java:comp/env/jdbc/test,或者只使用jdbc/test,两者都可以正常工作。但是,如果为了测试,我以冒号的形式公开了我的资源,即java:/jdbc/test,那么我必须在查找jndi之前加上java:/comp/env,即java:/comp/env/java:/jdbc/test。我目前无法找到背后的原因,但如果我这样做,将更新答案。您最好的办法是现在给我们完整的jndi名称,看看它是否有效。您将
    “jdbc/test”
    设置为jndi名称,是否应该是例如
    “java:/jdbc/test”
    ?我这么问是因为我的JNDI名字