【问题标题】:Spring Boot - Tomcat JNDI fails in Service beansSpring Boot - Tomcat JNDI 在服务 bean 中失败
【发布时间】:2014-12-04 08:35:31
【问题描述】:

我正在尝试在 Spring Boot 1.1.6 Web 项目中将数据源从 c3p0 切换到 Tomcat JNDI。我在GitHub 中找到了一个示例应用程序,当从@RestController 带注释的类访问DataSource 实例时,它可以正常工作。

@RestController
public class TestController {

  @Autowired
  private DataSource dataSource;

  @RequestMapping("/test")
  @ResponseBody
  public String test() {

  // Gets object instance... everything is OK...
  System.out.println(this.dataSource); 
  }

但是,当我尝试将相同的数据源注入 @Service 带注释的 bean 时,只要在代码中使用实例,我就会得到 javax.naming.NameNotFoundException

@Service
public class TestService {

  @Autowired
  private DataSource dataSource;

  @PostConstruct
  private void init() {

    // Throws exception...
    System.out.println(this.dataSource);

}

堆栈跟踪:

Caused by: javax.naming.NameNotFoundException: Name [java:comp/env/jdbc/myDataSource] is not bound in this Context. Unable to find [java:comp].
    at org.apache.naming.NamingContext.lookup(NamingContext.java:819)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:167)
    at javax.naming.InitialContext.lookup(InitialContext.java:411)
    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.JndiObjectTargetSource.getTarget(JndiObjectTargetSource.java:135)
    ... 11 more

我想知道为什么无法从@Service 类访问 JNDI 数据源 bean?有什么想法吗?

【问题讨论】:

  • 你是如何指定容器拾取@Service注解的?
  • 应用上下文启动时实例化一次。
  • 您确定 DI 容器会接收到它吗?你在使用组件扫描吗?
  • 是的,主类用@ComponentScan注解。
  • 你是如何配置 jndi 模板和数据源 bean 的?

标签: java spring tomcat jndi


【解决方案1】:

实际问题是您试图在 PostConstruct 中获取数据源,而不是它在服务 bean 中特别失败。

默认情况下,Tomcat 使用线程上下文类加载器来确定应针对哪个 JNDI 上下文执行查找。因此,当您将资源绑定到 Web 应用程序的 JNDI 上下文时,您需要确保在 Web 应用程序的类加载器(即org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedWebappClassLoader)是线程上下文类加载器时执行查找。

以下是执行上述检查的org.apache.naming.ContextBindings.getClassLoader 代码的 sn-p:

/**
 * Retrieves the naming context bound to a class loader.
 */
public static Context getClassLoader()
    throws NamingException {
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    Context context = null;
    do {
        context = clBindings.get(cl);
        if (context != null) {
            return context;
        }
    } while ((cl = cl.getParent()) != null);
    throw new NamingException
        (sm.getString("contextBindings.noContextBoundToCL"));
}

因此,当代码在PostConstruct 中并且您执行 JNDI 查找(在您访问对象时隐式完成)时,线程上下文类加载器仍然是应用程序类加载器(即 sun.misc.Launcher$AppClassLoader)。这就是 JNDI 无法找到对象的原因,因为它试图从 sun.misc.Launcher$AppClassLoader 中找到 java:comp/env/jdbc/myDataSource 命名上下文(即 javax.naming.Context)。

您可以从PostConstruct 移动代码,它应该可以正常工作。

交叉链接另一个related 问题,这也提供了对该问题的更多见解。

我希望这能回答你的问题。

【讨论】:

  • 带着一些不同的问题来到这里,但这个答案正是我的原因。我的自定义上下文类加载器的层次结构中没有 WebappClassLoader,因此找不到 jndi 对象。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-11
  • 1970-01-01
  • 1970-01-01
  • 2019-03-14
  • 2021-05-02
  • 2017-05-21
相关资源
最近更新 更多