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.jspstampare 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 personcome
<!-- Declaring person -->
<jsp:useBean id="person" class="foo.Person" scope="request" />
non verrà compilato con successo .
1 Supponendo che Person.classci 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_PROPERTYproprietà 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 personFAGIOLO 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 persone 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.Validatore org.apache.jasper.compiler.Generator.