Статьи

Гранд Тур: Старая Школа JSP

Я думал, что я ушел, не так ли? Декабрь был для меня сумасшедшим месяцем, поэтому мне пришлось приостановить этот блог, но с наступлением нового года появились новые приоритеты, и я вернулся, чтобы служить вам, жаждущим Java орд.

Сначала в этом году, еще один шаг на пути к пониманию уровня техники в разработке веб-приложений на Java. Ранее мы рассматривали сервлеты , классы Java, предназначенные для ответа на запросы веб-браузера. Используя сервлет, мы собрали простой веб-интерфейс в нашем списке задач, управляемых базой данных. И хотя это сработало, было бы непросто писать практические веб-приложения таким образом, когда вся логика приложения смешивалась с HTML-кодом страницы.

JavaServer Pages (JSP) были созданы для преодоления этой слабости сервлетов. Вместо написания Java-класса с HTML-кодом, вы пишете HTML-страницу с Java-кодом. Затем сервер на лету преобразует эту страницу в сервлет Java для обработки запросов страницы. Как мы увидим, изменение подхода таким образом решает некоторые проблемы, но создает некоторые новые.

Начиная с веб-приложения списка дел, которое мы создали в прошлый раз, давайте todo.jsp с нашим надоедливым сервлетом и заменим его страницей JavaServer, которая делает то же самое: todo.jsp . Вот обновленная структура файлов и каталогов для нашего приложения:

  /todo.jsp
 /styles.css
 /WEB-INF/web.xml
 /WEB-INF/classes/com/sitepoint/ToDoItem.class
 /WEB-INF/classes/com/sitepoint/ToDoItem.java
 /WEB-INF/classes/com/sitepoint/ToDoList.class
 /WEB-INF/classes/com/sitepoint/ToDoList.java
 /WEB-INF/classes/uk/co/anyware/html/HTMLEscaper.class
 /WEB-INF/classes/uk/co/anyware/html/HTMLEscaper.java
 /WEB-INF/lib/mysql-connector-java-version-bin.jar 

Итак, начнем писать наш файл todo.jsp , поэтому откройте ваш любимый текстовый редактор и мы приступим к приготовлению пищи!

JSP-файлы очень похожи на HTML-файлы, за исключением того, что в них будут присутствовать специальные теги JSP. Как правило, все теги JSP начинаются с <% и заканчиваются %> . Когда мы рассмотрим более современные и продвинутые способы работы с JSP в будущем, это правило будет нарушено, но сейчас все будет в порядке, если вы примете это как Евангелие.

Первое, что нам нужно сделать, это предоставить некоторую информацию о странице. Это делается с помощью директивы страницы. Директивы — это тип тега JSP, который содержит различные настройки и другие типы информации о конфигурации. Директивы имеют знак @ после их открытия <% . Вот наша директива страницы:

 <%@ page import="java.util.*,com.sitepoint.*,uk.co.anyware.html.*" %> 

Наиболее распространенное использование директивы страницы — это импорт пакетов Java, которые будут использоваться кодом Java в файле JSP. В этом случае мы указали, что нам нужны классы из java.util , com.sitepoint и uk.co.anyware.html . Эти же пакеты были импортированы нашим сервлетом списка дел (код, который вы хотели бы иметь поблизости для сравнения).

Затем мы можем продолжить писать нашу страницу так же, как любой (X) HTML-файл:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>To-Do List</title> <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> <link rel="stylesheet" type="text/css" href="<%= request.getContextPath() %>/styles.css" /> </head> 

Посмотрите внимательно, и вы увидите еще один тег JSP: <%= request.getContextPath() %> . Это то, что мы называем выражением, о чем свидетельствует знак = после открытия in the tag. Expressions are used to output the values of Java code; in this case, we're outputting the value returned by the in the tag. Expressions are used to output the values of Java code; in this case, we're outputting the value returned by the in the tag. Expressions are used to output the values of Java code; in this case, we're outputting the value returned by the getContextPath() объекта request — корневого каталога нашего веб-приложения, который необходим браузеру для поиска файла styles.css .

Да, код JSP, как и код сервлета, имеет доступ к запросу браузера через переменную под названием request . Существует также переменная response для ответа браузера. Обе эти переменные называются неявными объектами, поскольку они создаются неявно для вас.

