如何在Spring Boot中使用嵌入式Tomcat容器创建JNDI上下文

60
import org.apache.catalina.Context;
import org.apache.catalina.deploy.ContextResource;
import org.apache.catalina.startup.Tomcat;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

@Configuration
@EnableAutoConfiguration
@ComponentScan
@ImportResource("classpath:applicationContext.xml")
public class Application {

    public static void main(String[] args) throws Exception {
        new SpringApplicationBuilder()
                .showBanner(false)
                .sources(Application.class)
                .run(args);
}

@Bean
public TomcatEmbeddedServletContainerFactory tomcatFactory() {
    return new TomcatEmbeddedServletContainerFactory() {
        @Override
        protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
                Tomcat tomcat) {
            tomcat.enableNaming();
            return super.getTomcatEmbeddedServletContainer(tomcat);
        }
    };
}

@Bean
public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer() {
    return new EmbeddedServletContainerCustomizer() {
        @Override
        public void customize(ConfigurableEmbeddedServletContainer container) {
            if (container instanceof TomcatEmbeddedServletContainerFactory) {
                TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory = (TomcatEmbeddedServletContainerFactory) container;
                tomcatEmbeddedServletContainerFactory.addContextCustomizers(new TomcatContextCustomizer() {
                    @Override
                    public void customize(Context context) {
                        ContextResource mydatasource = new ContextResource();
                        mydatasource.setName("jdbc/mydatasource");
                        mydatasource.setAuth("Container");
                        mydatasource.setType("javax.sql.DataSource");
                        mydatasource.setScope("Sharable");
                        mydatasource.setProperty("driverClassName", "oracle.jdbc.driver.OracleDriver");
                        mydatasource.setProperty("url", "jdbc:oracle:thin:@mydomain.com:1522:myid");
                        mydatasource.setProperty("username", "myusername");
                        mydatasource.setProperty("password", "mypassword");

                        context.getNamingResources().addResource(mydatasource);

                    }
                });
            }
        }
    };
}
我正在使用Spring Boot,并尝试使用嵌入式Tomcat启动,以创建一个JNDI上下文来访问我的数据源:
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
        <version>1.1.4.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <version>1.1.4.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-oracle</artifactId>
        <version>1.0.0.RELEASE</version>
    </dependency>

如果我删除@ImportResource,我的应用程序就可以正常启动。 我可以连接到Tomcat实例。 我可以检查所有的执行器端点。 使用JConsole,我可以连接到应用程序,可以在MBeans中看到我的数据源(Catalina->资源->上下文->“/”->localhost->javax.sql.DataSource->jdbc / mydatasource)。

同样,使用JConsole,这里也会显示MBeans(Tomcat->DataSource->/->localhost->javax.sql.DataSource->jdbc / mydatasource)。

但是,当我使用@ImportResource通过JNDI寻找mydatasource时,它找不到它。

<bean id="myDS" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/jdbc/mydatasource"/>
</bean>

我导入的xml文件中相关的部分

我上面配置的ContextResource与我在部署应用程序到Tomcat容器时使用的context.xml中完全相同的参数。当我的导入的bean和应用程序部署到Tomcat容器时,它们可以正常工作。

因此,现在似乎我有了一个上下文,但名称似乎不正确。我已经尝试了各种资源名称的组合,但似乎不能在这个上下文中生成一个“comp”绑定。

Caused by: javax.naming.NameNotFoundException: Name [comp/env/jdbc/mydatasource] is not bound in this Context. Unable to find [comp].
    at org.apache.naming.NamingContext.lookup(NamingContext.java:819)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:167)
    at org.apache.naming.SelectorContext.lookup(SelectorContext.java:156)
    at javax.naming.InitialContext.lookup(InitialContext.java:392)
    at org.springframework.jndi.JndiTemplate$1.doInContext(JndiTemplate.java:155)
    at org.springframework.jndi.JndiTemplate.execute(JndiTemplate.java:87)
    at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:152)
    at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:179)
    at org.springframework.jndi.JndiLocatorSupport.lookup(JndiLocatorSupport.java:95)
    at org.springframework.jndi.JndiObjectLocator.lookup(JndiObjectLocator.java:106)
    at org.springframework.jndi.JndiObjectFactoryBean.lookupWithFallback(JndiObjectFactoryBean.java:231)
    at org.springframework.jndi.JndiObjectFactoryBean.afterPropertiesSet(JndiObjectFactoryBean.java:217)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1612)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1549)
    ... 30 more
