2014-10-09 21 views
3

我試圖在Spring Boot 1.1.6 web項目中將數據源從c3p0切換到Tomcat JNDI。我在GitHub中找到了一個示例應用程序,它在從@RestController帶註釋的類訪問DataSource實例時工作正常。Spring Boot - Tomcat JNDI在服務bean中失敗

@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註釋豆,我儘快得到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 

我不知道爲什麼JNDI數據源豆不能從@服務類訪問?有任何想法嗎 ?

+0

你是怎麼指定容器拿起@Service註釋的? – mreiterer 2014-10-09 12:12:18

+0

當應用程序上下文啓動時,它會被篡改一次。 – 2014-10-09 12:35:45

+0

你確定DI容器拿起它嗎?你在使用組件掃描嗎? – mreiterer 2014-10-09 12:37:52

回答

4

實際的問題是您試圖獲取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問題,這也提供了更多的問題的見解。

我希望這能回答你的問題。

+1

來到這裏有一個不同的問題,但這個答案給了我完全的原因。在我的自定義上下文類加載器的層次結構中沒有WebappClassLoader,因此沒有找到jndi對象。謝謝。 – 2015-08-26 08:33:45