вівторок, 10 червня 2008 р.

Сервлеты действия

Введение

На заре Web-разработки многие профессиональные программисты должны были определять, как эффективно использовать сервлеты. Наиболее часто это приводило к бурному росту числа сервлетов на сервере. На каждый тип запроса приходился один сервлет.

Это быстро стало неудобным, поэтому программисты начали включать в свои сервлеты условную логику, которая делала их более адаптируемыми, обрабатывающими несколько типов запросов. Со временем это тоже стало приводить к плохому коду. Существует лучший способ, называемый сервлетом действия (action servlet), который реализует концепцию под названием Model 2. Насколько я знаю, эта идея впервые была описана Дэвидом М. Гиари (David M. Geary) (см. раздел "Ресурсы"), а эффективно использована в популярных библиотеках сервлетов, например в проекте Jakarta Struts.

Сервлет действия не имеет условной логики, выбирающей поведение сервлета. Вместо этого у вас имеются действия (определенные программистом классы), которым сервлет передает полномочия для обработки запросов различного типа. В большинстве случаев это намного лучший объектно-ориентированный подход, чем использование нескольких сервлетов или нескольких условий if в одном сервлете.

Что делает наш пример сервлета действия

Наш пример сервлета действия работает диспетчером для упрощенного приложения, которое позволяет создавать, сохранять, просматривать и удалять записи списка контактов. Эти записи будут хорошо отформатированы. В конечном итоге, пользователи должны будут регистрироваться в приложении для его использования, но мы добавим эту функциональную возможность позже в разделе "Пользователи и данные".


Настройка проекта

Создайте новый Tomcat-проект в Eclipse точно также, как создавали для HelloWorld. Обратите внимание на то, что название вашего проекта является контекстным значением для сервлета по умолчанию, поэтому вы будете использовать его при вводе URL для обращения к сервлету. Если вы настроили Tomcat на использование контекстных файлов, он должен автоматически создать их для этого проекта.

Eclipse также должен создать проект, имеющий корректную структуру, со следующими важными каталогами:

  • WEB-INF
  • WEB-INF/src
  • work

В первом каталоге (WEB-INF) хранятся важные конфигурационные файлы, в частности, файл web.xml, который мы рассмотрим позже. В нем также хранится наш откомпилированный код в каталоге classes. Во втором каталоге (WEB-INF/src) хранится исходный код наших Java-классов. В третьем каталоге (work) хранится откомпилированный код наших файлов JavaServer Pages (JSP), который Tomcat создает для нас автоматически каждый раз, когда мы вызываем JSP-страницу первый раз после изменения кода (мы рассмотрим JSP-технологию более подробно в следующем разделе). Корневой каталог проекта содержит все наши исходные JSP-файлы, а также наш файл базы данных.

Обратите внимание на то, что всю эту структуру можно увидеть в перспективе Resource в Eclipse, но в перспективе Java Browsing вы увидите только каталоги WEB-INF/src и work.

Все эти файлы содержатся в архиве contacts.jar, поставляемом с данным руководством (ссылка приведена разделе "Ресурсы"). Для их импортирования просто создайте новый Tomcat-проект, затем импортируйте contacts.jar (используя Import>Zip file). При этом все файлы разместятся в нужном месте, за исключением исходного кода. Исходный код помещается в подкаталог src корневого каталога проекта. Переместите содержимое этого каталога в WEB-INF/src, и настройка будет завершена.

Представление

В конце концов, это руководство посвящено сервлетам, которые почти не имеют ничего общего с представлением. Но без просмотра каких-либо результатов на экране мы рассказали бы вам только часть истории. Конечно же, вы можете писать сервлеты, которые совсем не участвуют в представлении, но большинство Web-приложений отображают информацию в браузере. Это означает, что вы должны выбрать используемый механизм представления. Технология Java Server Pages является одной из обычных альтернатив и широко применяется.

Используя технологию JSP, вы можете создавать динамические Web-страницы. Они поддерживают статический HTML (или другой язык разметки, например XML) и динамические элементы, которые, исходя из их названия, могут создавать содержимое динамически. JSP-страницы компилируются в сервлеты (то есть, в Java-код) контейнером Tomcat. Однако вы почти никогда не будете об этом беспокоиться. Просто знайте, что происходит следующее:

  • Пользователь вводит URL, который J2EE-контейнер сервлетов направляет в сервлет.
  • Сервлет делает свою работу и помещает информацию в сессию, или в компонент, и направляет его в JSP-страницу.
  • JSP-код преобразовывает информацию в компоненте и/или сессии, и передает ответ в браузер.

