java - @资源错误: "Naming binding already exists for foo.NewServlet/userName in namespace"

标签 java servlets jakarta-ee jndi servlet-3.0

我正在考虑使用 servlet 3.0+ 容器中可用的“@Resource String ...”注入(inject)来轻松地向 servlet 提供配置参数。我希望默认值能够工作,但如果 JNDI 中不存在 key (表明配置错误),则默认值会失败

我在 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”来看看如何定义它。 Glassfish 4.1.1 似乎创建了“java:comp/env/foo:NewServlet/fullName”名称作为 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 <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="abf9ced8c4ded9c8ceebc1caddca85c7cac5cc85f8dfd9c2c5cc" rel="noreferrer noopener nofollow">[email protected]</a>@!USERNAME!@@, java:module/env/foo.NewServlet/fullName=Env-Prop: java:comp/env/foo.NewServlet/fullName@Non-Injectable <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="a0f2c5d3cfd5d2c3c5e0cac1d6c18eccc1cec78ef3d4d2c9cec7" rel="noreferrer noopener nofollow">[email protected]</a>@!BAR!@@}

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

  1. 要么不使用容器生成的默认值向 @Resource 标签提供任何提示,要么仅使用“our.application.fullName”之类的键。
  2. 如果出现任何问题,包括键值不存在,请大声失败。

建议?一个好的答案将给予 500 点奖励。

最佳答案

您实际上只错过了两个重要细节:

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

         <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>
    

因此,如果您在 web.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 上)。这可能是由于对注入(inject)的 CDI 规范有所延迟,该规范似乎没有太多关于无法找到注入(inject)资源的说明。

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

关于java - @资源错误: "Naming binding already exists for foo.NewServlet/userName in namespace",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45709634/

相关文章:

java - Glassfish 5.1 服务器在启用安全管理后无法启动(NoClassDefFoundError : sun/security/ssl/HelloExtension)

java - JPA 双向未知列

java - Java SSLSocket 问题

javascript - 使用servlet从/WEB-INF/异步加载javascript : general concern

java - 如何使用 PPTP android 创建自动连接到 Vpn 的应用程序?

java - 用于浏览器缓存的 Servlet 过滤器?

java - 无法在 Jersey 中获取 POST 请求的有效负载

jakarta-ee - 设置类加载顺序以在 Java EE 应用程序中更喜欢应用程序类的缺点是什么

java - 有一个sql PreparedStatement池有意义吗?

java - 内存不足 PermGen 空间