Perché non posso utilizzare <jsp: getProperty> senza <jsp: useBean>?

Jan 01 2021

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

2 LiveandLetLive Jan 01 2021 at 03:03

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.

1 Bogdan Jan 01 2021 at 18:18

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.