我正在考虑使用" @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教程并搜索建议对我没有帮助。我真的很想要两件事:
连连呢?一个好的答案将给予500点奖励。
答案 0 :(得分:1)
你真的只错过了两个重要的细节:
在部署描述符(例如web.xml)中指定资源的名称时,无论是env-entry-name
,resource-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>
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>
因此,如果您在web.xml文件中使用这些完全限定名称,那么您可以按照您的要求愉快地声明带注释的字段:
@Resource
private String userName;
private String fullname;
@Resource
public void setFullName(String fullName){
this.fullname = fullName;
}
现在该规范的相同章节指出:
如果容器未能找到注入所需的资源,则该类的初始化必须失败,并且该类不得投入使用。
然而,这在实践中似乎没有发生(至少在GlassFish上为你和WildFly对我来说)。这可能是由于CDI注射规范的某些延迟,对于未能找到注射资源似乎没什么好说的。
因此,我们可能会在init
方法或@PostConstruct
带注释的方法中验证这些字段。