Другие неявные объекты включают out , объект, который работает так же, как PrintWriter который используется в сервлете для отправки вывода в браузер, session , объект для хранения значений в ходе посещения конкретного пользователя вашим сайтом и application , объект для хранения значений для использования в вашем приложении.

Теперь, так же, как наш сервлет полагался на объект ToDoList чтобы выполнить большую часть работы приложения, так и наш файл JSP. Нам нужно объявить этот объект, и для этого нам нужен еще один тег JSP: объявление.

 <%! private ToDoList toDoList; public void jspInit() { toDoList = new ToDoList(getInitParameter("jdbcDriver"), getInitParameter("jdbcConnectionString")); } %> 

Как вы можете видеть, объявления обозначаются восклицательным знаком ( ! ) После открытия <% и содержат объявления свойств и методов Java точно так же, как вы найдете в классе Java.

В этом случае мы объявили объект ToDoList именем toDoList и написали метод с именем jspInit() который создает этот объект из параметров инициализации, так же, как мы делали это в методе init() нашего сервлета. Хотя init() — это специальный метод для сервлетов, который вызывается сервером перед первым использованием сервлета, jspInit() — это специальный метод, который вызывается автоматически для инициализации файлов JSP.

Далее идет код, который будет обрабатывать отправку форм на этой странице. Из нашего сервлета вы помните, что страница будет содержать две формы: одну для добавления элементов в список дел и одну для их удаления. Код для обработки этих представлений будет идентичным в нашем JSP, за исключением того, что он идет внутри еще одного типа тега JSP: скриптлета.

 <% String newToDo = request.getParameter("newtodo"); if (newToDo != null) { toDoList.addItem(newToDo); response.sendRedirect(request.getRequestURI()); return; } String deleteid = request.getParameter("deleteid"); if (deleteid != null) { try { toDoList.deleteItem(Integer.parseInt(deleteid)); response.sendRedirect(request.getRequestURI()); return; } catch (NumberFormatException e) { throw new ServletException("Bad deleteid value submitted.", e); } } %> 

Когда JSP был впервые задуман, ожидается, что скриптлеты будут наиболее распространенным типом тегов JSP, поэтому они обозначаются простыми разделителями <% и %> — без специальных символов в начале тега. Java-код внутри скриптлета выполняется каждый раз, когда запрашивается страница.

С обработкой формы все, что осталось, — это создать сами формы. Во-первых, у нас есть список дел, который отображает текущие элементы дел и позволяет пользователю удалить их:

 <body> <% Iterator toDoItems = toDoList.getToDoItems(); if (toDoItems.hasNext()) { %> <form action="<%= request.getRequestURI() %>" method="post"> <select name="deleteid" size="<%= Math.max(2, toDoList.getItemCount()) %>"> <% while (toDoItems.hasNext()) { ToDoItem toDoItem = (ToDoItem) toDoItems.next(); %> <option value="<%= toDoItem.getId() %>"><%= HTMLEscaper.escape(toDoItem.toString()) %></option> <% } %> </select> <input type="submit" value="Delete Selected Item" /> </form> <% } %> 

Выглядит немного беспорядочно, не так ли? У нас есть смесь HTML-кода, JSP-скриплетов и JSP-выражений. Позвольте мне разобрать это для вас.

  <% Iterator toDoItems = toDoList.getToDoItems(); if (toDoItems.hasNext()) { %> ... <% } %> 

Первый скриптлет получает объект Iterator содержащий элементы в списке дел. Наш сервлет сделал то же самое. Поскольку нам не нужно отображать список дел, если он не содержит никаких элементов, мы используем оператор if чтобы проверить, использует ли он метод hasNext() Iterator .

Затем весь код для формы идет внутри этого оператора if . Закрывающая скобка оператора if находится в последнем скриптлете страницы. Как видите, код Java внутри скриптлета не обязательно должен быть автономным; Вы можете думать о HTML-коде между вашими JSP-тегами как о инструкциях для вывода этого HTML-кода, а скриптлеты предоставляют логику того, когда и как должен отображаться этот HTML.

В форме мы используем выражения JSP для вывода необходимых значений в наших тегах <form> и <select> :

  <form action="<%= request.getRequestURI() %>" method="post"> <select name="deleteid" size="<%= Math.max(2, toDoList.getItemCount()) %>"> ... </select> <input type="submit" value="Delete Selected Item" /> </form> 

