Issue
I am reading Head First Servlets and JSP, where one servlet had code:
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);
}
Later on, they showed how to use <jsp:useBean>
and <jsp:useProperty>
to get person's name:
I thought I understand it - if there isn't attribute at request scope means variable person will be null, which also means new Person object using default constructor will be made. Bottom line: I won't have my attribute. Luckily, I previously stored Person object at request's scope, meaning it will find it.
So I wanted to try it out and change code a little. Instead of <jsp:useBean id="person" class="foo.Person" scope="request"/>
, I typed <jsp:useBean id="person" class="foo.Person" scope="application"/>
. Remember that our attribute is previously stored at request's scope and not application, meaning person will be null. To my surprise, it easily found the attribute and printed Evan
. How did it happen, when book clearly showed that it will search for attribute at the same scope that I wrote in scope's attribute?
Solution
The key takeaway is that <jsp:useBean>
can also create an object if it does not find one in the scope you specified. That means that:
- if the object is not in the scope you said it was,
<jsp:useBean>
will create a new object. - if the object is in the scope you said it is,
<jsp:useBean>
will use that object.
In other words, you can deal with one object or with two, depending in which context you placed the object, and in which context you say it is.
I know, it's confusing. So let's play with some examples. I'm going to butcher the Person class a bit to show where objects get created.
package test;
public class Person {
private String name;
public Person() {
try {
throw new RuntimeException("Just to see what's going on");
} catch (RuntimeException ex) {
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>");
for (StackTraceElement ste : ex.getStackTrace()) {
System.out.println(ste.toString());
}
System.out.println("<<<<<<<<<<<<<<<<<<<<<<<");
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
First example: scopes match
Servlet content:
public class TestController extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
session.setAttribute("person", new Person());
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/test.jsp");
requestDispatcher.forward(request, response);
}
}
test.jsp:
<%@ page contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html>
<body>
<jsp:useBean id="person" class="test.Person" scope="session" />
</body>
</html>
Accessing the servlet will cause this output:
>>>>>>>>>>>>>>>>>>>>>>>>>
test.Person.<init>(Person.java:8)
test.TestController.doGet(TestController.java:21)
javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
java.lang.Thread.run(Thread.java:744)
<<<<<<<<<<<<<<<<<<<<<<<
Look at the first two lines and you will see only one object is created, inside the servlet. The <jsp:useBean>
just retrieves this one.
Second example: scopes do not match
Servlet stays the same:
public class TestController extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
session.setAttribute("person", new Person());
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/test.jsp");
requestDispatcher.forward(request, response);
}
}
The JSP now uses application scope, instead of session scope where the actual person is located:
<%@ page contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html>
<body>
<jsp:useBean id="person" class="test.Person" scope="application" />
</body>
</html>
The result is now this:
>>>>>>>>>>>>>>>>>>>>>>>>>
test.Person.<init>(Person.java:8)
test.TestController.doGet(TestController.java:21)
javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
java.lang.Thread.run(Thread.java:744)
<<<<<<<<<<<<<<<<<<<<<<<
>>>>>>>>>>>>>>>>>>>>>>>>>
test.Person.<init>(Person.java:8)
org.apache.jsp.test_jsp._jspService(test_jsp.java:71)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:748)
org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:486)
org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:411)
org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:338)
test.TestController.doGet(TestController.java:24)
javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
java.lang.Thread.run(Thread.java:744)
<<<<<<<<<<<<<<<<<<<<<<<
You now have two objects. One created by the servlet, as in the first example, but also another one that gets created by the JSP's generated servlet, because <jsp:useBean>
couldn't find an object in application scope, so it used the class
attribute to create one.
The confusion is caused by <jsp:getProperty>
which searches for an object in scopes page > request > session > application
, until it finds one. This is separate from what <jsp:useBean>
does (as described above with the two examples).
When in doubt, refer to the JSP specs.
With that being said, note that <jsp:useBean>
and <jsp:getProperty>
aren't really very much used in practice. People use the JSP expression language, JSTL, or other (more powerful) custom tags. For the purpose of learning from a book and avoiding to use scriptlets (that are a bad practice), these are fine to get you started, but in practice they are not really people's favorite way of accessing objects and their properties.
Answered By - Bogdan
Answer Checked By - David Goodson (JavaFixing Volunteer)