为什么我不能在没有 <jsp:useBean> 的情况下使用 <jsp:getProperty>?
Why I can't use <jsp:getProperty> without <jsp:useBean>?
假设 servlet 的代码为:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
foo.Person p = new foo.Person("Evan");
req.setAttribute("person", p);
RequestDispatcher view = req.getRequestDispatcher("/result.jsp");
view.forward(req, resp);
}
,转到 result.jsp
打印名字 (Evan)。这是它的外观图片(来源 Head First Servlets 和 JSP):
我通过调用 getAttribute()
知道 <jsp:useBean>
returns 相同的 Person 对象,因为它们在相同的 请求范围 中。而在另一边,<jsp:getProperty>
将调用 findAttribute()
从字面上尝试查找值“person”的属性..并最终打印 Evan.
但是如果我不使用 <jsp:useBean>
怎么办?这是否意味着我无法在 request 范围内访问“person”属性?我的意思是它仍然会在那里,即使我没有使用 <jsp:useBean>.
所以为什么我必须在两个 id 中有相同的 value("person") <jsp:useBean>
中的 和 <jsp:getProperty>
中的 name?简单删除 <jsp:useBean>
会破坏我的程序。
知道 <jsp:getProperty>
调用 findAttribute()
,如果只有一个属性(比如 attribute-name)不是更合乎逻辑吗?将用作在范围 page>request>session>application 中查找属性的参数?为什么我必须“绑定”这两个标签:<jsp:useBean>
和 <jsp:getProperty>
?
你觉得下面的代码怎么样?
public class Main {
public static void main(String[] args) {
System.out.println(person);
}
}
你一定猜对了不会编译成功.
现在,下面的代码呢?
public class Main {
public static void main(String[] args) {
Person person = new Person();// Declaring person
System.out.println(person);
}
}
当然会编译成功1因为现在编译器明白了person
是什么
同样,使用
<jsp:getProperty name="person" property="name">
没有将 person
声明为
<!-- Declaring person -->
<jsp:useBean id="person" class="foo.Person" scope="request" />
编译不成功.
1 假设 Person.class
在那里。
TL;DR: 你应该记住你需要使用 <jsp:getProperty>
和 <jsp:useBean>
因为规范是这样说的。 <jsp:useBean>
需要将 bean 引入 JSP 处理器,然后 <jsp:getProperty>
才能使用它。
较长的解释:
Why I can't use <jsp:getProperty>
without <jsp:useBean>
?
因为它们“在某种程度上”旨在协同工作。我不知道为什么会这样决定(只有 JSP 规范的设计者才能回答这个问题),但是 the spec 本身对 <jsp:getProperty>
有这样的说法:
The object named by the name must have been “introduced” to the JSP processor using either the jsp:useBean action or a custom action with an associated VariableInfo entry for this name. If the object was not introduced in this manner, the container implementation is recommended (but not required) to raise a translation error, since the page implementation is in violation of the specification.
我说“有点”是为了一起工作而设计的,因为在某些情况下你可以使用 <jsp:getProperty>
而不使用 <jsp:useBean>
,但是你必须配置 JSP 处理器来忽略 JSP.5.3 规范规则(对于允许的服务器)。
这不是很清楚,所以让我们看看一些代码会发生什么。
我有以下 JSP:
-------------------------------------------------------------------
<jsp:useBean id="person" class="test.Person" scope="application" />
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
<jsp:getProperty name="person" property="name" />
-------------------------------------------------------------------
我使用了这些定界符,以便稍后可以在 JSP 生成的 servlet 中找到它们,它们在其中产生以下代码:
out.write("\t\t-------------------------------------------------------------------\r\n");
out.write("\t\t");
test.Person person = null;
synchronized (application) {
person = (test.Person) _jspx_page_context.getAttribute("person", javax.servlet.jsp.PageContext.APPLICATION_SCOPE);
if (person == null){
person = new test.Person();
_jspx_page_context.setAttribute("person", person, javax.servlet.jsp.PageContext.APPLICATION_SCOPE);
}
}
out.write("\r\n");
out.write("\t\t+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\r\n");
out.write("\t\t");
out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString((((test.Person)_jspx_page_context.findAttribute("person")).getName())));
out.write("\r\n");
out.write("\t\t-------------------------------------------------------------------\r\n");
如果您查看 <jsp:getProperty>
,您会发现它转换为 test.Person
:
org.apache.jasper.runtime.JspRuntimeLibrary.toString((((test.Person)_jspx_page_context.findAttribute("person")).getName()))
但是那是从哪里来的呢?在您的 <jsp:getProperty>
中,您指定了一个 bean 名称 (person
) 和一个 属性 名称 (name
),但没有 class。所以这些属性只会导致 findAttribute("person")
,然后导致 getName()
。 class 是从哪里拿来的?答案是,之前对 <jsp:useBean>
的调用在 JSP 处理器中引入了这个。
所以你必须调用 <jsp:useBean>
来在 JSP 处理器中引入 bean,这样当处理器看到 <jsp:getProperty>
它就知道它在处理什么。所以基本上,<jsp:useBean>
定义它,然后 <jsp:getProperty>
使用它。如果你不调用 <jsp:useBean>
,<jsp:getProperty>
会尝试使用未定义的东西,JSP 处理器会报错,你会得到一个异常:
jsp:getProperty for bean with name 'person'. Name was not previously introduced as per JSP.5.3
但是如果你阅读规格,它会说:
[...] the container implementation is recommended (but not required) to raise a translation error [...]
如果您使用 Tomcat,例如,有一个 org.apache.jasper.compiler.Generator.STRICT_GET_PROPERTY
系统 属性 控制要求 <jsp:getProperty>
中使用的对象是以前 "引入”到 JSP 处理器(基本上,执行或不执行 JSP.5.3 规则)。例如,参见 Tomcat documentation page.
因此,如果我使用以下系统变量启动 Tomcat 服务器:
-Dorg.apache.jasper.compiler.Generator.STRICT_GET_PROPERTY=false
然后我可以在没有 <jsp:useBean>
的情况下使用 <jsp:getProperty>
,前提是我以其他方式在范围内引入 person
BEAN(比如从带有 request.setAttribute()
的 servlet,session.setAttribute()
或 application.setAttribute()
以便 <jsp:getProperty>
可以执行 pageContext.findAttribute()
并查找名为 person
的 bean 并找到它。
如果您使用该系统 属性,则 <jsp:getProperty>
标记生成的输出会发生变化。它不再依赖于 <jsp:useBean>
并且删除了演员表:
out.write("\t\t+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\r\n");
out.write("\t\t");
out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString(org.apache.jasper.runtime.JspRuntimeLibrary.handleGetProperty(_jspx_page_context.findAttribute("person"), "name")));
out.write("\r\n");
out.write("\t\t-------------------------------------------------------------------\r\n");
如果有人对所有这些混乱的展开方式感兴趣,class要查看的(对于 Tomcat 服务器)是:org.apache.jasper.compiler.Validator
和 org.apache.jasper.compiler.Generator
.
假设 servlet 的代码为:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
foo.Person p = new foo.Person("Evan");
req.setAttribute("person", p);
RequestDispatcher view = req.getRequestDispatcher("/result.jsp");
view.forward(req, resp);
}
,转到 result.jsp
打印名字 (Evan)。这是它的外观图片(来源 Head First Servlets 和 JSP):
我通过调用 getAttribute()
知道 <jsp:useBean>
returns 相同的 Person 对象,因为它们在相同的 请求范围 中。而在另一边,<jsp:getProperty>
将调用 findAttribute()
从字面上尝试查找值“person”的属性..并最终打印 Evan.
但是如果我不使用 <jsp:useBean>
怎么办?这是否意味着我无法在 request 范围内访问“person”属性?我的意思是它仍然会在那里,即使我没有使用 <jsp:useBean>.
所以为什么我必须在两个 id 中有相同的 value("person") <jsp:useBean>
中的 和 <jsp:getProperty>
中的 name?简单删除 <jsp:useBean>
会破坏我的程序。
知道 <jsp:getProperty>
调用 findAttribute()
,如果只有一个属性(比如 attribute-name)不是更合乎逻辑吗?将用作在范围 page>request>session>application 中查找属性的参数?为什么我必须“绑定”这两个标签:<jsp:useBean>
和 <jsp:getProperty>
?
你觉得下面的代码怎么样?
public class Main {
public static void main(String[] args) {
System.out.println(person);
}
}
你一定猜对了不会编译成功.
现在,下面的代码呢?
public class Main {
public static void main(String[] args) {
Person person = new Person();// Declaring person
System.out.println(person);
}
}
当然会编译成功1因为现在编译器明白了person
是什么
同样,使用
<jsp:getProperty name="person" property="name">
没有将 person
声明为
<!-- Declaring person -->
<jsp:useBean id="person" class="foo.Person" scope="request" />
编译不成功.
1 假设 Person.class
在那里。
TL;DR: 你应该记住你需要使用 <jsp:getProperty>
和 <jsp:useBean>
因为规范是这样说的。 <jsp:useBean>
需要将 bean 引入 JSP 处理器,然后 <jsp:getProperty>
才能使用它。
较长的解释:
Why I can't use
<jsp:getProperty>
without<jsp:useBean>
?
因为它们“在某种程度上”旨在协同工作。我不知道为什么会这样决定(只有 JSP 规范的设计者才能回答这个问题),但是 the spec 本身对 <jsp:getProperty>
有这样的说法:
The object named by the name must have been “introduced” to the JSP processor using either the jsp:useBean action or a custom action with an associated VariableInfo entry for this name. If the object was not introduced in this manner, the container implementation is recommended (but not required) to raise a translation error, since the page implementation is in violation of the specification.
我说“有点”是为了一起工作而设计的,因为在某些情况下你可以使用 <jsp:getProperty>
而不使用 <jsp:useBean>
,但是你必须配置 JSP 处理器来忽略 JSP.5.3 规范规则(对于允许的服务器)。
这不是很清楚,所以让我们看看一些代码会发生什么。
我有以下 JSP:
-------------------------------------------------------------------
<jsp:useBean id="person" class="test.Person" scope="application" />
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
<jsp:getProperty name="person" property="name" />
-------------------------------------------------------------------
我使用了这些定界符,以便稍后可以在 JSP 生成的 servlet 中找到它们,它们在其中产生以下代码:
out.write("\t\t-------------------------------------------------------------------\r\n");
out.write("\t\t");
test.Person person = null;
synchronized (application) {
person = (test.Person) _jspx_page_context.getAttribute("person", javax.servlet.jsp.PageContext.APPLICATION_SCOPE);
if (person == null){
person = new test.Person();
_jspx_page_context.setAttribute("person", person, javax.servlet.jsp.PageContext.APPLICATION_SCOPE);
}
}
out.write("\r\n");
out.write("\t\t+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\r\n");
out.write("\t\t");
out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString((((test.Person)_jspx_page_context.findAttribute("person")).getName())));
out.write("\r\n");
out.write("\t\t-------------------------------------------------------------------\r\n");
如果您查看 <jsp:getProperty>
,您会发现它转换为 test.Person
:
org.apache.jasper.runtime.JspRuntimeLibrary.toString((((test.Person)_jspx_page_context.findAttribute("person")).getName()))
但是那是从哪里来的呢?在您的 <jsp:getProperty>
中,您指定了一个 bean 名称 (person
) 和一个 属性 名称 (name
),但没有 class。所以这些属性只会导致 findAttribute("person")
,然后导致 getName()
。 class 是从哪里拿来的?答案是,之前对 <jsp:useBean>
的调用在 JSP 处理器中引入了这个。
所以你必须调用 <jsp:useBean>
来在 JSP 处理器中引入 bean,这样当处理器看到 <jsp:getProperty>
它就知道它在处理什么。所以基本上,<jsp:useBean>
定义它,然后 <jsp:getProperty>
使用它。如果你不调用 <jsp:useBean>
,<jsp:getProperty>
会尝试使用未定义的东西,JSP 处理器会报错,你会得到一个异常:
jsp:getProperty for bean with name 'person'. Name was not previously introduced as per JSP.5.3
但是如果你阅读规格,它会说:
[...] the container implementation is recommended (but not required) to raise a translation error [...]
如果您使用 Tomcat,例如,有一个 org.apache.jasper.compiler.Generator.STRICT_GET_PROPERTY
系统 属性 控制要求 <jsp:getProperty>
中使用的对象是以前 "引入”到 JSP 处理器(基本上,执行或不执行 JSP.5.3 规则)。例如,参见 Tomcat documentation page.
因此,如果我使用以下系统变量启动 Tomcat 服务器:
-Dorg.apache.jasper.compiler.Generator.STRICT_GET_PROPERTY=false
然后我可以在没有 <jsp:useBean>
的情况下使用 <jsp:getProperty>
,前提是我以其他方式在范围内引入 person
BEAN(比如从带有 request.setAttribute()
的 servlet,session.setAttribute()
或 application.setAttribute()
以便 <jsp:getProperty>
可以执行 pageContext.findAttribute()
并查找名为 person
的 bean 并找到它。
如果您使用该系统 属性,则 <jsp:getProperty>
标记生成的输出会发生变化。它不再依赖于 <jsp:useBean>
并且删除了演员表:
out.write("\t\t+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\r\n");
out.write("\t\t");
out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString(org.apache.jasper.runtime.JspRuntimeLibrary.handleGetProperty(_jspx_page_context.findAttribute("person"), "name")));
out.write("\r\n");
out.write("\t\t-------------------------------------------------------------------\r\n");
如果有人对所有这些混乱的展开方式感兴趣,class要查看的(对于 Tomcat 服务器)是:org.apache.jasper.compiler.Validator
和 org.apache.jasper.compiler.Generator
.