Java Servlet和JSP访问同一个Session Bean

12

假设我有一个简单的登录servlet,它检查传递的name并创建User对象,并将其存储在会话中。

User user = new User();
user.setId(name);

request.getSession().setAttribute("user", user);
response.sendRedirect("index.jsp");

index.jsp页面中,我通过jsp:useBean访问用户对象。

<jsp:useBean id="user" scope="session"
             class="package.name.User"/>

<div class="panel">
    Welcome ${user.id}
</div>

目前为止它是有效的。

从jsp beans文档中

按照以下顺序查找或实例化Bean:

  1. 尝试定位具有指定范围和名称的Bean。
  2. 使用您指定的名称定义对象引用变量。
  3. 如果找到了Bean,则在变量中存储对其的引用。 如果指定了类型,则将该类型赋予Bean。
  4. 如果未找到Bean,则从您指定的类实例化它,并在新变量中存储对其的引用。 如果类名表示序列化模板,则Java.beans.Beans.instantiate实例化Bean。
  5. 如果已经实例化了Bean(而不是定位),并且如果它具有body标签或元素(介于 and 之间),则执行body标签。

问题:

尝试定位具有指定范围和名称的Bean

它没有明确说明“定位”过程。这是否意味着它会检查HttpServletRequest.getSession(),还是仅检查其他页面是否已创建此bean?

如果未找到Bean,则从您指定的类实例化它,并在新变量中存储对其的引用。

这实际上意味着Jsp可以使用jsp_internal_name_user将新创建的bean与会话相关联。 没有提到Jsp如何在会话中存储和查找bean。

有一种选项可以使用${sessionScope.user}来访问会话对象,并保证获取Java会话对象中的"user"。同样的我自己放进去的那个。

Java EE 5示例“Book Store”使用${sessionScope.name}方法访问会话对象。

仅使用${user}就可以工作。这让我担心。我想在规范中看到关于“定位”过程以及${user}是否必须工作或者是由JSP和/或JSTL参考实现决定的特定句子。

3个回答

6

如果控制器(servlet)负责处理模型,则jsp:useBean仅在默认实例(使用无参构造函数构造的实例)与不存在的实例具有不同的行为/状态时才有用。例如,如果您想要一个默认用户名“未知用户”,则可以执行以下操作:

public User {
    this.id = "Unknown User";
}

否则,最终用户可能会看到 "欢迎" 而不是 "欢迎未知用户" 的显示。在您的特定情况下,您可以安全地将其删除。这是多余的。
然而,我也看到过这个参数对于纯文档非常有用。您可以在 JSP 页面顶部声明 "无用的" jsp:useBean 实例,以便您可以了解在特定的 JSP 页面中使用了哪些模型。虽然我觉得这很聪明,但我自己从来没有需要以这种方式记录 JSP 中的模型。根据评论,另一个争论确实是,这样 IDE(如 IDEA 和 Eclipse)就能够在 EL 中自动完成 bean 属性。
更新:关于定位,它使用 PageContext#findAttribute() 进行定位,然后使用反射/JavaBean 内省来调用 getter 方法。例如:
${user.name}

大致相当于

out.print(pageContext.findAttribute("user").getName())

此外,还可以参考JSP规范JSP EL规范
更新2:可以确定的是,<jsp:useBean>并不使用内部名称或会话属性前缀。请自行循环遍历所有会话属性以查看实际键和值:
<c:forEach items="${sessionScope}" var="entry">
    ${entry.key} = ${entry.value}<br>
</c:forEach>

或在servlet中

for (String name : Collections.list(session.getAttributeNames())) {
   System.out.println(name + " = " + session.getAttribute(name));
}

在Intellij IDEA中,只有在显式声明的情况下才会自动完成变量字段。 - Mykola Golubyev
1
  1. 你在说EL吗?请查看JSP EL规范。简而言之,它使用PageContext#findAttribute()来定位任何作用域中的属性。
  2. 那的确也是我以前见过的另一个论点。
- BalusC
谢谢指出。我参考了JSP规范,但没有找到有用的信息。现在我会查看EL规范。 - Mykola Golubyev
请问您能指出您在哪个部分找到了“大致解决为…”这句话吗? - Mykola Golubyev
请查看http://java.sun.com/javaee/5/docs/tutorial/doc/bnahq.html#bnahw。至于书店示例,这只是一个“显式”引用,以确保它不会返回页面或请求范围内的“巧合”`cart`实例(在会话范围之前扫描)。这只是一种“练习”,但并非必要。 - BalusC
显示剩余6条评论

5

引用JSP规范 JSP.5.1:

基本语义是使用id和scope查找现有对象。如果未找到该对象,则尝试使用其他属性创建该对象。

换句话说,

<jsp:useBean id="user" scope="session" class="package.name.User"/>

大致翻译成Java代码如下:

package.name.User user = (package.name.User)session.getAttribute("user");
if (user == null){
  user = new package.name.User();
  session.setAttribute("user", user);
}

嘿,很高兴在这里见到你 :) 对于特定的 jsp:usebean 行,你的答案确实是正确的,只是你忘记了在 if 块中添加 session.setAttribute("user", user); ;) - BalusC
我想查看“查找现有”过程的描述。实际上,JSP可以使用“user_jsp_internal”名称存储对象。 - Mykola Golubyev
不,它并没有。对象是使用id中指定的属性键存储的。否则,您怎么能通过${user}等方式在普通EL中访问它呢? - BalusC
由于当前的实现方式,为什么我们有${scopeSession.user}显式表达式可供使用?请指出您在哪里找到它将使用ID将其存储在会话中的信息?我认为这是JSR实现人员可以自行决定的事情。 - Mykola Golubyev
参考JSP.5.1章节,所有内容都清晰明了地列出。 - evnafets

2

从文档中得知:

<jsp:useBean>元素用于定位或实例化JavaBeans组件。 <jsp:useBean>首先尝试定位Bean的实例。 如果Bean不存在,则<jsp:useBean>从类或序列化模板实例化它。

由于“定位”bean是完全正常的,因此我们可以假设bean可能通过其他方式可用,而不是通过<jsp:useBean>实例化。例如,通过在servlet中创建它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接