@Resource错误:"名称绑定已存在于名称空间中的foo.NewServlet / userName"

时间:2017-08-16 09:17:27

标签: java servlets java-ee jndi servlet-3.0

我正在考虑使用" @Resource String ..." servlet 3.0+容器中提供了注入,可以轻松地为servlet提供配置参数。如果JNDI中没有密钥(表示配置错误),我希望默认值能够工作并失败

我在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
}

没有&#34; web.xml&#34;字段只是空(并且没有失败)。然后我玩了#34; web.xml&#34;看看我如何定义这个。 &#34; java:comp / env / foo:NewServlet / fullName&#34; name是Glassfish 4.1.1似乎创建的名称,作为fullName setter的名称。

<?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/userName@Non-Injectable Resource@java.lang.String@!USERNAME!@@, java:module/env/foo.NewServlet/fullName=Env-Prop: java:comp/env/foo.NewServlet/fullName@Non-Injectable Resource@java.lang.String@!BAR!@@}

项目中除了这两个文件之外别无其他。显然我误解了一些基本的东西,但是阅读Java EE教程并搜索建议对我没有帮助。我真的很想要两件事:

  1. 使用容器生成的默认值或仅使用&#34; our.application.fullName&#34;等键来向@ Resource-tag提供任何提示。
  2. 如果出现任何问题,请大声失败,包括键值不存在。
  3. 连连呢?一个好的答案将给予500点奖励。

1 个答案:

答案 0 :(得分:1)

你真的只错过了两个重要的细节:

  1. 在部署描述符(例如web.xml)中指定资源的名称时,无论是env-entry-nameresource-env-ref-name还是ejb-ref-name等, JNDI名称的java:comp/env部分始终是隐式。因此,如果您希望env-entry定义的资源在java:comp/env/foo的JNDI中显示,那么您将其env-entry-name指定为:

         <env-entry-name>foo</env-entry-name>
    
  2. Java EE规范(§EE.5.2.5)修改了应用于@Resource注释的“默认”名称的规则:

      

    一个类的字段可能是注入的目标。该领域不能是最终的。默认情况下,该字段的名称与该类的完全限定名称组合,并直接用作应用程序组件命名上下文中的名称。例如,com.example包中MyApp类中名为myDatabase的字段将对应于JNDI名称java:comp / env / com.example.MyApp / myDatabase。注释还允许显式指定JNDI名称。使用部署描述符条目指定注入时,将明确指定JNDI名称和字段名称。请注意,默认情况下,JNDI名称是相对于java:comp / env命名上下文。

    换句话说,如果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>
    
  3. 因此,如果您在web.xml文件中使用这些完全限定名称,那么您可以按照您的要求愉快地声明带注释的字段:

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

    现在该规范的相同章节指出:

      

    如果容器未能找到注入所需的资源,则该类的初始化必须失败,并且该类不得投入使用。

    然而,这在实践中似乎没有发生(至少在GlassFish上为你和WildFly对我来说)。这可能是由于CDI注射规范的某些延迟,对于未能找到注射资源似乎没什么好说的。

    因此,我们可能会在init方法或@PostConstruct带注释的方法中验证这些字段。