【问题标题】:My problem in understanding Head First's JavaBean explanation我在理解 Head First Java Bean 解释时遇到的问题
【发布时间】:2021-04-07 20:26:24
【问题描述】:

我正在阅读 Head First Servlets 和 JSP,其中一个 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);
}

后来,他们展示了如何使用<jsp:useBean><jsp:useProperty> 来获取人名:

我想我明白了 - 如果在 request 范围内没有属性意味着变量 person 将为空,这也意味着 new将使用默认构造函数生成 Person 对象。底线:我不会有我的属性。幸运的是,我之前将 Person 对象存储在请求的范围内,这意味着它会找到它。

所以我想尝试一下并稍微更改一下代码。我输入的不是<jsp:useBean id="person" class="foo.Person" scope="request"/>,而是<jsp:useBean id="person" class="foo.Person" scope="application"/>。请记住,我们的属性之前存储在 request 的范围内,而不是 application,这意味着 person 将为 null。令我惊讶的是,它很容易找到该属性并打印了Evan。当 book 清楚地表明它将在 我在范围的属性中编写的相同范围内搜索属性时,它是如何发生的?

【问题讨论】:

    标签: java jsp servlets attributes javabeans


    【解决方案1】:

    关键的一点是<jsp:useBean> 也可以在您指定的范围内找不到对象时创建一个对象。这意味着:

    • 如果对象不在您所说的范围内,<jsp:useBean> 将创建一个新对象。
    • 如果对象在您所说的范围内,<jsp:useBean> 将使用该对象。

    换句话说,您可以处理一个对象或两个对象,具体取决于您放置对象的上下文以及您所说的上下文。

    我知道,这很令人困惑。所以让我们玩一些例子。我将稍微简化一下 Person 类以显示创建对象的位置。

    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;
        }
        
    }
    

    第一个例子:范围匹配

    Servlet 内容:

    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>
    

    访问 servlet 将导致以下输出:

    >>>>>>>>>>>>>>>>>>>>>>>>>
    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)
    <<<<<<<<<<<<<<<<<<<<<<<
    

    查看前两行,您将看到在 servlet 内只创建了一个对象。 &lt;jsp:useBean&gt; 只是检索这个。

    第二个例子:范围不匹配

    Servlet 保持不变:

    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);
        }
    }
    

    JSP 现在使用应用程序范围,而不是实际人员所在的会话范围:

    <%@ page contentType="text/html; charset=UTF-8" %>
    <!DOCTYPE html>
    <html>
        <body>
            <jsp:useBean id="person" class="test.Person" scope="application" />
        </body>
    </html>
    

    结果现在是这样的:

    >>>>>>>>>>>>>>>>>>>>>>>>>
    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)
    <<<<<<<<<<<<<<<<<<<<<<<
    

    您现在有两个对象。一个由 servlet 创建,如第一个示例所示,还有另一个由 JSP 生成的 servlet 创建,因为&lt;jsp:useBean&gt; 在应用程序范围内找不到对象,所以它使用class 属性创建一个.

    混淆是由&lt;jsp:getProperty&gt; 引起的,它在page &gt; request &gt; session &gt; application 范围内搜索对象,直到找到一个对象。这与&lt;jsp:useBean&gt; 所做的不同(如上面的两个示例所述)。

    如有疑问,请联系the JSP specs

    话虽如此,请注意&lt;jsp:useBean&gt;&lt;jsp:getProperty&gt; 在实践中并没有真正被广泛使用。人们使用JSP expression languageJSTL 或其他(更强大的)自定义标签。为了从书中学习并避免使用 scriptlet(这是一种不好的做法),这些可以帮助您入门,但实际上它们并不是人们真正喜欢的访问对象及其属性的方式。

    【讨论】:

    • 您的另一个好答案!非常感谢博格丹。你能告诉我为什么我们还要在 中写我们想要的scope,而它总是试图“找到它”吗?
    • &lt;jsp:useBean&gt; 将尝试从该范围获取对象(它是 getAttribute,而不是 findAttribute),但如果失败,它会创建一个对象并将其保存到该范围中。仔细阅读您在问题中发布的图片中的解释(在同步块内,顺便说一句,这是指定范围的另一个原因,有一些东西要同步)。 &lt;jst:getProperty&gt; 执行 findAttribute。再一次,这是您在这里处理的两个标签,它们以自己的方式工作,做自己的事情。将它们放在一起是令人困惑的部分。
    • 再次感谢您,我现在完全明白了。我有一个核心问题要问,我让你一个人呆着;)为什么要对&lt;jsp:useBean&gt; 大惊小怪,而&lt;jsp:getProperty&gt; 不在乎谁创建了 Person 对象? 它所做的只是搜索一个对象。为什么我必须具有与 &lt;jsp:useBean&gt; 中的 name&lt;jsp:getProperty&gt; 中的 id 相同的 value 才能使其工作?无论如何,&lt;jsp:getProperty&gt; 不应该调用 findAttribute(),因为它所做的只是字面意思吗?希望你能理解我:/
    • @Stefan:我看到你问过另一个question,所以我answered there
    【解决方案2】:

    JavaServer Pages™Specification查看&lt;jsp:getProperty&gt;的以下描述

    jsp:setProperty和jsp:getProperty中name属性的值 将引用从 pageContext 对象获取的对象 通过它的 findAttribute 方法。

    现在,检查同一文档中findAttribute 的描述:

    搜索命名属性 在页面、请求、会话(如果有效)和应用范围中 order 并返回关联的值或 null。

    因此,如果在request 范围内找不到属性person,则搜索将扩大到application 范围。

    &lt;jsp:useBean id="person" class="foo.Person" scope="application"/&gt; 将创建一个新的Person 并将其放入application 范围内,如果不存在则不管该属性是否已存在于request 范围内。

    当你使用&lt;jsp:getProperty name="person" property="name"&gt;时,搜索属性person会在页面 > request > session(如果有效)> application 范围按顺序并以 find-first 方法,即一旦找到,就返回值(即不再进行搜索)。

    由于您有两个具有名为person 的属性的Person 对象:一个具有名称,Evanrequest 范围内,另一个作为new Person()application 范围内, request 范围内的一个是由于 findAttribute 的工作方式而返回的。

    【讨论】:

    • 谢谢!我也读了博格丹的回答。这是否意味着在我的情况下我会有两个 Person 对象(一个在 servlet 的请求范围内,一个在 jsp 的应用程序范围内)?所以当我执行&lt;jsp:getProperty&gt; 时,它会从请求的范围中“获取”对象,因为它首先要被遍历?因为page>request>session>application
    • @Stefan - 我已经更新了我的答案(最后添加了更多解释)。如有任何疑问,请随时发表评论。
    • 谢谢!我对另一件事感到怀疑和困惑:图片显示了来自 id 和来自 name 是如何“绑定”的。那么为什么在这种情况下我必须使用相同的值(“人”)才能起作用? 无论 name 属性中的值如何,都不会调用 findAttribute() 吗?
    • name="attribute-name" 是使用&lt;jsp:getProperty&gt; 声明属性的语法。基于此声明,findAttribute 将搜索此attribute-name&lt;jsp:useBean&gt; 使用 id="attribute-name"。我不确定为什么规范架构师为同一件事选择不同的名称(即nameid)。是你在上面的评论中问的吗?如有任何疑问,请随时发表评论。
    • 不,不。我指的是为什么它们都必须具有相同的 id 和 name,即 value "person"。 &lt;jsp:getProperty&gt; 不应该是独立的吗?因为它只是“尝试”使用findAttribute() 查找属性。为什么必须将其“绑定”到&lt;jsp:useBean&gt;?因为当我删除&lt;jsp:useBean&gt; 时,程序不起作用..
    猜你喜欢
    • 2017-08-19
    • 2011-03-17
    • 2018-06-27
    • 1970-01-01
    • 2023-01-10
    • 1970-01-01
    • 2019-11-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多