TL;DR: You should just remember that you need to use <jsp:getProperty>
with <jsp:useBean>
because the specification says so. <jsp:useBean>
needs to introduce the bean to the JSP processor, before <jsp:getProperty>
can use it.
The longer explanation:
Why I can't use <jsp:getProperty>
without <jsp:useBean>
?
Because they were "somewhat" designed to work together. I don't know why it was decided like that (only the designers of the JSP specification can answer that), but the spec itself has this to say about <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.
I say "somewhat" designed to work together, because in some cases you can use <jsp:getProperty>
without <jsp:useBean>
, but you have to configure the JSP processor to ignore the JSP.5.3 specification rule (for servers that allow that).
This is not very clear, so let's see what happens with some code.
I have the following JSP:
-------------------------------------------------------------------
<jsp:useBean id="person" class="test.Person" scope="application" />
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
<jsp:getProperty name="person" property="name" />
-------------------------------------------------------------------
I used those delimiters so that I can later find them in the JSP generated servlet, where they result in this code:
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");
If you look at the <jsp:getProperty>
you can see that it does a cast to test.Person
:
org.apache.jasper.runtime.JspRuntimeLibrary.toString((((test.Person)_jspx_page_context.findAttribute("person")).getName()))
But where did that came from? In your <jsp:getProperty>
you specify a bean name (person
) and a property name (name
), but no class. So those attributes only result in findAttribute("person")
and then in getName()
. Where was the class taken from? And the answer is, the previous call to <jsp:useBean>
introduced this in the JSP processor.
So you have to call <jsp:useBean>
to introduce the bean in the JSP processor so that when the processor sees the <jsp:getProperty>
it knows what it's dealing with. So basically, <jsp:useBean>
defines it, then <jsp:getProperty>
uses it. If you don't call <jsp:useBean>
, the <jsp:getProperty>
will try to use something undefined, the JSP processor will complain, and you get back an exception of:
jsp:getProperty for bean with name 'person'. Name was not previously introduced as per JSP.5.3
But if you read the specs, it says:
[...] the container implementation is recommended (but not required) to raise a translation error [...]
If you use Tomcat, for example, there is a org.apache.jasper.compiler.Generator.STRICT_GET_PROPERTY
system property that controls the requirement to have the object used in <jsp:getProperty>
to be previously "introduced" to the JSP processor (basically, enforcing or not the JSP.5.3 rule). See for example this Tomcat documentation page.
So, if I start my Tomcat server with a system variable of:
-Dorg.apache.jasper.compiler.Generator.STRICT_GET_PROPERTY=false
Then I can use <jsp:getProperty>
without <jsp:useBean>
, PROVIDED THAT I INTRODUCE THE person
BEAN IN SCOPE SOME OTHER WAY (like from a servlet with request.setAttribute()
, session.setAttribute()
or application.setAttribute()
so that <jsp:getProperty>
can do a pageContext.findAttribute()
and look for a bean named person
and find it.
If you use that system property, then the output generated by the <jsp:getProperty>
tag changes. It no longer depends on <jsp:useBean>
and the cast is removed:
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");
If someone is interested in how all of this mess unfolds, the classes to look at (for a Tomcat server) are: org.apache.jasper.compiler.Validator
and org.apache.jasper.compiler.Generator
.