8个回答

68

默认情况下,嵌入式Tomcat禁用了JNDI,这会导致NoInitialContextException。您需要调用Tomcat.enableNaming()来启用它。最简单的方法是使用TomcatEmbeddedServletContainer子类:

@Bean
public TomcatEmbeddedServletContainerFactory tomcatFactory() {
    return new TomcatEmbeddedServletContainerFactory() {

        @Override
        protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
                Tomcat tomcat) {
            tomcat.enableNaming();
            return super.getTomcatEmbeddedServletContainer(tomcat);
        }
    };
}

如果您采用这种方法,还可以通过在TomcatEmbeddedServletContainerFactory子类中覆盖postProcessContext方法来将DataSource注册到JNDI中。 context.getNamingResources().addResource将资源添加到java:comp/env上下文中,因此资源的名称应为jdbc/mydatasource而不是java:comp/env/mydatasource
Tomcat使用线程上下文类加载器确定应针对哪个JNDI上下文执行查找。您正在将资源绑定到Web应用程序的JNDI上下文中,因此需要确保在Web应用程序的类加载器是线程上下文类加载器时执行查找。您可以通过在jndiObjectFactoryBean上设置lookupOnStartupfalse来实现这一点。您还需要将expectedType设置为javax.sql.DataSource
<bean class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/jdbc/mydatasource"/>
    <property name="expectedType" value="javax.sql.DataSource"/>
    <property name="lookupOnStartup" value="false"/>
</bean>

这将为DataSource创建一个代理,实际的JNDI查找将在第一次使用时执行,而不是在应用程序上下文启动期间执行。
上述方法的示例可在Spring Boot示例中看到。

我认为你的提示让我更接近了答案。尽管资源在JConsole中显示出来,但我仍然无法找到它们。 - DaShaun
1
我在实现这个过程中遇到了困难,至少在使用Graisl 3.0时是这样的。是否有更新的方法来执行此操作?你们概述的方法似乎不再起作用。 - angryip
这里创建的JNDI上下文是否可在整个JVM中使用,即使是未由Spring实例化的对象也可以使用?我得到了一个NameNotFound错误,详细信息为“无法找到[java:comp]”。 - KC Baltz
1
可以使用在"application.properties"文件中基于"spring.datasource.*"属性生成的Spring DataSource,而不是在"postProcessContext"方法中定义DataSource吗? - KC Baltz
4
这对我不起作用,我收到了 javax.naming.NoInitialContextException 错误:需要在环境或系统属性中指定类名,或者作为小应用程序参数、或在应用程序资源文件中定义: java.naming.factory.initial。我已经将我的 jndi 资源定义为其他文件中的 beans。 - Krzysztof Krasoń
显示剩余6条评论

31
我最近有一个需求,需要在Spring Boot中使用嵌入式Tomcat的JNDI。
实际上,一些答案给出了一些有趣的提示来解决我的任务,但是对于Spring Boot 2可能不够更新。
这是我测试过的与Spring Boot 2.0.3.RELEASE兼容的贡献。 在运行时指定类路径中可用的数据源 你有多种选择:
- 使用DBCP 2数据源(不要使用已过时且效率较低的DBCP 1)。 - 使用Tomcat JDBC数据源。 - 使用其他任何数据源,例如HikariCP。
如果你没有指定其中任何一个,在默认配置下,数据源的实例化将会抛出异常:
Caused by: javax.naming.NamingException: Could not create resource factory instance
    at org.apache.naming.factory.ResourceFactory.getDefaultFactory(ResourceFactory.java:50)
    at org.apache.naming.factory.FactoryBase.getObjectInstance(FactoryBase.java:90)
    at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:321)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:839)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:159)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:827)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:159)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:827)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:159)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:827)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:173)
    at org.apache.naming.SelectorContext.lookup(SelectorContext.java:163)
    at javax.naming.InitialContext.lookup(InitialContext.java:417)
    at org.springframework.jndi.JndiTemplate.lambda$lookup$0(JndiTemplate.java:156)
    at org.springframework.jndi.JndiTemplate.execute(JndiTemplate.java:91)
    at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:156)
    at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:178)
    at org.springframework.jndi.JndiLocatorSupport.lookup(JndiLocatorSupport.java:96)
    at org.springframework.jndi.JndiObjectLocator.lookup(JndiObjectLocator.java:114)
    at org.springframework.jndi.JndiObjectTargetSource.getTarget(JndiObjectTargetSource.java:140)
    ... 39 common frames omitted
