Java-Spring Boot-Tomcat JNDI在服务bean中失败

我正在尝试在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?有任何想法吗 ?

解决方法:

实际的问题是您正在尝试在PostConstruct中获取数据源,而不是它在服务bean中专门失败.

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

以下是org.apache.naming.ContextBindings.getClassLoader的代码片段,该代码执行上述检查:

/**
 * 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问题,这也提供了对该问题的更多见解.

我希望这回答了你的问题.

上一篇:java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析


下一篇:JNDI Java 命名与目录接口