Pourquoi je ne peux pas utiliser <jsp: getProperty> sans <jsp: useBean>?
Disons qu'il y a un servlet qui a du code:
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);
}
, qui va à result.jsp
imprimer le prénom (Evan). Voici à quoi cela ressemblerait (source Head First Servlets et JSP):

Je sais que <jsp:useBean>
renvoie le même objet Person en appelant getAttribute()
- car ils sont dans la même portée de demande . Tandis que de l'autre côté, <jsp:getProperty>
appellera findAttribute()
pour essayer littéralement de trouver l'attribut de valeur "personne" .. et finalement imprimer Evan .
Mais que faire si je ne l'utilise pas <jsp:useBean>
? Cela signifie-t-il que je n'ai pas pu accéder à l'attribut "personne" à la demande d' étendue ? Je veux dire qu'il serait toujours là, même si je ne pas utiliser <jsp:useBean>.
Alors , pourquoi je dois avoir la même valeur ( « personne ») à l' intérieur à la fois id en <jsp:useBean>
et nom à l' intérieur <jsp:getProperty>
? Une simple suppression <jsp:useBean>
interrompt mon programme.
Sachant que les <jsp:getProperty>
appels findAttribute()
, ne serait-il pas plus logique s'il y avait un seul attribut (comme nom-attribut ), qui sera utilisé comme argument pour trouver des attributs dans la page des portées > demande> session> application ? Pourquoi je dois "lier" ces deux balises: <jsp:useBean>
et <jsp:getProperty>
?
Réponses
Que pensez-vous du code suivant?
public class Main {
public static void main(String[] args) {
System.out.println(person);
}
}
Vous devez avoir déjà correctement deviné qu'il ne sera pas compilé avec succès .
Maintenant, qu'en est-il du code suivant?
public class Main {
public static void main(String[] args) {
Person person = new Person();// Declaring person
System.out.println(person);
}
}
Bien sûr, il sera compilé avec succès 1 car maintenant le compilateur comprend ce que person
c'est.
De même, en utilisant
<jsp:getProperty name="person" property="name">
sans déclarer person
comme
<!-- Declaring person -->
<jsp:useBean id="person" class="foo.Person" scope="request" />
ne sera pas compilé avec succès .
1 En supposant que Person.class
c'est là.
TL; DR: N'oubliez pas que vous devez utiliser <jsp:getProperty>
avec <jsp:useBean>
parce que la spécification le dit. <jsp:useBean>
doit introduire le bean dans le processeur JSP avant de <jsp:getProperty>
pouvoir l'utiliser.
L'explication plus longue:
Pourquoi je ne peux pas utiliser
<jsp:getProperty>
sans<jsp:useBean>
?
Parce qu'ils étaient «quelque peu» conçus pour fonctionner ensemble. Je ne sais pas pourquoi cela a été décidé comme ça (seuls les concepteurs de la spécification JSP peuvent répondre à cela), mais la spécification elle-même a ceci à dire à propos de <jsp:getProperty>
:
L'objet nommé par le nom doit avoir été «introduit» dans le processeur JSP à l'aide de l'action jsp: useBean ou d'une action personnalisée avec une entrée VariableInfo associée pour ce nom. Si l'objet n'a pas été introduit de cette manière, l'implémentation du conteneur est recommandée (mais pas obligatoire) pour générer une erreur de traduction, car l'implémentation de la page est en violation de la spécification.
Je dis "un peu" conçu pour fonctionner ensemble, car dans certains cas, vous pouvez utiliser <jsp:getProperty>
sans <jsp:useBean>
, mais vous devez configurer le processeur JSP pour ignorer la règle de spécification JSP.5.3 (pour les serveurs qui le permettent).
Ce n'est pas très clair, voyons donc ce qui se passe avec du code.
J'ai la JSP suivante:
-------------------------------------------------------------------
<jsp:useBean id="person" class="test.Person" scope="application" />
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
<jsp:getProperty name="person" property="name" />
-------------------------------------------------------------------
J'ai utilisé ces délimiteurs afin de pouvoir les trouver plus tard dans le servlet généré par JSP, où ils aboutissent à ce code:
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");
Si vous regardez le, <jsp:getProperty>
vous pouvez voir qu'il fait un casting pour test.Person
:
org.apache.jasper.runtime.JspRuntimeLibrary.toString((((test.Person)_jspx_page_context.findAttribute("person")).getName()))
Mais d'où cela vient-il? Dans votre <jsp:getProperty>
vous spécifiez un nom de bean ( person
) et un nom de propriété ( name
), mais pas de classe. Donc, ces attributs ne résultent que de findAttribute("person")
et ensuite getName()
. D'où la classe a-t-elle été prise? Et la réponse est que l'appel précédent a <jsp:useBean>
introduit cela dans le processeur JSP.
Vous devez donc appeler <jsp:useBean>
pour introduire le bean dans le processeur JSP afin que, lorsque le processeur le voit, <jsp:getProperty>
il sache de quoi il s'agit. Donc, fondamentalement, le <jsp:useBean>
définit, puis l' <jsp:getProperty>
utilise. Si vous n'appelez pas <jsp:useBean>
, le <jsp:getProperty>
essaiera d'utiliser quelque chose d'indéfini, le processeur JSP se plaindra et vous obtiendrez une exception de:
jsp: getProperty pour le bean avec le nom 'person'. Le nom n'a pas été précédemment introduit selon JSP.5.3
Mais si vous lisez les spécifications, il est dit:
[...] l'implémentation du conteneur est recommandée (mais pas obligatoire) pour générer une erreur de traduction [...]
Si vous utilisez Tomcat, par exemple, il existe une org.apache.jasper.compiler.Generator.STRICT_GET_PROPERTY
propriété système qui contrôle l'exigence d'avoir l'objet utilisé <jsp:getProperty>
pour être précédemment «introduit» dans le processeur JSP (en gros, appliquer ou non la règle JSP.5.3). Voir par exemple cette page de documentation Tomcat .
Donc, si je démarre mon serveur Tomcat avec une variable système de:
-Dorg.apache.jasper.compiler.Generator.STRICT_GET_PROPERTY=false
Ensuite, je peux utiliser <jsp:getProperty>
sans <jsp:useBean>
, à condition que je présente le haricot dans la person
portée d'une autre manière (comme à partir d'un servlet avec request.setAttribute()
, session.setAttribute()
ou application.setAttribute()
alors qui <jsp:getProperty>
peut faire un pageContext.findAttribute()
et rechercher un bean nommé person
et le trouver.
Si vous utilisez cette propriété système, la sortie générée par la <jsp:getProperty>
balise change. Cela ne dépend plus <jsp:useBean>
et la distribution est supprimée:
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");
Si quelqu'un s'intéresse à la façon dont tout ce désordre se déroule, les classes à examiner (pour un serveur Tomcat) sont: org.apache.jasper.compiler.Validator
et org.apache.jasper.compiler.Generator
.