Caused by: java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:264)
    at org.apache.naming.factory.ResourceFactory.getDefaultFactory(ResourceFactory.java:47)
    ... 58 common frames omitted

要使用Apache JDBC数据源,您不需要添加任何依赖项,但您必须将默认工厂类更改为org.apache.tomcat.jdbc.pool.DataSourceFactory。 您可以在资源声明中进行此操作: resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory"); 我将在下面解释在哪里添加这行代码。
要使用DBCP 2数据源,需要一个依赖项:
<dependency>
  <groupId>org.apache.tomcat</groupId>
  <artifactId>tomcat-dbcp</artifactId>
  <version>8.5.4</version>
</dependency>

当然,根据你的Spring Boot Tomcat嵌入式版本来适应工件版本。
  • 要使用HikariCP,请添加所需的依赖项(如果尚未在配置中存在,可能已经存在于Spring Boot的持久性启动器中):
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>3.1.0</version>
</dependency>

并在资源声明中指定与之配套的工厂。
resource.setProperty("factory", "com.zaxxer.hikari.HikariJNDIFactory");

数据源配置/声明

您需要自定义创建TomcatServletWebServerFactory实例的bean。
需要执行两个步骤:

  • 启用默认情况下禁用的JNDI命名

  • 在服务器上下文中创建并添加JNDI资源

例如,对于PostgreSQL和DBCP 2数据源,执行以下操作:

@Bean
public TomcatServletWebServerFactory tomcatFactory() {
    return new TomcatServletWebServerFactory() {
        @Override
        protected TomcatWebServer getTomcatWebServer(org.apache.catalina.startup.Tomcat tomcat) {
            tomcat.enableNaming(); 
            return super.getTomcatWebServer(tomcat);
        }

        @Override 
        protected void postProcessContext(Context context) {

            // context
            ContextResource resource = new ContextResource();
            resource.setName("jdbc/myJndiResource");
            resource.setType(DataSource.class.getName());
            resource.setProperty("driverClassName", "org.postgresql.Driver");

            resource.setProperty("url", "jdbc:postgresql://hostname:port/dbname");
            resource.setProperty("username", "username");
            resource.setProperty("password", "password");
            context.getNamingResources()
                   .addResource(resource);            
        }
    };
}

这里是Tomcat JDBC和HikariCP数据源的变体。
postProcessContext()中,根据之前对Tomcat JDBC ds的解释,设置工厂属性。
@Override 
protected void postProcessContext(Context context) {
    ContextResource resource = new ContextResource();        
    //...
    resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
    //...
    context.getNamingResources()
           .addResource(resource);            
}

对于HikariCP:
@Override 
protected void postProcessContext(Context context) {
    ContextResource resource = new ContextResource();        
    //...
    resource.setProperty("factory", "com.zaxxer.hikari.HikariJNDIFactory");
    //...
    context.getNamingResources()
           .addResource(resource);            
}

使用/注入数据源

现在您应该可以通过使用标准的InitialContext实例在任何地方查找JNDI资源:

InitialContext initialContext = new InitialContext();
DataSource datasource = (DataSource) initialContext.lookup("java:comp/env/jdbc/myJndiResource");

您还可以使用Spring的JndiObjectFactoryBean来查找资源。
JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
bean.setJndiName("java:comp/env/jdbc/myJndiResource");
bean.afterPropertiesSet();
DataSource object = (DataSource) bean.getObject();

为了充分利用 DI 容器,您也可以将 DataSource 设置为 Spring bean。
@Bean(destroyMethod = "")
public DataSource jndiDataSource() throws IllegalArgumentException, NamingException {
    JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
    bean.setJndiName("java:comp/env/jdbc/myJndiResource");
    bean.afterPropertiesSet();
    return (DataSource) bean.getObject();
}

现在你可以在任何Spring bean中注入DataSource,例如:
@Autowired
private DataSource jndiDataSource;

请注意,互联网上的许多示例似乎在启动时禁用了对JNDI资源的查找。
bean.setJndiName("java:comp/env/jdbc/myJndiResource");
bean.setProxyInterface(DataSource.class);
bean.setLookupOnStartup(false);
bean.afterPropertiesSet(); 