Вы легко можете создавать простые JSP-страницы и запускать их в Tomcat лишь с небольшими изменениями в конфигурации нашего Web-приложения и без загрузки дополнительных библиотек кода, поэтому мы будем их использовать (ссылки на дополнительную информацию по JSP-технологии приведены в разделе "Ресурсы").

Наше приложение Contacts будет иметь одну главную JSP-страницу со списком существующих контактов и формой для добавления новых. Позже мы добавим страницы для регистрации и выхода из приложения.

Важно помнить, что JSP-технология является только одним из вариантов представления. Существуют и другие. Одним из вариантов, становящихся очень популярными, является пакет шаблонов Jakarta Velocity (см. "Ресурсы"). JSP-технология имеет один главный недостаток, который заключается в том, что если вы хотите отделить вашу логику от представления, то сложные, функциональные приложения потребуют очень сложных JSP-страниц и некоторой дополнительной работы сервера для создания пользовательских тегов. Другим недостатком JSP-технологии является то, что она часто вызывает непреодолимое желание смешать бизнес-логику и представление, что приводит к созданию хрупких систем, которые очень трудно обслуживать.

По моему мнению, JSP-технология часто является неправильным выбором, в отличие от Velocity (или какого-нибудь другого шаблонного подхода). Но она вполне подойдет в нашем простом примере для демонстрации концепций, которые мы должны рассмотреть. В таких простых случаях смешение логики и представления допустимо. Однако, с профессиональной точки зрения, чаще всего такой подход не желателен, хотя многие программисты и предпочитают его.

Файл web.xml

Для того чтобы мы могли использовать JSP-страницу, которую собираемся создать, необходимо указать Tomcat, как ее обрабатывать. Для этого мы должны создать файл web.xml в нашем каталоге WEB-INF. Он должен выглядеть примерно так:


<!DOCTYPE web-app PUBLIC '-//Sun Microsystems, Inc.//DTD
Web Application 2.3//EN' 'http://java.sun.com/dtd/web-app_2_3.dtd'>
<web-app>
<servlet>

<servlet-name>contacts</servlet-name>
<servlet-class>
com.roywmiller.contacts.model2.ContactsServlet</servlet-class>
</servlet>

<servlet-mapping>

<servlet-name>contacts</servlet-name>
<url-pattern>/index.htm</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>contacts</servlet-name>

<url-pattern>*.perform</url-pattern>
</servlet-mapping>

<servlet>
<servlet-name>jspAssign</servlet-name>

<servlet-class>
org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>logVerbosityLevel</param-name>
<param-value>WARNING</param-value>

</init-param>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>

