ทำไมฉันไม่สามารถใช้ <jsp: getProperty> โดยไม่มี <jsp: useBean>

Jan 01 2021

บอกว่ามี 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):

ฉันรู้ว่า<jsp:useBean>ผลตอบแทนวัตถุบุคคลเดียวกันโดยการโทรgetAttribute()- ตั้งแต่พวกเขาอยู่ในที่เดียวกันขอบเขตการร้องขอ ในขณะที่ในด้านอื่น ๆ ที่<jsp:getProperty>จะเรียกfindAttribute()ที่แท้จริงพยายามที่จะหาแอตทริบิวต์ของมูลค่า "บุคคล" .. และในที่สุดก็พิมพ์อีวาน

แต่ถ้าไม่ได้ใช้<jsp:useBean>ล่ะ? หมายความว่าฉันไม่สามารถเข้าถึงแอตทริบิวต์ "บุคคล" ตามคำขอขอบเขตใช่หรือไม่ ฉันหมายความว่ามันจะยังคงอยู่ที่นั่นแม้ว่าฉันจะไม่ได้ใช้<jsp:useBean>.แล้วทำไมฉันต้องมีค่าเดียวกัน("คน") ทั้งในidใน<jsp:useBean>และชื่อข้างใน<jsp:getProperty>? การลบอย่างง่าย<jsp:useBean>จะทำให้โปรแกรมของฉันแตก

รู้ว่า<jsp:getProperty>สายfindAttribute()จะไม่ได้เป็นตรรกะมากขึ้นถ้ามีแอตทริบิวต์เดียว (เช่นแอตทริบิวต์ชื่อ ) ที่จะนำมาใช้เป็นข้อโต้แย้งที่จะหาแอตทริบิวต์ในขอบเขตหน้า> คำขอ> เซสชั่น> แอปพลิเค ? ทำไมฉันต้อง "ผูก" สองแท็ก: <jsp:useBean>และ<jsp:getProperty>?

คำตอบ

2 LiveandLetLive Jan 01 2021 at 03:03

คุณคิดอย่างไรกับรหัสต่อไปนี้?

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อยู่ที่นั่น

1 Bogdan Jan 01 2021 at 18:18

TL; DR:คุณควรจำไว้ว่าคุณจำเป็นต้องใช้<jsp:getProperty>ด้วย<jsp:useBean>เนื่องจากข้อกำหนดระบุไว้อย่างนั้น <jsp:useBean>จำเป็นต้องแนะนำ bean ให้กับโปรเซสเซอร์ JSP ก่อน<jsp:getProperty>จึงจะสามารถใช้งานได้

คำอธิบายอีกต่อไป:

ทำไมฉันไม่สามารถใช้งานได้<jsp:getProperty>โดยไม่ต้อง<jsp:useBean>?

เนื่องจากพวกเขา "ค่อนข้าง" ออกแบบมาเพื่อทำงานร่วมกัน ฉันไม่รู้ว่าทำไมถึงตัดสินใจแบบนั้น (เฉพาะนักออกแบบของข้อกำหนด JSP เท่านั้นที่สามารถตอบได้) แต่ข้อมูลจำเพาะนั้นมีไว้เพื่อพูดถึง<jsp:getProperty>:

อ็อบเจ็กต์ที่ตั้งชื่อตามชื่อต้องได้รับการ“ แนะนำ” ให้กับโปรเซสเซอร์ JSP โดยใช้แอ็คชัน jsp: useBean หรือแอ็คชันแบบกำหนดเองที่มีรายการ VariableInfo ที่เกี่ยวข้องสำหรับชื่อนี้ หากไม่ได้นำออบเจ็กต์มาใช้ในลักษณะนี้ขอแนะนำให้ใช้คอนเทนเนอร์ (แต่ไม่จำเป็น) เพื่อเพิ่มข้อผิดพลาดในการแปลเนื่องจากการใช้งานเพจละเมิดข้อกำหนด

ฉันพูดว่า "ค่อนข้าง" ออกแบบมาเพื่อทำงานร่วมกันเพราะในบางกรณีคุณสามารถใช้ได้<jsp:getProperty>โดยไม่ต้อง<jsp:useBean>แต่คุณต้องกำหนดค่าตัวประมวลผล JSP ให้ละเว้นกฎข้อกำหนด JSP.5.3 (สำหรับเซิร์ฟเวอร์ที่อนุญาต)

