2017-08-16 32 views
2

我正在使用servlet 3.0+容器中提供的「@Resource String ...」注入來輕鬆地向servlet提供配置參數。如果密鑰不存在於JNDI中(表示配置錯誤),我希望缺省值能夠工作並失敗@Resource error:「命名空間中foo.NewServlet/userName的命名綁定已存在」

我已經在Netbeans 8.2中用Glassfish 4.1.1玩過一個簡單的servlet,我希望userName場,和setFullName(String fullName)集:

package foo; 

import java.io.IOException; 
import java.io.PrintWriter; 
import javax.annotation.Resource; 
import javax.servlet.ServletException; 
import javax.servlet.annotation.WebServlet; 
import javax.servlet.annotation.WebInitParam; 
import javax.servlet.http.HttpServlet; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 

@WebServlet(name = "NewServlet", urlPatterns = {"/NewServlet"}) 
public class NewServlet extends HttpServlet { 

    @Resource(description="user name") 
    String userName; 

    private String fullname; 

    @Resource() 
    public void setFullName(String fullName){ 
     this.fullname = fullName; 
    } 

    protected void processRequest(HttpServletRequest request, HttpServletResponse response) 
      throws ServletException, IOException { 
     response.setContentType("text/html;charset=UTF-8"); 
     try (PrintWriter out = response.getWriter()) { 
      /* TODO output your page here. You may use following sample code. */ 
      out.println("<!DOCTYPE html>"); 
      out.println("<html>"); 
      out.println("<head>"); 
      out.println("<title>Servlet NewServlet</title>");    
      out.println("</head>"); 
      out.println("<body>"); 
      out.println("Full name = " + fullname); 
      out.println("<h1>Servlet NewServlet at " + request.getContextPath() + "</h1>"); 
      out.println("Username = " + userName); 
      out.println("</body>"); 
      out.println("</html>"); 


     } 
    } 
// Autogenerated stuff omitted 
} 

沒有 「web.xml中」 的字段爲空只(無故障)。然後我玩弄了「web.xml」,看看我可以如何定義它。 「java:comp/env/foo:NewServlet/fullName」名稱是Glassfish 4.1.1似乎創建的全名設置器的名稱。

<?xml version="1.0" encoding="UTF-8"?> 
<web-app xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
    version="3.0"> 
    <env-entry > 
     <env-entry-name>java:comp/env/foo.NewServlet/fullName</env-entry-name> 
     <env-entry-type>java.lang.String</env-entry-type> 
     <env-entry-value>!BAR!</env-entry-value> 
    </env-entry> 
    <env-entry > 
     <env-entry-name>java:comp/env/foo.NewServlet/userName</env-entry-name> 
     <env-entry-type>java.lang.String</env-entry-type> 
     <env-entry-value>!USERNAME!</env-entry-value> 
    </env-entry> 
</web-app> 

這再失敗,

Severe: Exception while deploying the app [WebApplication4] : Naming binding already exists for foo.NewServlet/userName in namespace {java:module/env/foo.NewServlet/userName=Env-Prop: java:comp/env/foo.NewServlet/[email protected] [email protected]@[email protected]@, java:module/env/foo.NewServlet/fullName=Env-Prop: java:comp/env/foo.NewServlet/[email protected] [email protected]@[email protected]@} 

沒有什麼別的,但在該項目這兩個文件。顯然我誤解了一些基本的東西,但是閱讀Java EE教程並尋找建議並沒有幫助我。我真的很喜歡兩件事:

  1. 要麼使用容器生成的默認值或只是像「我們的應用程序。全名」這樣的鍵提供任何提示@資源標記。
  2. 如果有任何錯誤,包括鍵值不存在,則會大聲地失敗。

建議?一個好的答案會給500點獎勵。

+0

在Wildfly/Glassfish行爲上打開的單獨問題 - https://stackoverflow.com/q/45716813/53897 –

回答

1

你真的只錯過了兩個重要的細節:

  1. 當指定的部署描述符的資源的名稱(如web.xml)中,無論是env-entry-nameresource-env-ref-nameejb-ref-name, JNDI名稱的java:comp/env部分總是隱含。因此,如果你想通過env-entry定義的資源在JNDI在java:comp/env/foo出現,那麼你指定其env-entry-name爲:

     <env-entry-name>foo</env-entry-name> 
    
  2. Java EE規範(§EE.5.2.5)修改了規則「默認」的名稱應用於@Resource註釋:

    A field of a class may be the target of injection. The field must not be final. By default, the name of the field is combined with the fully qualified name of the class and used directly as the name in the application component’s naming context. For example, a field named myDatabase in the class MyApp in the package com.example would correspond to the JNDI name java:comp/env/ com.example.MyApp/myDatabase. The annotation also allows the JNDI name to be specified explicitly. When a deployment descriptor entry is used to specify injection, the JNDI name and the field name are both specified explicitly. Note that, by default, the JNDI name is relative to the java:comp/env naming context.

    換句話說,如果你的servlet的全名是com.p45709634.NewServlet,那麼userName場的JNDI名稱將是java:comp/env/com.p45709634.NewServlet/userName。因此,其env-entry-name將是:

     <env-entry-name>com.p45709634.NewServlet/userName</env-entry-name> 
    

所以,如果你在你的網絡中使用這些完全合格的名稱。XML文件,那麼你可以愉快地宣佈註釋字段按你們的要求:

@Resource 
    private String userName; 

    private String fullname; 

    @Resource 
    public void setFullName(String fullName){ 
     this.fullname = fullName; 
    } 

現在規範規定的同一章:

If the container fails to find a resource needed for injection, initialization of the class must fail, and the class must not be put into service.

然而,這似乎並沒有在實踐中發生的(至少在GlassFish上爲你和WildFly爲我)。這可能是由於CDI注射劑的某些推遲,這似乎沒有說明注射資源的定位問題。

因此,我們可能會停留在init方法或@PostConstruct帶註釋的方法中驗證這些字段。

+0

我從env-entry-name標記中刪除了「java:/ comp/env」部分,並且它工作正常!謝謝!我猜錯誤消息是關於「/ comp/env」已經被綁定,而不是已經使用的實際密鑰。我想知道這是一個已知的bug還是Wildfly/Glassfish的一個功能,並且它是否可以使用開關來執行?無論如何,我會盡可能給你賞金。提醒我,如果我忘了。 –

相關問題