但是我认为这是无助的,因为它在afterPropertiesSet()之后调用了查找!

3
首先,非常感谢你。这不仅是一个描述如何在Spring Boot 2的嵌入式Tomcat中使用JNDI的好文章,也是我找到的为数不多的资源之一。再次感谢。 需要注意的是,我需要使用setProxyInterface / setLookupOnState调用来避免InstanceAlreadyExistsException异常。 - bwags
1
@bwags 感谢您的好评。我大约需要花整整一天的时间才能让它正确运行:通常旧的解决方法没有很好的文档记录...但非常高兴这篇文章可以帮助其他人解决这个不常见的需求。关于你最后提到的那点,很有意思,我删除这两个语句后并没有遇到这个异常。 - davidxxx
4
对我来说,使用Hikari不起作用,我不得不将工厂行更改为resource.setProperty("factory", "com.zaxxer.hikari.HikariJNDIFactory");(而不是HikariDataSource) - Vincent V.
感谢您对所有内容进行了详细的解释。由于问题非常通用且其他答案都没有帮助我,因此这可以转换为博客。向您致敬! - Ayush Kumar

15

最终我通过Wikisona得到了答案,首先是豆子:

@Bean
public TomcatEmbeddedServletContainerFactory tomcatFactory() {
    return new TomcatEmbeddedServletContainerFactory() {

        @Override
        protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
                Tomcat tomcat) {
            tomcat.enableNaming();
            return super.getTomcatEmbeddedServletContainer(tomcat);
        }

        @Override
        protected void postProcessContext(Context context) {
            ContextResource resource = new ContextResource();
            resource.setName("jdbc/myDataSource");
            resource.setType(DataSource.class.getName());
            resource.setProperty("driverClassName", "your.db.Driver");
            resource.setProperty("url", "jdbc:yourDb");

            context.getNamingResources().addResource(resource);
        }
    };
}

@Bean(destroyMethod="")
public DataSource jndiDataSource() throws IllegalArgumentException, NamingException {
    JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
    bean.setJndiName("java:comp/env/jdbc/myDataSource");
    bean.setProxyInterface(DataSource.class);
    bean.setLookupOnStartup(false);
    bean.afterPropertiesSet();
    return (DataSource)bean.getObject();
}

完整的代码在这里:https://github.com/wilkinsona/spring-boot-sample-tomcat-jndi


1
当我尝试使用上述代码时,出现了“java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory”的错误。如果我通过“application.properties”定义tomcat db pools,则一切正常。我已经使用了Spring Boot依赖项,但似乎我在JNDI方面缺少了一些东西。这是针对嵌入式Tomcat版本8.5.x的。 - Sabir Khan
1
我通过添加以下代码行解决了问题:resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");我的SO问题链接 或者我需要添加单独的dbcp依赖,但这在Spring Boot中似乎不是一个好主意。 - Sabir Khan
@Sabir Khan,使用Tomcat jdbc DS是一个不错的选择。但我认为使用其他DS也没有错。Spring Boot并没有以开箱即用的方式涵盖这一部分,因为在Spring Boot中直接使用JNDI并不是标准做法,但有时我们不想受限于Tomcat jdbc DS或者不想使用它。 - davidxxx

5
在SpringBoot 2.1中,我发现了另一种解决方案。扩展标准工厂类方法getTomcatWebServer。然后从任何地方将其作为bean返回。
public class CustomTomcatServletWebServerFactory extends TomcatServletWebServerFactory {

    @Override
    protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
        System.setProperty("catalina.useNaming", "true");
        tomcat.enableNaming();
        return new TomcatWebServer(tomcat, getPort() >= 0);
    }
}

@Component
public class TomcatConfiguration {
    @Bean
    public ConfigurableServletWebServerFactory webServerFactory() {
        TomcatServletWebServerFactory factory = new CustomTomcatServletWebServerFactory();

        return factory;
    }

虽然从context.xml中加载资源不起作用。 我们将尝试找出原因。


2
请注意,这里使用的是“而不是”的意思,而不是“请注意替换”。
public TomcatEmbeddedServletContainerFactory tomcatFactory()

我必须使用以下方法签名

public EmbeddedServletContainerFactory embeddedServletContainerFactory() 

2
在Spring Boot v3中,似乎以前的解决方案已经不再可行。
通过Spring文档,我找到了以下方法:
  1. 创建一个WebServerFactoryCustomizer并创建您的jndi资源
  2. 添加生命周期监听器以启用命名
  3. 添加Tomcat JDBC依赖项
自定义程序:
@Component
public class MyDatasourceJndiCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

