Perché non posso utilizzare <jsp: getProperty> senza <jsp: useBean>?
Diciamo che c'è un servlet che ha il codice:
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);
}
, che va a result.jsp
stampare il nome dato (Evan). Ecco un'immagine di come apparirebbe (fonte Head First Servlets e JSP):
So che <jsp:useBean>
restituisce lo stesso oggetto Person chiamando getAttribute()
, poiché si trovano nello stesso ambito di richiesta . Mentre dall'altra parte, <jsp:getProperty>
chiamerà findAttribute()
per provare letteralmente a trovare l'attributo di valore "persona" .. ed eventualmente stampare Evan .
Ma se non l'avessi usato <jsp:useBean>
? Significa che non ho potuto accedere all'attributo "persona" su richiesta dell'ambito ? Voglio dire che sarebbe ancora lì, anche se non ho usato <jsp:useBean>.
allora perché devo avere lo stesso valore ( "persona") all'interno sia id in <jsp:useBean>
e nome dentro <jsp:getProperty>
? La semplice rimozione <jsp:useBean>
interrompe il mio programma.
Sapendo che le <jsp:getProperty>
chiamate findAttribute()
, non sarebbe più logico se ci fosse un singolo attributo (come nome-attributo ), che verrà usato come argomento per trovare attributi nella pagina degli ambiti > richiesta> sessione> applicazione ? Perché devo "legare" questi due tag: <jsp:useBean>
e <jsp:getProperty>
?
Risposte
Cosa ne pensate del seguente codice?
public class Main {
public static void main(String[] args) {
System.out.println(person);
}
}
Devi aver già indovinato correttamente che non verrà compilato con successo .
Ora, che dire del codice seguente?
public class Main {
public static void main(String[] args) {
Person person = new Person();// Declaring person
System.out.println(person);
}
}
Naturalmente, verrà compilato con successo 1 perché ora il compilatore capisce cosa person
è.
Allo stesso modo, usando
<jsp:getProperty name="person" property="name">
senza dichiarare person
come
<!-- Declaring person -->
<jsp:useBean id="person" class="foo.Person" scope="request" />
non verrà compilato con successo .
1 Supponendo che Person.class
ci sia.
TL; DR: Dovresti solo ricordare che devi usare <jsp:getProperty>
con <jsp:useBean>
perché la specifica lo dice. <jsp:useBean>
deve introdurre il bean al processore JSP, prima di <jsp:getProperty>
poterlo utilizzare.
La spiegazione più lunga:
Perché non posso usare
<jsp:getProperty>
senza<jsp:useBean>
?
Perché erano "un po '" progettati per lavorare insieme. Non so perché sia stato deciso in questo modo (solo i progettisti della specifica JSP possono rispondere), ma la specifica stessa ha questo da dire su <jsp:getProperty>
:
L'oggetto denominato con il nome deve essere stato "introdotto" nel processore JSP utilizzando l'azione jsp: useBean o un'azione personalizzata con una voce VariableInfo associata per questo nome. Se l'oggetto non è stato introdotto in questo modo, l'implementazione del contenitore è consigliata (ma non obbligatoria) per generare un errore di traduzione, poiché l'implementazione della pagina viola la specifica.
Dico "un po '" progettato per funzionare insieme, perché in alcuni casi è possibile utilizzarlo <jsp:getProperty>
senza <jsp:useBean>
, ma è necessario configurare il processore JSP per ignorare la regola della specifica JSP.5.3 (per i server che lo consentono).
Questo non è molto chiaro, quindi vediamo cosa succede con un po 'di codice.
Ho il seguente JSP:
-------------------------------------------------------------------
<jsp:useBean id="person" class="test.Person" scope="application" />
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
<jsp:getProperty name="person" property="name" />
-------------------------------------------------------------------
Ho usato quei delimitatori in modo da poterli successivamente trovare nel servlet generato da JSP, dove risultano in questo codice:
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");
Se guardi il <jsp:getProperty>
puoi vedere che fa un cast per test.Person
:
org.apache.jasper.runtime.JspRuntimeLibrary.toString((((test.Person)_jspx_page_context.findAttribute("person")).getName()))
Ma da dove viene? Nel tuo <jsp:getProperty>
si specifica un nome di bean ( person
) e un nome di proprietà ( name
), ma nessuna classe. Quindi quegli attributi risultano solo in findAttribute("person")
e poi in getName()
. Da dove è stata presa la lezione? E la risposta è, la chiamata precedente per averlo <jsp:useBean>
introdotto nel processore JSP.
Quindi devi chiamare <jsp:useBean>
per introdurre il bean nel processore JSP in modo che quando il processore lo vede <jsp:getProperty>
sa con cosa ha a che fare. Quindi, fondamentalmente, lo <jsp:useBean>
definisce, quindi lo <jsp:getProperty>
usa. Se non chiami <jsp:useBean>
, <jsp:getProperty>
proverà a usare qualcosa di indefinito, il processore JSP si lamenterà e otterrai un'eccezione di:
jsp: getProperty per il bean con nome "person". Il nome non era stato precedentemente introdotto secondo JSP.5.3
Ma se leggi le specifiche, dice:
[...] l'implementazione del contenitore è consigliata (ma non obbligatoria) per generare un errore di traduzione [...]
Se si utilizza Tomcat, ad esempio, è presente una org.apache.jasper.compiler.Generator.STRICT_GET_PROPERTY
proprietà di sistema che controlla il requisito di utilizzare l'oggetto <jsp:getProperty>
per essere "introdotto" in precedenza nel processore JSP (fondamentalmente, applicando o meno la regola JSP.5.3). Vedi ad esempio questa pagina della documentazione di Tomcat .
Quindi, se avvio il mio server Tomcat con una variabile di sistema di:
-Dorg.apache.jasper.compiler.Generator.STRICT_GET_PROPERTY=false
Quindi posso usare <jsp:getProperty>
senza <jsp:useBean>
, A PUNTO DI INTRODURRE IL person
FAGIOLO IN SCOPO IN QUALCHE ALTRO MODO (come da un servlet con request.setAttribute()
, session.setAttribute()
o application.setAttribute()
così che <jsp:getProperty>
può fare un pageContext.findAttribute()
e cercare un fagiolo chiamato person
e trovarlo.
Se utilizzi quella proprietà di sistema, l'output generato dal <jsp:getProperty>
tag cambia. Non dipende più da <jsp:useBean>
e il cast viene rimosso:
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");
Se qualcuno è interessato a come si svolge tutto questo casino, le classi da guardare (per un server Tomcat) sono: org.apache.jasper.compiler.Validator
e org.apache.jasper.compiler.Generator
.