Почему я не могу использовать <jsp: getProperty> без <jsp: useBean>?

Jan 01 2021

Скажем, есть сервлет с кодом:

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

, который идет на result.jspпечать данного имени (Эван). Вот изображение того, как это будет выглядеть (исходные сервлеты Head First и JSP):

Я знаю, что <jsp:useBean>при вызове возвращается тот же объект Person, getAttribute()поскольку они находятся в той же области запроса . Находясь с другой стороны, <jsp:getProperty>будет вызывать findAttribute()буквально попытаться найти атрибут значения "person" .. и в конечном итоге напечатать Evan .

Но что, если я не использовал <jsp:useBean>? Означает ли это, что я не смог получить доступ к атрибуту "person" в запросе области ? Я имею в виду, что он все еще был бы там, даже если бы я не использовал. <jsp:useBean>.Так почему же у меня должно быть одно и то же значение ("человек") внутри как id in, так <jsp:useBean>и name внутри <jsp:getProperty>? Простое удаление <jsp:useBean>ломает мою программу.

Зная об этих <jsp:getProperty>вызовах findAttribute(), не было бы логичнее, если бы был один атрибут (например, имя-атрибута ), который будет использоваться в качестве аргумента для поиска атрибутов на странице областей > запрос> сеанс> приложение ? Почему я должен «связать» эти два тега: <jsp:useBean>и <jsp:getProperty>?

Ответы

2 LiveandLetLive Jan 01 2021 at 03:03

Что вы думаете о следующем коде?

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

Вы, должно быть, уже правильно догадались, что он не будет успешно скомпилирован .

А как насчет следующего кода?

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

Конечно, он будет успешно скомпилирован 1, потому что теперь компилятор понимает, что это personтакое.

Аналогично, используя

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

без объявления personкак

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

не будет успешно скомпилирован .


1 Предполагая, Person.classчто есть.

1 Bogdan Jan 01 2021 at 18:18

TL; DR: Вы должны просто помнить, что вам нужно использовать <jsp:getProperty>with, <jsp:useBean>потому что так сказано в спецификации. <jsp:useBean>необходимо представить компонент JSP-процессору, прежде чем его <jsp:getProperty>можно будет использовать.

Более подробное объяснение:

Почему я не могу использовать <jsp:getProperty>без <jsp:useBean>?

Потому что они были «в некоторой степени» созданы для совместной работы. Я не знаю, почему было принято такое решение (на это могут ответить только разработчики спецификации JSP), но в самой спецификации сказано следующее <jsp:getProperty>:

Объект, названный по имени, должен быть «представлен» процессору JSP с помощью действия jsp: useBean или настраиваемого действия со связанной записью VariableInfo для этого имени. Если объект не был представлен таким образом, реализация контейнера рекомендуется (но не требуется), чтобы вызвать ошибку перевода, поскольку реализация страницы нарушает спецификацию.

Я говорю «несколько», разработанные для совместной работы, потому что в некоторых случаях вы можете использовать и <jsp:getProperty>без них <jsp:useBean>, но вам нужно настроить процессор JSP так, чтобы он игнорировал правило спецификации JSP.5.3 (для серверов, которые это позволяют).

Это не очень понятно, поэтому давайте посмотрим, что происходит с некоторым кодом.

У меня есть следующий JSP:

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

Я использовал эти разделители, чтобы позже найти их в сгенерированном JSP сервлете, где они приводят к следующему коду:

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

Если вы посмотрите на, <jsp:getProperty>вы увидите, что он выполняет приведение к test.Person:

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

Но откуда это взялось? В вашем <jsp:getProperty>вы указываете имя компонента ( person) и имя свойства ( name), но не класс. Таким образом, эти атрибуты только приводят findAttribute("person")к getName(). Откуда взяли класс? И ответ таков: предыдущий вызов <jsp:useBean>представил это в процессоре JSP.

Таким образом, вы должны позвонить, <jsp:useBean>чтобы ввести bean-компонент в JSP-процессор, чтобы, когда процессор его видит, <jsp:getProperty>он знал, с чем имеет дело. Итак, в основном, <jsp:useBean>определяет его, а затем <jsp:getProperty>использует. Если вы не позвоните <jsp:useBean>, <jsp:getProperty>он попытается использовать что-то неопределенное, обработчик JSP пожалуется, и вы получите исключение:

jsp: getProperty для bean-компонента с именем person. Имя ранее не вводилось согласно JSP.5.3

Но если вы прочитаете спецификации, там написано:

[...] реализация контейнера рекомендуется (но не требуется), чтобы вызвать ошибку перевода [...]

Например, если вы используете Tomcat, существует org.apache.jasper.compiler.Generator.STRICT_GET_PROPERTYсистемное свойство, которое контролирует требование о том, чтобы используемый объект <jsp:getProperty>был предварительно «представлен» процессору JSP (в основном, применяя или не применяя правило JSP.5.3). См., Например, эту страницу документации Tomcat .

Итак, если я запустил свой сервер Tomcat с системной переменной:

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

Тогда я могу использовать <jsp:getProperty>без <jsp:useBean>, ПРЕДОСТАВЛЕНИЕ, ЧТО Я ПРЕДСТАВЛЯЮ personБИН В ОБЛАСТИ ДРУГИМ СПОСОБОМ (например, из сервлета с request.setAttribute(), session.setAttribute()или application.setAttribute()так, чтобы он <jsp:getProperty>мог выполнить pageContext.findAttribute()и искать бин с именем personи найти его.

Если вы используете это системное свойство, то вывод, сгенерированный <jsp:getProperty>тегом, изменится. Больше не зависит <jsp:useBean>и снята литье:

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

Если кого-то интересует, как разворачивается весь этот беспорядок, классы, на которые следует обратить внимание (для сервера Tomcat): org.apache.jasper.compiler.Validatorи org.apache.jasper.compiler.Generator.