  @Value("${mydatasource.jndi.name}")
  private String jndiName;

  @Value("${mydatasource.jndi.driver-class-name}")
  private String driverClassName;

  @Value("${mydatasource.jndi.url}")
  private String url;

  @Value("${mydatasource.username}")
  private String username;

  @Value("${jndi.password}")
  private String password;

  @Override
  public void customize(TomcatServletWebServerFactory server) {
    server.addContextCustomizers(new TomcatContextCustomizer() {
      @Override
      public void customize(Context context) {
        ContextResource resource = new ContextResource();
        resource.setName(jndiName);
        resource.setType(DataSource.class.getName());
        resource.setProperty("driverClassName", driverClassName);
        resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
        resource.setProperty("url", url);
        resource.setProperty("username", username);
        resource.setProperty("password", password);
        context.getNamingResources()
          .addResource(resource);
      }

    });

    enableNaming(server);
  }


  private static void enableNaming(TomcatServletWebServerFactory server) {
    server.addContextLifecycleListeners(new NamingContextListener());
    
    // The following code is copied from Tomcat 
    System.setProperty("catalina.useNaming", "true");
    String value = "org.apache.naming";
    String oldValue = System.getProperty("java.naming.factory.url.pkgs");
    if (oldValue != null) {
      if (oldValue.contains(value)) {
        value = oldValue;
      } else {
        value = value + ":" + oldValue;
      }
    }

    System.setProperty("java.naming.factory.url.pkgs", value);
    value = System.getProperty("java.naming.factory.initial");
    if (value == null) {
      System.setProperty("java.naming.factory.initial", "org.apache.naming.java.javaURLContextFactory");
    }
  }

}

依赖关系:

    <dependency>
      <groupId>org.apache.tomcat</groupId>
      <artifactId>tomcat-jdbc</artifactId>
      <version>10.1.9</version>
    </dependency>

1

你尝试过使用@Lazy加载数据源吗?因为你在Spring上下文中初始化嵌入式Tomcat容器,所以必须延迟初始化DataSource(直到设置完JNDI变量)。

N.B. 我还没有测试过这段代码!

@Lazy
@Bean(destroyMethod="")
public DataSource jndiDataSource() throws IllegalArgumentException, NamingException {
    JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
    bean.setJndiName("java:comp/env/jdbc/myDataSource");
    bean.setProxyInterface(DataSource.class);
    //bean.setLookupOnStartup(false);
    bean.afterPropertiesSet();
    return (DataSource)bean.getObject();
}

您可能还需要在使用DataSource的地方添加@Lazy注释,例如:
@Lazy
@Autowired
private DataSource dataSource;

1
它适用于我的情况。 - Alessandro C

0
从头开始解决这个问题,因为我遇到的所有示例都对我无效。 看起来它取决于嵌入式应用程序的配置...
目标:将旧版应用程序作为war文件与JNDI资源一起运行,并在嵌入式服务器上运行。
下面的代码片段适用于Spring Boot 2和3,但要小心,因为Spring Boot 3使用Jakarta EE 9规范(具有新的顶级jakarta包),可能与您的应用程序不兼容(就像我的情况一样)。
主要痛点用//important标记出来。
@Bean
public TomcatServletWebServerFactory servletContainerFactory() {
    return new TomcatServletWebServerFactory() {
        @Override
        protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
            //add webapp
            try {
                //...app preparation here
                final Context context = tomcat.addWebapp(contextPath, application.getURL());
                context.setParentClassLoader(getClass().getClassLoader()); //important: helps the embedded app reach spring boot dependencies
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            //configure jndi
            tomcat.enableNaming(); //important: speaks for itself
            ContextResource resource = new ContextResource();
            resource.setType(DataSource.class.getName());
            resource.setName("jdbc/JNDI_NAME_HERE");
            resource.setProperty("factory", HikariJNDIFactory.class.getName());
            resource.setProperty("jdbcUrl", getUrl());
            resource.setProperty("driverClassName", getDriverClassName());
            tomcat.getServer().getGlobalNamingResources().addResource(resource); //important: solution for successful jndi lookup

            return super.getTomcatWebServer(tomcat);
        }
    };
}

总共只有3个步骤,其中jndi只有2个步骤。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接