สิ่งนี้ยังไม่ชัดเจนดังนั้นเรามาดูกันว่าเกิดอะไรขึ้นกับโค้ดบางส่วน

ฉันมี JSP ต่อไปนี้:

-------------------------------------------------------------------
<jsp:useBean id="person" class="test.Person" scope="application" />
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
<jsp:getProperty name="person" property="name" />
-------------------------------------------------------------------

ฉันใช้ตัวคั่นเหล่านั้นเพื่อที่ฉันจะสามารถค้นหาได้ในภายหลังใน servlet ที่สร้างโดย JSP ซึ่งส่งผลให้เกิดรหัสนี้:

  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) แต่ไม่มีคลาส ดังนั้นคุณลักษณะเหล่านั้นเพียง แต่ส่งผลในและจากนั้นในfindAttribute("person") getName()ชั้นเรียนมาจากไหน? และคำตอบคือการเรียกก่อนหน้านี้เพื่อ<jsp:useBean>แนะนำสิ่งนี้ในโปรเซสเซอร์ JSP

ดังนั้นคุณต้องโทร<jsp:useBean>ไปแนะนำ bean ในโปรเซสเซอร์ JSP เพื่อที่ว่าเมื่อโปรเซสเซอร์เห็น<jsp:getProperty>มันจะรู้ว่ามันเกี่ยวข้องกับอะไร โดยพื้นฐานแล้ว<jsp:useBean>ให้นิยามมันแล้ว<jsp:getProperty>ใช้มัน หากคุณไม่ได้โทร<jsp:useBean>ที่<jsp:getProperty>จะพยายามที่จะใช้สิ่งที่ไม่ได้กำหนดหน่วยประมวลผล JSP จะบ่นและคุณได้รับกลับเป็นข้อยกเว้นของ:

jsp: getProperty สำหรับ bean ที่มีชื่อ 'person' ก่อนหน้านี้ไม่ได้มีการแนะนำชื่อตาม JSP.5.3

แต่ถ้าคุณอ่านรายละเอียดมันจะบอกว่า:

[... ] แนะนำให้ใช้คอนเทนเนอร์ (แต่ไม่จำเป็น) เพื่อเพิ่มข้อผิดพลาดในการแปล [... ]

ตัวอย่างเช่นหากคุณใช้ Tomcat มีorg.apache.jasper.compiler.Generator.STRICT_GET_PROPERTYคุณสมบัติของระบบที่ควบคุมข้อกำหนดในการให้อ็อบเจ็กต์ที่ใช้ใน<jsp:getProperty>การ "แนะนำ" กับโปรเซสเซอร์ JSP ก่อนหน้านี้ (โดยทั่วไปบังคับใช้หรือไม่ใช่กฎ JSP.5.3) ดูตัวอย่างนี้Tomcat หน้าเอกสาร

ดังนั้นหากฉันเริ่มเซิร์ฟเวอร์ Tomcat ด้วยตัวแปรระบบเป็น:

-Dorg.apache.jasper.compiler.Generator.STRICT_GET_PROPERTY=false

แล้วฉันสามารถใช้<jsp:getProperty>โดยไม่ต้อง<jsp:useBean>มีเงื่อนไขว่าผมแนะนำpersonBEAN อยู่ในขอบเขตบางวิธีอื่น ๆ (เช่นจากเซิร์ฟเล็ตด้วยrequest.setAttribute(), session.setAttribute()หรือapplication.setAttribute()เพื่อให้<jsp:getProperty>สามารถทำpageContext.findAttribute()และมองหาถั่วที่มีชื่อpersonและพบว่า

หากคุณใช้คุณสมบัติของระบบนั้นเอาต์พุตที่สร้างโดย<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");

ถ้ามีคนที่มีความสนใจในวิธีการทั้งหมดของแผ่ระเบียบนี้เรียนไปดูที่ (สำหรับเซิร์ฟเวอร์ Tomcat) คือ: และorg.apache.jasper.compiler.Validatororg.apache.jasper.compiler.Generator