Наконец, нам нужно вывести <option> для каждого элемента в нашем списке дел. Код снова удивительно похож на наш сервлет, так как мы используем цикл while для выполнения этой работы:

  <% while (toDoItems.hasNext()) { ToDoItem toDoItem = (ToDoItem) toDoItems.next(); %> <option value="<%= toDoItem.getId() %>"> <%= HTMLEscaper.escape(toDoItem.toString()) %></option> <% } %> 

На первый взгляд это беспорядок, но если вы посмотрите внимательно (или еще лучше, если у вас есть редактор, который выделяет теги JSP другим цветом), вы можете увидеть, как логика в скриплетах JSP, а остальная часть кода просто HTML с выражениями JSP для вывода динамических значений.

Форма для добавления новых элементов списка дел — это просто кусок пирога по сравнению с первым:

  <form action="<%= request.getRequestURI() %>" method="post"> <input type="text" name="newtodo" /> <input type="submit" value="Add New Item" /> </form> </body> </html> 

Это заботится о нашем файле JSP! jdbcDriver только обновить web.xml конфигурации web.xml для нашего приложения, чтобы сделать параметры инициализации ( jdbcDriver и jdbcConnectionString ) доступными для нашего файла JSP:

 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <display-name>ToDoJSP</display-name> <servlet> <servlet-name>toDo</servlet-name> <jsp-file>/todo.jsp</jsp-file> <init-param> <description>The JDBC driver class.</description> <param-name>jdbcDriver</param-name> <param-value>com.mysql.jdbc.Driver</param-value> </init-param> <init-param> <description>The JDBC connection string.</description> <param-name>jdbcConnectionString</param-name> <param-value>jdbc:mysql://localhost/todo?user=root&amp;password=password</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>toDo</servlet-name> <url-pattern>/todo.jsp</url-pattern> </servlet-mapping> </web-app> 

В файле web.xml который мы использовали с нашим сервлетом, есть два существенных изменения:

  • Вместо <servlet-class> для указания сервлета, для которого мы предоставляем параметры, у нас есть <jsp-file> для указания файла JSP.

  • Тег <url-pattern> в <servlet-mapping> указывает на файл JSP .

После внесения этих изменений вы готовы объединить приложение и развернуть его на своем сервере. В отличие от сервлетов, вам не нужно заранее компилировать файлы JSP — сервер преобразует их в сервлеты и компилирует их для вас на лету. Однако вам все равно нужно скомпилировать классы, которые использует ваш JSP ( ToDoList , ToDoItem , HTMLEscaper ).

Как и в случае с сервлетом, используйте утилиту командной строки jar для создания файла WAR содержащего ваше приложение:

  jar cvf ToDoJSP.war. 

webapps этот файл в каталог webapps Tomcat, чтобы развернуть его (или разверните на любом предпочитаемом Java-совместимом сервере), и отредактируйте web.xml соответствующий конфигурации вашей базы данных.

Затем загрузите http://localhost:8080/ToDoJSP/todo.jsp чтобы опробовать ваше приложение. Вы должны увидеть свой знакомый интерфейс списка дел, на этот раз сгенерированный JSP.

Вот готовый WAR-файл, включая исходный код, с которым вы можете поиграть:

Загрузить код (250 КБ)

Теперь вы помните, что весь смысл JSP состоял в том, чтобы преодолеть беспорядок смешивания HTML-кода в Java-код сервлета. Если вы посмотрели код для нашего нового файла JSP, я бы простил вас за скептическое отношение к тому, достигли ли мы нашей цели.

Файл JSP нашего списка дел написан с тем, что я называю «JSP старой школы». Это чистый JSP, как это было задумано много лет назад, без современных удобств. В течение следующих нескольких недель я покажу вам, как изменить его, используя текущие и расширенные функции JSP. Прежде всего, мы вернем сервлеты обратно в изображение (но в гораздо более простой форме!), Затем мы рассмотрим такие стандарты, как JavaBeans, библиотека стандартных тегов Java и язык выражений JSP.

Вообще говоря, цель будет состоять в том, чтобы избавиться от всего кода Java в файле JSP, либо заменив его более удобными для конструктора конструкциями тегов, либо переместив его во внешние независимые классы Java. К тому времени, как мы закончим, вы будете знать, как написать файл JSP, чтобы его было трудно отличить от стандартного файла HTML.