Dlaczego nie mogę używać <jsp: getProperty> bez <jsp: useBean>?

Jan 01 2021

Powiedzmy, że istnieje serwlet, który ma kod:

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

, który idzie do result.jspwydrukowania imienia (Evan). Oto obraz tego, jak by to wyglądało (serwlety Head First i JSP źródłowe):

Wiem, że <jsp:useBean>zwraca ten sam obiekt Person przez wywołanie getAttribute()- ponieważ mają one ten sam zakres żądania . Będąc po drugiej stronie, <jsp:getProperty>wywoła, findAttribute()aby dosłownie spróbować znaleźć atrybut wartości "osoba" .. i ostatecznie wypisać Evan .

Ale co jeśli nie użyłem <jsp:useBean>? Czy to oznacza, że ​​nie mogłem uzyskać dostępu do atrybutu „osoba” na żądanie zakresu ? Znaczy to nadal istnieje, nawet jeśli nie korzystać <jsp:useBean>.Więc dlaczego muszę mieć taką samą wartość ( „osoba”) zarówno wewnątrz id in <jsp:useBean>a name wewnątrz <jsp:getProperty>? Proste usunięcie <jsp:useBean>przerywa mój program.

Wiedząc, że <jsp:getProperty>wywołania findAttribute(), czy nie byłoby bardziej logiczne, gdyby istniał pojedynczy atrybut (taki jak nazwa-atrybutu ), który zostanie użyty jako argument do znalezienia atrybutów w zakresach strona> żądanie> sesja> aplikacja ? Dlaczego muszę „zawiązać” te dwa znaczniki: <jsp:useBean>i <jsp:getProperty>?

Odpowiedzi

2 LiveandLetLive Jan 01 2021 at 03:03

Co myślisz o poniższym kodzie?

public class Main {
    public static void main(String[] args) {
        System.out.println(person);
    }
}

Musiałeś już poprawnie zgadnąć, że nie zostanie pomyślnie skompilowany .

A co z następującym kodem?

public class Main {
    public static void main(String[] args) {
        Person person = new Person();// Declaring person
        System.out.println(person);
    }
}

Oczywiście zostanie pomyślnie skompilowany 1, ponieważ teraz kompilator rozumie, co personto jest.

Podobnie, używając

<jsp:getProperty name="person" property="name">

bez deklarowania personjako

<!-- Declaring person -->
<jsp:useBean id="person" class="foo.Person" scope="request" />

nie zostanie pomyślnie skompilowany .


1 Zakładając, że Person.classistnieje.

1 Bogdan Jan 01 2021 at 18:18

TL; DR: Powinieneś tylko pamiętać, że musisz używać <jsp:getProperty>z, <jsp:useBean>ponieważ specyfikacja tak mówi. <jsp:useBean>musi wprowadzić ziarno do procesora JSP, zanim będzie <jsp:getProperty>można go użyć.

Dłuższe wyjaśnienie:

Dlaczego nie mogę używać <jsp:getProperty>bez <jsp:useBean>?

Ponieważ zostały „w pewnym sensie” przeznaczone do współpracy. Nie wiem, dlaczego tak zdecydowano (tylko projektanci specyfikacji JSP mogą na to odpowiedzieć), ale sama specyfikacja mówi o tym <jsp:getProperty>:

Obiekt nazwany tą nazwą musi zostać „wprowadzony” do procesora JSP za pomocą akcji jsp: useBean lub akcji niestandardowej z powiązaną pozycją VariableInfo dla tej nazwy. Jeśli obiekt nie został wprowadzony w ten sposób, implementacja kontenera jest zalecana (ale nie wymagana) w celu wywołania błędu tłumaczenia, ponieważ implementacja strony jest niezgodna ze specyfikacją.

Mówię „trochę” zaprojektowane do współpracy, ponieważ w niektórych przypadkach możesz używać <jsp:getProperty>bez <jsp:useBean>, ale musisz skonfigurować procesor JSP tak, aby ignorował regułę specyfikacji JSP.5.3 (dla serwerów, które na to pozwalają).

Nie jest to zbyt jasne, więc zobaczmy, co się stanie z jakimś kodem.

Mam następujący JSP:

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

Użyłem tych ograniczników, aby później móc je znaleźć w serwlecie wygenerowanym przez JSP, gdzie wynikają z tego kodu:

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

Jeśli spojrzysz na to <jsp:getProperty>, zobaczysz, że wykonuje rzut na test.Person:

org.apache.jasper.runtime.JspRuntimeLibrary.toString((((test.Person)_jspx_page_context.findAttribute("person")).getName()))

Ale skąd to się wzięło? W swoim <jsp:getProperty>określasz nazwę fasoli ( person) i nazwę właściwości ( name), ale bez klasy. Więc te atrybuty skutkują tylko findAttribute("person")i potem getName(). Skąd wzięła się klasa? Odpowiedź brzmi: poprzednie wezwanie do <jsp:useBean>wprowadzenia tego w procesorze JSP.

Musisz więc zadzwonić, <jsp:useBean>aby wprowadzić ziarno do procesora JSP, aby gdy procesor zobaczył <jsp:getProperty>, wiedział, z czym ma do czynienia. Zasadniczo <jsp:useBean>definiuje to, a następnie <jsp:getProperty>używa. Jeśli nie zadzwonisz <jsp:useBean>, <jsp:getProperty>spróbuje użyć czegoś niezdefiniowanego, procesor JSP będzie narzekał, a ty otrzymasz wyjątek:

jsp: getProperty dla fasoli o nazwie „osoba”. Nazwa nie była wcześniej wprowadzana zgodnie z JSP.5.3

Ale jeśli przeczytasz specyfikację, jest napisane:

[...] implementacja kontenera jest zalecana (ale nie wymagana) w celu wywołania błędu tłumaczenia [...]

Na przykład, jeśli używasz Tomcata, istnieje org.apache.jasper.compiler.Generator.STRICT_GET_PROPERTYwłaściwość systemowa, która kontroluje wymaganie, aby obiekt użyty w programie <jsp:getProperty>został wcześniej „wprowadzony” do procesora JSP (w zasadzie wymusza lub nie stosuje reguły JSP.5.3). Zobacz na przykład tę stronę dokumentacji Tomcat .

Tak więc, jeśli uruchomię serwer Tomcat ze zmienną systemową:

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

Wtedy mogę użyć <jsp:getProperty>bez <jsp:useBean>, POD personwarunkiem, że WPROWADZĘ ZIARNO W ZAKRESIE W JAKIŚ INNY SPOSÓB (np. Z serwletu z request.setAttribute(), session.setAttribute()lub application.setAttribute()tak, że <jsp:getProperty>może zrobić pageContext.findAttribute()i poszukać fasoli o nazwie personi ją znaleźć.

Jeśli używasz tej właściwości systemowej, dane wyjściowe generowane przez <jsp:getProperty>tag ulegną zmianie. To już nie zależy <jsp:useBean>i obsada jest usuwana:

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

Jeśli ktoś jest zainteresowany tym, jak rozwija się ten bałagan, klasy do obejrzenia (dla serwera Tomcat) to: org.apache.jasper.compiler.Validatori org.apache.jasper.compiler.Generator.