¿Por qué no puedo usar <jsp: getProperty> sin <jsp: useBean>?

Jan 01 2021

Digamos que hay un servlet que tiene código:

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);
}

, que va a result.jspimprimir el nombre de pila (Evan). Aquí hay una imagen de cómo se vería (fuente Head First Servlets y JSP):

Sé que <jsp:useBean>devuelve el mismo objeto Person al llamar getAttribute(), ya que están en el mismo alcance de solicitud . Mientras que en el otro lado, <jsp:getProperty>llamará findAttribute()para intentar literalmente buscar el atributo de valor "persona" .. y eventualmente imprimir Evan .

Pero, ¿y si no lo usé <jsp:useBean>? ¿Eso significa que no pude acceder al atributo "persona" cuando se solicitó el alcance ? Me refiero a que todavía estaría allí, aunque yo no usar <jsp:useBean>.tanto por lo que debe tener el mismo valor ( "persona") en el interior tanto Identificación de <jsp:useBean>y nombre en el interior <jsp:getProperty>? La simple eliminación <jsp:useBean>rompe mi programa.

Sabiendo que las <jsp:getProperty>llamadas findAttribute(), ¿no sería más lógico si hubiera un solo atributo (como el nombre del atributo ), que se usará como argumento para encontrar atributos en la página de alcances > solicitud> sesión> aplicación ? ¿Por qué debo "atar" esas dos etiquetas: <jsp:useBean>y <jsp:getProperty>?

Respuestas

2 LiveandLetLive Jan 01 2021 at 03:03

¿Qué opinas del siguiente código?

public class Main {
    public static void main(String[] args) {
        System.out.println(person);
    }
}

Ya debe haber adivinado correctamente que no se compilará correctamente .

Ahora, ¿qué pasa con el siguiente código?

public class Main {
    public static void main(String[] args) {
        Person person = new Person();// Declaring person
        System.out.println(person);
    }
}

Por supuesto, se compilará con éxito 1 porque ahora el compilador comprende lo que persones.

Del mismo modo, usando

<jsp:getProperty name="person" property="name">

sin declarar personcomo

<!-- Declaring person -->
<jsp:useBean id="person" class="foo.Person" scope="request" />

no se compilará correctamente .


1 Suponiendo que Person.classestá ahí.

1 Bogdan Jan 01 2021 at 18:18

TL; DR: Solo debe recordar que debe usar <jsp:getProperty>con <jsp:useBean>porque la especificación lo dice. <jsp:useBean>necesita introducir el bean en el procesador JSP, antes de <jsp:getProperty>poder usarlo.

La explicación más larga:

¿Por qué no puedo usar <jsp:getProperty>sin <jsp:useBean>?

Porque fueron "algo" diseñados para trabajar juntos. No sé por qué se decidió así (solo los diseñadores de la especificación JSP pueden responder eso), pero la especificación en sí tiene esto que decir sobre <jsp:getProperty>:

El objeto nombrado por el nombre debe haber sido "introducido" en el procesador JSP usando la acción jsp: useBean o una acción personalizada con una entrada VariableInfo asociada para este nombre. Si el objeto no se introdujo de esta manera, se recomienda (pero no es obligatorio) la implementación del contenedor para generar un error de traducción, ya que la implementación de la página infringe la especificación.

Digo "algo" diseñado para trabajar en conjunto, porque en algunos casos se puede utilizar <jsp:getProperty>sin <jsp:useBean>, pero hay que configurar el procesador JSP para ignorar la regla de especificación JSP.5.3 (para servidores que lo permitan).

Esto no está muy claro, así que veamos qué pasa con algún código.

Tengo el siguiente JSP:

-------------------------------------------------------------------
<jsp:useBean id="person" class="test.Person" scope="application" />
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
<jsp:getProperty name="person" property="name" />
-------------------------------------------------------------------

Usé esos delimitadores para que luego pueda encontrarlos en el servlet generado por JSP, donde dan como resultado este código:

  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 miras el <jsp:getProperty>, puedes ver que se lanza a test.Person:

org.apache.jasper.runtime.JspRuntimeLibrary.toString((((test.Person)_jspx_page_context.findAttribute("person")).getName()))

Pero, ¿de dónde vino eso? En su <jsp:getProperty>, especifica un nombre de bean ( person) y un nombre de propiedad ( name), pero no una clase. Entonces esos atributos solo resultan en findAttribute("person")y luego en getName(). ¿De dónde se tomó la clase? Y la respuesta es, la llamada anterior <jsp:useBean>introdujo esto en el procesador JSP.

Por lo tanto, debe llamar <jsp:useBean>para introducir el bean en el procesador JSP para que, cuando el procesador lo vea <jsp:getProperty>, sepa con qué está tratando. Básicamente, lo <jsp:useBean>define y luego lo <jsp:getProperty>usa. Si no llama <jsp:useBean>, <jsp:getProperty>intentará usar algo indefinido, el procesador JSP se quejará y obtendrá una excepción de:

jsp: getProperty para bean con nombre 'persona'. El nombre no se introdujo previamente según JSP.5.3

Pero si lee las especificaciones, dice:

[...] se recomienda la implementación del contenedor (pero no es obligatorio) para generar un error de traducción [...]

Si utiliza Tomcat, por ejemplo, hay una org.apache.jasper.compiler.Generator.STRICT_GET_PROPERTYpropiedad del sistema que controla el requisito de que el objeto utilizado <jsp:getProperty>sea ​​previamente "introducido" en el procesador JSP (básicamente, aplicando o no la regla JSP.5.3). Consulte, por ejemplo, esta página de documentación de Tomcat .

Entonces, si inicio mi servidor Tomcat con una variable de sistema de:

-Dorg.apache.jasper.compiler.Generator.STRICT_GET_PROPERTY=false

Entonces puedo usar <jsp:getProperty>sin <jsp:useBean>, SIEMPRE QUE INTRODUZCA EL personBEAN EN ALCANCE DE ALGUNA OTRA MANERA (como de un servlet con request.setAttribute(), session.setAttribute()o application.setAttribute()para que <jsp:getProperty>pueda hacer una pageContext.findAttribute()y busque un bean con nombre persony lo encuentre.

Si usa esa propiedad del sistema, la salida generada por la <jsp:getProperty>etiqueta cambia. Ya no depende <jsp:useBean>y se quita el yeso:

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 alguien está interesado en cómo se desarrolla todo este lío, las clases a considerar (para un servidor Tomcat) son: org.apache.jasper.compiler.Validatory org.apache.jasper.compiler.Generator.