Соглашения в коде JavaServer Pages
Соглашения в коде JavaServer Pages
Окончание. Начало в КГ №11.
JSP-объявления
Как и соглашения, принятые в Java-коде, каждое объявление переменной в JSP, пусть даже одного и того же типа, необходимо размещать на разных строках:
Не рекомендуется | Рекомендуется |
<%! private int x, y; %> | <%! private int x; %> <%! private int y; %> |
А вообще JSP-объявления для переменных заметно осложняются, когда это приводит к использованию скриптового языка для описания бизнес-логики и Java-кода в JSP, который изначально проектировался для презентационных целей, а также осложняются переизбытком необходимости управлять целыми скопами переменных.
JSP-скриптлеты
По возможности следует избегать использования JSP-скриптлетов, в особенности когда библиотеки тэгов предоставляют эквивалентные функции. Такой подход делает страницы более читабельными и помогает отделить бизнес-логику от презентационной части/логики. Помимо этого ваши страницы будет очень просто переписать в стиле JSP версии 2.0 (хотя в JSP 2.0 есть поддержка скриптлетов, но их использование в этой спецификации всячески не приветствуется). В следующих примерах для каждого типа данных, которым представляется набор заказчиков, необходимо писать отдельный скриптлет:
customers как массив объектов Customer
<table>
<% for (int i=0; i<customers.length; i++) { %>
<tr>
<td><%= customers[i].getLastName() %></td>
<td><%= customers[i].getFirstName() %></td>
</tr>
<% } %>
</table>
customers как экземпляр класса Enumeration
<table>
<% for (Enumeration e = customers.elements();
e.hasMoreElements();) {
Customer customer = (Customer)e.nextElement();
%>
<tr>
<td><%= customer.getLastName() %></td>
<td><%= customer.getFirstName() %></td>
</tr>
<% } %>
</table>
Однако когда вы пользуетесь библиотекой тэгов, вы получаете более высокую гибкость при необходимости использования различных типов, которыми представляются заказчики. Например, следующий кусок JSP-кода будет поддерживать как массив, так и Enumeration-представление заказчиков:
<table>
<c:forEach var="customer" items="${customers}">
<tr>
<td><c:out value="${customer.lastName}"/></td>
<td><c:out value="${customer.firstName}"/></td>
</tr>
</c:forEach>
</table>
Это в духе применения шаблона проектирования MVC (model-view-controller) для послабления связи между презентационным ярусом и бизнес-логикой, поскольку JSP-скриптлеты вообще не должны использоваться для описания бизнес-логики. JSP-скриптлеты предпочтительнее использовать, если необходимо, для трансформирования данных (также иногда эти данные называют "объектами значений"), возвращаемых от обработанных клиентских запросов в подходящий, удобный формат. Но даже здесь лучше всего воспользоваться библиотекой тэгов или же отдельным сервлетом-контроллером. Например, следующий код получает имена заказчиков (customers) напрямую из базы данных и отображает их клиенту:
<%
// ЭТО НЕ РЕКОМЕНДУЕТСЯ ДЕЛАТЬ В ВИДЕ СКРИПТЛЕТА
Connection conn = null;
try {
// Получаем объект Connection
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("customerDS");
conn = ds.getConnection();
// Получаем имена заказчиков
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT name FROM customer");
// Выводим имена
while (rs.next()) {
out.println(rs.getString("name") + "<br>");
}
} catch (SQLException e) {
out.println("Невозможно получить имена заказчиков из базы данных:" + e);
} finally {
if (conn!= null)
conn.close();
}
%>
Следующий сегмент JSP-кода будет намного лучше, если он делегирует функцию работы с базой данных custom-тэгу myTags:dataSource, который инкапсулирует и прячет все детали работы с базой данных в своей реализации:
<myTags:dataSource
name="customerDS"
table="customer"
columns="name"
var="result" />
<c:forEach var="row" items="${result.rows}">
<c:out value="${row.name}" />
<br />
</c:forEach>
result — это скриптовая переменная, введенная custom-тэгом myTags:dataSource для того, чтобы хранить полученные данные об именах заказчиков из базы данных. JSP-код также может быть более эффективным при условии поддержки сразу нескольких различных выходных форматов данных (HTML, XML, WML), которые определяются динамически в зависимости от потребностей клиента, без всяких угроз в сторону backend-кода (для тэга dataSource). Однако лучше всего было бы делегировать эту функцию на отдельный сервлет, который обрабатывал бы полученные данные и представлял результат для JSP-страницы посредством параметров-атрибутов.
Подведем некоторый итог касательно JSP-скриптлетов:
JSP-выражения должны применяться так же аккуратно, как и JSP-скриптлеты. Чтобы понять, почему так, давайте рассмотрим три следующих примера, которые выполняют одинаковые функции:
Пример 1 (Java-код):
<%= myBean.getName() %>
Пример 2 (JSP-тэг):
<jsp:getProperty name="myBean" property="name" />
Пример 3 (JSTL-тэг):
<c:out value="${myBean.name}" />
В первом примере предполагается, что переменная myBean уже определена. В остальных двух предполагается, что переменная myBean — это атрибут, который может быть найден с использованием метода PageContext.findAttribute(). Кроме того, второй пример также предполагает, что переменная myBean была введена на этой странице при помощи тэга <jsp:useBean>.
Из трех этих примеров предпочтение следует отдать третьему. Во-первых, третий вариант достаточно короток, чтобы сойти за JSP-выражение, во-вторых, его проще понять, и, в-третьих, он имеет мало общего с JSP-скриптлетами (которые предполагают, что web-разработчик знаком с языком Java, а также с основными API-вызовами). Более того, это позволяет странице больше походить на стиль JSP 2.0, где, для того чтобы выполнить аналогичную функцию, достаточно следующего кода: "${myBean.name}" в тексте шаблона. Какой бы пример ни применялся, он должен быть одобрен и согласован всеми web-разработчиками и быть последовательным во всех создаваемых JSP-страницах одного проекта. Также следует отметить, что третий пример, с использованием JSTL, несколько отличается от остальных тем, что получает значение myBean из контекста страницы, а не из локальной Java-переменной.
И, наконец, предпочтительнее использовать JSP-выражения вместо эквивалентных JSP-скриптлетов, которые основываются на своем скриптовом языке. Например:
<%= x %>
лучше, чем
<% out.print(x); %>
Пустые линии и пробелы
Пробельные символы обычно используются для того, чтобы сделать JSP-код более красивым, а также упростить процесс поддержки и изучения этого кода. В частности, пустые линии и пробелы должны присутствовать на JSP-страницах в местах, где имеет смысл их вставлять.
Пустые линии
Пустые линии обычно используются для улучшения точности и разборчивости JSP-страниц. Важно также, что пустые строки не вызывают каких-либо побочных эффектов для выходного формата. Например, ниже приведены два JSP-выражения, которые разделены пустой строкой и заключены в HTML-тэг <PRE>. В этом случае между результатами данных выражений на странице в браузере пользователя будет вставлена дополнительная пустая строка, как это было на самой JSP-странице до ее обработки. Однако, если блок тегов <PRE> отсутствует, то эта дополнительная пустая строка никак не скажется на внешнем виде результативной HTML-страницы в браузере пользователя.
JSP-выражения | Вид в браузере пользователя |
<pre> <%= customer.getFirstName() %> <%= customer.getLastName() %> </pre> | Joe Block |
<pre> <%= customer.getFirstName() %> <%= customer.getLastName() %> </pre> | Joe Block |
<%= customer.getFirstName() %> <%= customer.getLastName() %> | Joe Block |
Пробелы
Символ пробела всегда должен присутствовать между самим JSP-тэгом и его содержимым. Например:
<%= customer.getName() %>
выглядит куда лучше, чем
<%=customer.getName()%>
Также присутствие пробельных символов желательно для разделения тэга комментария и самого комментария:
<%—
- многостроковый комментарий, который разбит
- на две строки.
—%>
<%— короткий комментарий в одну строку —%>
Именные соглашения
Применяя именные соглашения, вы обеспечиваете элементам вашего web-компонента простоту идентификации, классифицирования и координации в проектах. В этой части статьи мы рассмотрим те именные соглашения, которые так или иначе касаются JSP.
Имена JSP-файлов
Имена JSP-файлов всегда должны начинаться с букв в нижнем регистре. Имя может состоять из нескольких слов. В таком случае каждое последующее слово (исключая начальное) следует начинать с заглавной буквы. Имя JSP-файла может быть как простым глаголом, так и небольшим предложением. Однако имен, состоящих только лишь из одного глагола, следует избегать, поскольку такие имена не несут в себе никакой информации для разработчиков. Например:
perform.jsp
не так понятно, как, скажем, вот это:
performLogin.jsp
В случае использования глагола как части имени JSP-файла по возможности лучше использовать этот глагол в настоящем времени, а не в виде герундия и прочих форм. Например:
showAccountDetails.jsp
предпочтительнее, чем
showingAccountDetails.jsp
Имена тэгов
Ниже приведена таблица правил именования обработчиков JSP-тэгов и ассоциативных классов:
Описание | Имя класса |
Тэг XXX, дополнительная информация (потомок javax.servlet.jsp.tagext.TagExtraInfo) | XXXTEI |
Тэг XXX, TLV (потомок javax.servlet.jsp.tagext.TagLibraryValidator) | XXXTLV |
Тэг XXX, интерфейс обработчика (потомок javax.servlet.jsp.tagext.Tag/IterationTag/BodyTag) | XXXTag |
Тэг XXX, реализация обработчика | XXXTag |
Также для того, чтобы эти классы тэгов было проще отличать от других классов, полезно добавлять суффиксы пакетов, тэгов, а также библиотек тэгов в имя пакета класса, например:
com.mycorp.myapp.tags.XXXTag
Префикс имен тэгов
Префикс имени тэга должен быть достаточно коротким, соответствовать функции, выполняемой этим тэгом, а также начинаться, как и имена JSP-файлов, с буквы нижнего регистра. Префикс имени тэга не должен содержать каких бы то ни было неалфавитных символов. Вот несколько примеров:
Пример | OK? |
mytaglib | нет |
myTagLib | да |
MyTagLib | нет |
MyTagLib1 | нет |
My_Tag_Lib | нет |
My$Tag$Lib | нет |
JSP-страницы в синтаксисе XML
JSP предоставляет два разных синтаксиса: стандартный синтаксис для написания JSP-странц и XML-синтаксис для написания JSP-документов. В этой статье мы преимущественно рассматривали стандартный синтаксис JSP. Однако многие из рассмотренных нами концепций могут также применяться и для JSP-документов. Введение JSP-документов произошло во многом из-за преобладания XML и его повсеместного внедрения, а также в целях предоставления возможности спецификации JSP 2.0 обеспечить более дружественное взаимодействие с XML-синтаксисом.
Структура JSP-документа
Все JSP-документы должны иметь примерно следующую структуру:
<? xml version="1.0"?>
<!—
- Author(s):
- Date:
- Copyright Notice:
- @(#)
- Description:
—>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:prefix1="URI-for-taglib1"
xmlns:prefix2="URI-for-taglib2"
version="1.2">
JSP Document...
</jsp:root>
Первая строчка — это опциальный XML-пролог, который, собственно, позволяет определить, что это есть некий XML-документ. После пролога идет комментарий к документу.
Элемент <jsp:root> говорит о том, что это есть именно JSP-документ, а не какой-нибудь другой. Этот элемент должен быть корневым. Внутри этого тэга должны импортироваться все используемые именные пространства, а также библиотеки тэгов. Фактическое содержимое JSP-страницы — это все подэлементы корневого элемента <jsp:root>.
Поскольку JSP-документы должны соответствовать всем правилам построения XML-документов, некоторые элементы, как, например, <% %> должны быть заменены их XML-эквивалентами (в этом случае — на <jsp:scriptlet />). За более подробной информацией обращайтесь к спецификации JSP.
XML-комментарий
В спецификации JSP ничего не сказано по поводу того, остаются ли XML-комментарии внутри документа после обработки его JSP-процессором, поэтому лучше всего воспользоваться обходным маневром, т.к. в этом случае клиент в любом случае получит желаемое вами. Например, вот так:
...
<jsp:text><![CDATA[
<!—
- Многостроковый комментарий, который
- будет послан клиенту.
—>
]]></jsp:text>
...
Java-код в JSP-документах
При использовании Java-кода внутри объявлений, скриптлетов или выражений элемент CDATA должен использоваться лишь тогда, когда необходимо быть уверенным, что ваш код не вредит структуре документа:
...
<jsp:scriptlet>
for(int level = 0; level < 3; level++) {
</jsp:scriptlet>
<tr>
<td>
<jsp:expression><![CDATA[
"<h" + level + ">Text</h" + level + ">"
]]></jsp:expression>
</td>
</tr>
<jsp:scriptlet>
}
</jsp:scriptlet>
...
А вообще написания Java-кода в ваших JSP-страницах следует избегать хотя бы по следующим причинам:
JSP предоставляет подходящий элемент для инициализации всех свойств JavaBean'ов, например:
<jsp:setProperty name="bankClient" property="*"/>
Однако такой прием следует использовать с осторожностью. Во-первых, если бин содержит свойство, скажем, amount, и такого параметра (amount) не существует в текущем объекте ServletRequest или же его значение равно "", ничего не выйдет: JSP даже не присвоит null самым обыкновенным свойствам этого бина. Во-вторых, неэлементарные свойства, которые не имеют определенного заранее PropertyEditor'а, не обязательно будут проинициализированы строковым значением из объекта ServletRequest. В-третьих, злоумышленники могут указать дополнительные параметры в строке запроса, тем самым установив нежелательные свойства вашему бину в случае, если приложение спроектировано недостаточно тщательно.
Если вы все равно не хотите отказываться от использования property="*" в тэге jsp:setProperty и таким образом хотите получать более чистый код, примите во внимание совет добавлять комментарий перед этим тэгом, в котором указывать, какие свойства вы предполагаете инициализировать у конкретного бина.
Например, в следующем примере комментарий оповещает о том, что у бина bankClient должны быть проинициализированы два свойства: firstName и lastName.
<%—
- requires firstName and lastName from the ServletRequest
—%>
<jsp:setProperty name="bankClient" property="*" />
Кавычки
Вместо одинарных кавычек (') в большинстве случаев следует использовать двойные кавычки ("). Это общепринятое соглашение. Например:
Одинарные кавычки (не рекомендуется) | Двойные кавычки (предпочтительнее) |
<%@ page import='javabeans.*'%> <%@ page import="java.util.*" %> | <%@ page import="javabeans.*" %> <%@ page import="java.util.*" %> |
<jsp:include page='<%= getFoodMenuBar("Monday") %>' />
Использование JavaScript
Все ваши скрипты на JavaScript не должны зависеть от каких-либо особенностей конкретного браузера, чтобы правильно функционировать на всех существующих браузерах, которые поддерживают JavaScript. Также правилом хорошего тона в программировании считается выносить JavaScript-скрипты в отдельные файлы, отдельно от JSP-страниц. Для того, чтобы вставить файл со скриптом, можно воспользоваться следующим HTML-выражением:
<script language=javascript src="/js/main.js">
Это значительно увеличивает шансы этого скрипта на то, что им можно будет воспользоваться в других проектах в дальнейшем, и нисколько не осложнит JSP-код.
Каскадные таблицы стилей (CSS)
Как известно, каскадные таблицы стилей (Cascading Style Sheets) используются для централизации контроля над основными свойствами и характеристиками различных тэгов (заголовков, таблиц, ссылок и т.д.). Они значительно упрощают процесс поддержки кода и значительно уменьшают его размер. Так, вместо того, чтобы встраивать информацию о стиле в каждый HTML-тэг, где это необходимо, например:
<H1><FONT color="blue">Chapter 1</FONT></H1>
...
<H1><FONT color="blue">Chapter 2</FONT></H1>
...
Можно определить информацию о стилистике заголовка H1 в файле (например, myJspStyle.css) следующим образом:
H1 { color: blue }
А после применить его к JSP-странице:
<link rel="stylesheet" href="css/myJspStyle.css" type="text/css">
...
<H1>Chapter 1</H1>
...
<H1>Chapter 2</H1>
...
Прочие рекомендации
В этой статье было представлено множество рекомендаций касательно соглашений в коде JSP и прочих артефактов при разработке web-компонентов с целью улучшить и тем самым упростить его дальнейшую поддержку, а также придать ему более профессиональный и стандартизированный вид. Однако, если вы углубитесь в этот процесс, то обнаружите множество других полезных и важных приемов. Например, в спецификации JSP 1.2 присутствует множество дополнительных рекомендаций, с которыми вы можете ознакомиться.
Исходный код проекта, который использовался для написания этой статьи, а также уже собранный проект вы можете скачать отсюда: http://developer.java.sun.com/servlet/CodeConventionServlet/ . Вам понадобится скачать WAR-файл.
По материалам Kam Hay Fung и Mark Roth
Подготовил Алексей Литвинюк,
http://www.litvinuke.hut.ru
Компьютерная газета. Статья была опубликована в номере 12 за 2003 год в рубрике программирование :: разное