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