Por que não consigo usar <jsp: getProperty> sem <jsp: useBean>?

Jan 01 2021

Digamos que haja um servlet com 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);
}

, isso vai para result.jspimprimir o prenome (Evan). Aqui está uma imagem de como seria (fonte Head First Servlets e JSP):

Eu sei que <jsp:useBean>retorna o mesmo objeto Person chamando getAttribute()- uma vez que eles estão no mesmo escopo de solicitação . Enquanto do outro lado, <jsp:getProperty>vai ligar findAttribute()para literalmente tentar encontrar o atributo de valor “pessoa” .. e eventualmente imprimir o Evan .

Mas e se eu não usar <jsp:useBean>? Isso significa que não consegui acessar o atributo "pessoa" na solicitação de escopo ? Quero dizer que ainda estaria lá, mesmo se eu não usar <jsp:useBean>.Então, por que eu deve ter mesmo valor ( "pessoa") dentro ambos id em <jsp:useBean>e nome dentro <jsp:getProperty>? A remoção simples <jsp:useBean>quebra meu programa.

Sabendo que <jsp:getProperty>as chamadas findAttribute(), não seria mais lógico se houvesse um único atributo (como atributo-name ), que será usado como um argumento para encontrar atributos em âmbitos página> pedido> sessão> aplicativo ? Por que devo "amarrar" essas duas tags: <jsp:useBean>e <jsp:getProperty>?

Respostas

2 LiveandLetLive Jan 01 2021 at 03:03

O que você acha do código a seguir?

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

Você já deve ter adivinhado corretamente que não será compilado com sucesso .

Agora, o que acontece com o código a seguir?

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

Claro, ele será compilado com sucesso 1 porque agora o compilador entende o que personé.

Da mesma forma, usando

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

sem declarar personcomo

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

não será compilado com sucesso .


1 Supondo que Person.classesteja lá.

1 Bogdan Jan 01 2021 at 18:18

TL; DR: Você deve apenas lembrar que precisa usar <jsp:getProperty>com <jsp:useBean>porque a especificação diz isso. <jsp:useBean>precisa apresentar o bean ao processador JSP, antes de <jsp:getProperty>poder usá-lo.

A explicação mais longa:

Por que não posso usar <jsp:getProperty>sem <jsp:useBean>?

Porque eles foram "de certa forma" projetados para funcionarem juntos. Não sei por que foi decidido dessa forma (apenas os designers da especificação JSP podem responder a isso), mas a própria especificação tem o seguinte a dizer sobre <jsp:getProperty>:

O objeto nomeado pelo nome deve ter sido “apresentado” ao processador JSP usando a ação jsp: useBean ou uma ação customizada com uma entrada VariableInfo associada para este nome. Se o objeto não foi introduzido dessa maneira, a implementação do contêiner é recomendada (mas não obrigatória) para gerar um erro de tradução, uma vez que a implementação da página viola a especificação.

Eu digo "um pouco" projetado para trabalhar em conjunto, porque em alguns casos você pode usar <jsp:getProperty>sem <jsp:useBean>, mas você tem que configurar o processador JSP para ignorar a regra de especificação JSP.5.3 (para servidores que permitem isso).

Isso não está muito claro, então vamos ver o que acontece com alguns códigos.

Tenho o seguinte JSP:

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

Usei esses delimitadores para que possa localizá-los posteriormente no servlet gerado pelo JSP, onde eles resultam neste 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");

Se você olhar para o, <jsp:getProperty>verá que faz um elenco para test.Person:

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

Mas de onde veio isso? Em seu, <jsp:getProperty>você especifica um nome de bean ( person) e um nome de propriedade ( name), mas nenhuma classe. Portanto, esses atributos resultam apenas em findAttribute("person")e, em seguida, em getName(). De onde a aula foi tirada? E a resposta é, a chamada anterior <jsp:useBean>introduziu isso no processador JSP.

Portanto, você deve chamar <jsp:useBean>para introduzir o bean no processador JSP para que, quando o processador vir o <jsp:getProperty>, saiba com o que está lidando. Basicamente, <jsp:useBean>define e <jsp:getProperty>usa. Se você não chamar <jsp:useBean>, o <jsp:getProperty>tentará usar algo indefinido, o processador JSP reclamará e você receberá uma exceção de:

jsp: getProperty para bean com o nome 'pessoa'. O nome não foi introduzido anteriormente de acordo com JSP.5.3

Mas se você ler as especificações, diz:

[...] a implementação do contêiner é recomendada (mas não obrigatória) para gerar um erro de tradução [...]

Se você usar o Tomcat, por exemplo, há uma org.apache.jasper.compiler.Generator.STRICT_GET_PROPERTYpropriedade do sistema que controla a necessidade de que o objeto usado <jsp:getProperty>seja previamente "introduzido" no processador JSP (basicamente, aplicando ou não a regra JSP.5.3). Veja, por exemplo, esta página de documentação do Tomcat .

Portanto, se eu iniciar meu servidor Tomcat com uma variável de sistema de:

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

Então posso usar <jsp:getProperty>sem <jsp:useBean>, DESDE QUE EU INTRODUZA O personFEIJÃO NO ESCOPO DE OUTRA FORMA (como de um servlet com request.setAttribute(), session.setAttribute()ou de application.setAttribute()forma que <jsp:getProperty>possa fazer um pageContext.findAttribute()e procurar por um bean chamado persone encontrá-lo.

Se você usar essa propriedade do sistema, a saída gerada pela <jsp:getProperty>tag muda. Já não depende de <jsp:useBean>e o elenco é removido:

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 alguém estiver interessado em como toda essa bagunça se desdobra, as classes a serem examinadas (para um servidor Tomcat) são: org.apache.jasper.compiler.Validatore org.apache.jasper.compiler.Generator.