<load-on-startup>3</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>jspAssign</servlet-name>
<url-pattern>/*.jsp</url-pattern>

</servlet-mapping>
</web-app>
Мы создали основной файл web.xml для нашего HelloWorldServlet, но он является минимальным. По мере усложнения приложения файл web.xml становится более хитроумным. Давайте кратко его проанализируем.

Тег указывает имя псевдонима для нашего сервлета, который мы будем использовать в файле. Он также указывает Tomcat, экземпляр какого класса создавать при создании сервлета в оперативной памяти. В моем рабочем пространстве Eclipse я создал пакет com.roywmiller.contacts.model2 для хранения класса сервлета. Вы можете вызвать любой пакет по желанию, но путь к вашему сервлету должен соответствовать элементу . Второй определяемый нами сервлет поставляется с Tomcat, и нам не нужно его менять. Это просто сервлет JSP-обработки.

указывает Tomcat, какой сервлет выполнять при поступлении на сервер определенных URL. У нас имеется три отображения. Первое отображает страницу по умолчанию, которую Web-сервер ищет () для нашего сервлета. Второе отображение указывает Tomcat отображать любой URL, заканчивающийся .perform, в наш сервлет. Адреса URL этой формы будут указывать нашему сервлету, какое действие реализовать (позже мы рассмотрим, как это работает, более подробно). Третье отображение указывает Tomcat использовать JSP-сервлет для обработки JSP-страниц.

Кодирование JSP-страницы

Вот код нашей JSP-страницы:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%@ page import="java.util.*" %>
<%@ page import="com.roywmiller.contacts.model.*" %>
<html>

<head>
<title>Contacts List 1.0</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">

<style type="text/css">
body, table, hr {
color: black;
background: silver;
font-family: Verdana, sans-serif;
font-size: x-small;
}
</style>

</head>

<body>
<jsp:useBean id="contacts" scope="session"
class="com.roywmiller.contacts.model.ContactList"/>

<h2>Contact List 1.0</h2>
<hr size="2"/>
<table frame="below" width="100%">

<tr>
<th align="left"></th>
<th align="left">Name</th>
<th align="left">Street</th>

<th align="left">City</th>
<th align="left">State</th>
<th align="left">Zip</th>
<th align="left">Type</th>

</tr>
<%
List list = contacts.getContacts();
for (Iterator i = list.iterator(); i.hasNext();) {
Contact contact = (Contact)i.next();
%>
<tr>
<td width="100"><
a href="removeContactAction.perform?id=<%= contact.getId()%>"
>Delete</a></td>
<td width="200"><%
=contact.getFirstname()%> <%=contact.getLastname()%></td>

<td width="150"><%=contact.getStreet()%></td>
<td width="100"><%=contact.getCity()%></td>
<td width="100"><%=contact.getState()%></td>
<td width="100"><%=contact.getZip()%></td>

<td width="100"><%=contact.getType()%></td>
</tr>
<%
}
%>
</table>
<br/>
<br/>

<br/>
<fieldset>
<legend><b>Add Contact</b></legend>
<form method="post" action="addContactAction.perform">
<table>

<tr>
<td>First Name:<td>
<td><input type="text" size="30"
name="firstname"></td>
</tr>
<tr>

<td>Last Name:<td>
<td><input type="text" size="30"
name="lastname"></td>
</tr>
<tr>
<td>Street:<td>

<td><input type="text" size="30"
name="street"></td>
</tr>
<tr>
<td>City:<td>
<td><input type="text" size="30"
name="city"></td>

</tr>
<tr>
<td>State:<td>
<td><input type="text" size="30"
name="state"></td>
</tr>

<tr>
<td>Zip:<td>
<td><input type="text" size="30"
name="zip"></td>
</tr>
<tr>

<td>Type:<td>
<td><input type="radio" size="30"
name="type" value="family">
Family <input type="radio" size="30"
name="type" value="acquaintance"
checked> Acquaintance</td>
</tr>

</table>
<br/>
<input type="submit" name="addContact" value=" Add ">
</form>
</fieldset>

</body>

</html>

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

Анатомия простой JSP-страницы

В данной JSP-странице HTML - это HTML. Java-код, встроенный в таблицу, выглядит следующим образом:

<% Java code %>
Для того чтобы встроить Java-код в страницу, вы должны указать, где находятся классы, также как делаете это в Java-коде. Например:

<%@ page import="java.util.*" %>
Наша страница отображает список контактов, передаваемый из экземпляра ContactList, о котором JSP-страница знает благодаря следующей строке:


<jsp:useBean id="contacts" scope="session" class="com.roywmiller.contacts.model.ContactList"/>
Эта строка указывает JSP-странице использовать компонент, называемый contacts. Это экземпляр com.roywmiller.contacts.model.ContactList, имеющий область видимости для сессии.

Обратите внимание на то, что мы используем Java-цикл for в теле страницы:


List list = contacts.getContacts();
for (Iterator i = list.iterator(); i.hasNext();) {
Contact contact = (Contact)i.next();
%>
<tr>
<td width="100">
<a href="removeContactAction.perform?id=<%=
contact.getId()%>" >Delete</a></td>
<td width="200"><%=contact.getFirstname()%> <%=
contact.getLastname()%></td>

<td width="150"><%=contact.getStreet()%></td>
<td width="100"><%=contact.getCity()%></td>
<td width="100"><%=contact.getState()%></td>
<td width="100"><%=contact.getZip()%></td>

<td width="100"><%=contact.getType()%></td>
</tr>
<%
}

Это демонстрирует, как JSP-технология позволяет смешивать HTML и Java-код. Здесь мы выполняем цикл по списку контактов нашего объекта contact. В каждом цикле мы добавляем элемент к нашей HTML-таблице. В каждой строке таблицы мы вызываем getter-метод экземпляра Contact для заполнения ячеек таблицы. В первой ячейке мы должны создать ссылку Delete для каждой строки. Атрибут href мы устанавливаем в следующую строку:

removeContactAction.perform?id=<%= contact.getId()%>
Когда пользователь нажимает эту ссылку, данная строка добавляется в конец URL (после прямого слеша (/)), который посылается на сервер. Знак вопроса является разделителем для параметров запроса, которые следуют парами name=value. В данном случае мы передаем ID контакта.

То же самое происходит по всей странице, например, в форме для добавления новых контактов. Обратите внимание на тег <form>:


<form method="post" action="addContactAction.perform">
Когда пользователь нажимает кнопку Add (кнопка подтверждения внизу страницы), к URL добавляется addContactAction.perform.

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

Немає коментарів: