Dlaczego nie mogę używać <jsp: getProperty> bez <jsp: useBean>?
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.jsp
wydrukowania 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
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 person
to jest.
Podobnie, używając
<jsp:getProperty name="person" property="name">
bez deklarowania person
jako
<!-- Declaring person -->
<jsp:useBean id="person" class="foo.Person" scope="request" />
nie zostanie pomyślnie skompilowany .
1 Zakładając, że Person.class
istnieje.
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_PROPERTY
wł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 person
warunkiem, ż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 person
i 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.Validator
i org.apache.jasper.compiler.Generator
.