В этой главе рассматриваются контракты между JSP-контейнером и JSP-страницей.
Протокол прекомпиляции (см. Раздел JSP.8.4) также представлен здесь.
Информация, представленная здесь, не зависит от Языка Скриптинга, используемого на JSP-странице.
Глава JSP.6 описывает информацию для случая, когда атрибут
language
директивы page
установлен в "java"
.
Классы реализации
JSP-страницы должны использовать классы JspFactory
и PageContext
для получения
преимуществ платформозависимых реализаций.
JSP-страница представлена на этапе выполнения объектом реализации JSP-страницы и выполняется JSP-контейнером. Объект реализации JSP-страницы - это servlet/сервлет. JSP-контейнер направляет запросы от клиента объекту реализации JSP-страницы и ответы объекта реализации JSP-страницы - клиенту.
JSP-страница описывает создание объекта response из объекта request
для данного протокола, возможно, создавая и/или используя в ходе этого процесса
некоторые другие объекты. JSP-страница может также указывать, как должны обрабатываться некоторые события.
В JSP 1.2 только события init и destroy являются допустимыми событиями.
JSP-контейнер локализует соответствующий экземпляр класса реализации
JSP-страницы и направляет ему запросы, используя протокол Servlet.
JSP-контейнеру может потребоваться создать такой класс динамически из исходной JSP-страницы до
направления ей объектов request
и response
.
Класс Servlet
определяет контракт между JSP-контейнером и классом реализации JSP-страницы.
Если используется протокол HTTP, контракт описывается классом
HttpServlet
. Большинство JSP-страниц используют протокол HTTP, но другие
протоколы также разрешены данной спецификацией.
JSP-контейнер автоматически делает несколько серверных/server-side объектов
доступными объекту реализации JSP-страницы. См. Раздел JSP.2.8.3.
JSP-спецификация определяет контракт между JSP-контейнером и автором JSP-страницы.
Этот контракт определяет обязательства, которые автор может установить для
акций, описанных на JSP-странице.
Главной частью этого контракта является метод _jspService(), который
генерируется автоматически JSP-контейнером из JSP-страницы.
Детальная информация об этом контракте дана в Главе JSP.6.
Контракт описывает также, как автор JSP может обозначить, какие акции будут предприниматься, когда будут вызываться
методы реализации страницы init() и destroy(). В спецификации
JSP 1.2 это делается через определение методов с именами jspInit() и jspDestroy()
в объявлении элемента скриптинга в JSP-странице.
Метод jspInit(), если он имеется, будет вызываться для подготовки
страницы перед направлением первого запроса. Аналогично, JSP-контейнер может
затребовать ресурсы, используемые JSP-страницей, если запрос не обслуживается JSP-страницей,
через вызов её метода jspDestroy(), если он имеется.
Автор JSP-страниц не может (пере)определять методы Servlet через объявление элемента скриптинга.
JSP-спецификация резервирует имена методов и переменных, начинающиеся с jsp, _jsp, jspx и _jspx, в любом сочетании регистров.
HttpJspPage
Выполнению контракта между JSP-контейнером и автором JSP-страницы помогает
требование о том, что класс Servlet
, соответствующий JSP-странице, обязан
реализовывать интерфейс HttpJspPage
(или интерфейс JspPage
, если протокол - не HTTP).
JSP-Контейнер |
JSP-Страница |
---|---|
|
На Рисунке J2EE.8.1 показаны включённые контракты. Теперь мы рассмотрим этот процесс более детально.
JSP-контейнер создаёт класс реализации JSP-страницы для каждой JSP-страницы. Имя класса реализации JSP-страницы зависит от особенностей реализации. Объект реализации JSP-страницы принадлежит к зависящему от реализации именованному архиву. Этот архив может отличаться от одной JSP-страницы к другой. Неименованный архив не должен использоваться без явного "импортирования" класса.
JSP-контейнер может создавать для JSP-страницы класс реализации, либо суперкласс
может быть предоставлен автором JSP-страницы с помощью атрибута extends
директивы page
.
Механизм extends
предназначен для опытных пользователей. Он должен
использоваться предельно осторожно, так как он ограничивает возможность принятия
решений JSP-контейнером. Он может, к примеру, свести на нет усилия по повышению производительности.
Класс реализации JSP-страницы будет реализовывать Servlet, а протокол
Servlet будет использоваться для направления запросов классу.
Класс реализации JSP-страницы может зависеть от поддержки других классов. Если
класс реализации JSP-страницы упакован в WAR, все связанные классы должны будут
быть включены, так что пакет будет переносим на все JSP-контейнеры.
Автор JSP-страницы пишет JSP-страницу, ожидая, что клиент и сервер будут
взаимодействовать по определённому протоколу. JSP-контейнер обязан
гарантировать, штаа запросы и ответы для страницы используют нужный протокол.
Большинство JSP-страниц используют протокол HTTP, и их классы реализаций обязаны
реализовать интерфейс HttpJspPage
, который расширяет JspPage
.
Если это не HTTP-протокол, тогда класс реализует интерфейс, расширяющий JspPage
.
Контракт между JSP-контейнером и Java-классом, реализующим JSP-страницу, соответствует интерфейсу
Servlet
. См. детали в спецификации Servlet 2.3.
Ответственность за выполнение этого контракта лежит на реализации JSP-контейнера,
если JSP-страница не использует атрибут extends директивы jsp.
Если атрибут extends директивы jsp
используется, автор JSP-страниц обязан гарантировать, что суперкласс, заданный в
атрибуте extends
, поддерживает этот контракт.
Комментарии | Методы, вызываемые JSP-Контейнером |
---|---|
Метод может по выбору/optionally быть определён в JSP-странице. Метод вызывается при инициализации JSP-страницы. Если метод вызывается, доступны все методы сервлета, включая getServletConfig() . |
void jspInit()
|
Метод по выбору определяется в JSP-странице. Метод вызывается при уничтожении страницы. |
void jspDestroy()
|
Метод не может быть определён в JSP-странице. JSP-контейнер автоматически генерирует этот метод, базируясь на содержимом JSP-страницы. Метод вызывается при каждом клиентском запросе. |
void_jspService(<ServletRequestSubtype>, <ServletResponseSubtype>) throws
IOException, |
Как видно из Таблицы JSP.8-1, методы контракта между JSP-контейнером и JSP-страницей требуют наличия параметров запроса и ответа.
Формальным типом параметра запроса (который в этой спецификации называется <ServletRequestSubtype>
)
является интерфейс, расширяющий javax.servlet.ServletRequest
.
Этот интерфейс обязан определять зависящий от используемого протокола запроса
контракт между JSP-контейнером и классом, реализующим JSP-страницу.
Аналогично и формальный тип параметра ответа (называемый в этой спецификации
<ServletResponseSubtype>
)
является интерфейсом, расширяющим
javax.servlet.Servlet-Response
. Этот интерфейс обязан определять зависящий от
используемого протокола ответа контракт между JSP-контейнером и классом,
реализующим JSP-страницу. Интерфейсы запроса и ответа вместе описывают зависящий
от протокола контракт между JSP-контейнером и классом, реализующим эту JSP-страницу.
HTTP-контракт определяется интерфейсами
javax.servlet.http.HttpServletRequest
и javax.servlet.http.HttpServletResponse
.
Интерфейс JspPage
ссылается на эти методы,
но не может синтаксически описать методы, вызывающие подтипы
Servlet(Request,Response)
. Однако интерфейсы для специфических протоколов,
которые расширяют JspPage
, могут делать это, так же, как
HttpJspPage
описывает их для протокола HTTP.
JSP-контейнеры, соответствующие этой спецификации (классами реализации JSP-страницы
и работой JSP-контейнера), обязаны реализовывать интерфейсы запроса и ответа (request
и response
) для HTTP-протокола, как описано в этом разделе.
extends
Если атрибут extends
директивы page
(см.
Раздел 2.10.1) в JSP-странице не используется, JSP-контейнер может
генерировать любой класс, удовлетворяющий контракту, описанному в Таблице JSP.8-1,
если он трансформирует JSP-страницу.
В следующих примерах Пример Кода 8.1 иллюстрирует общий/родовой HTTP-суперкласс,
названный ExampleHttpSuper
.
В Примере Кода 8.2 показан подкласс, названный _jsp1344
, который расширяет
ExampleHttpSuper
и является классом, сгенерированным из JSP-страницы.
Используя отдельные классы _jsp1344
и
ExampleHttpSuper
, транслятор JSP-страницы не нуждается в поиске информации,
содержит ли JSP-страница объявления с jspInit()
или jspDestroy()
.
Это значительно упрощает реализацию.
imports javax.servlet.*;
imports javax.servlet.http.*;
imports javax.servlet.jsp.*;
/**
* Пример суперкласса для HTTP JSP-класса
*/
abstract class ExampleHttpSuper implements HttpJspPage {
private ServletConfig config;
final public void init(ServletConfig config) throws ServletException {
this.config = config;
jspInit();
public void jspInit() {
}
public void jspDestroy() {
}
}
final public ServletConfig getServletConfig() {
return config;
}
// Этот метод не является final, поэтому он может быть переопределён более точным методом
public String getServletInfo() {
return "Суперкласс для HTTP JSP"; // можно и получше?
}
final public void destroy() {
jspDestroy();
}
/**
* Точка входа в сервис.
*/
final public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
// количество отловленных исключений увеличится при наличии внутренней ошибки.
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
_jspService(request, resonse);
/**
* Абстрактный метод, предоставляемый JSP-процессором в подклассе,
* обязан быть определён в подклассе.
*/
abstract public void _jspService(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException;
}
imports javax.servlet.*;
imports javax.servlet.http.*;
imports javax.servlet.jsp.*;
/**
* Пример класса, генерируемого для JSP.
*
* Имя класса непредсказуемо.
* Мы принимаем, что это пакет HTTP JSP (как чаще всего и бывает)
*/
class _jsp1344 extends ExampleHttpSuper {
// Следующий код вставлен непосредственно через объявления.
// Любые следующие части могут или могут не иметься;
// если они не определены здесь, будут использоваться
// методы суперкласса.
public void jspInit() {....}
public void jspDestroy() {....}
// Следующий метод генерируется автоматически
// JSP-процессором.
// Тело/body JSP-страницы
public void _jspService(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// инициализация неявных переменных
HttpSession session = request.getSession();
ServletContext context =
getServletConfig().getServletContext();
// для этого примера мы принимаем, что директива буферизована
JSPBufferedWriter out = new
JSPBufferedWriter(response.getWriter());
// далее идёт код из скриптлетов, выражений и статического текста.
}
}
extends
Если автор JSP-страниц использует extends
,
генерируемый класс идентичен классу из Примера Кода 8.2, за исключением того,
что имя этого класса это имя, специфицированное в атрибуте extends
.
Контракт класса реализации JSP-страницы не изменяется. JSP-контейнер должен
проверить (обычно через отражение/reflection), что предоставленный суперкласс:
HttpJspPage
, если протокол - HTTP, либо
JspPage
- в ином случае.Servlet
объявлены final
.service()
из Servlet API вызывает метод _jspService()
;
init(ServletConfig)
хранит конфигурацию,
даёт к ней доступ как к getServletConfig
, затем вызывает jspInit
;destroy
вызывает jspDestroy
.JSP-контейнер может выдавать фатальную ошибку трансляции, если обнаружит, что предоставленный суперкласс не удовлетворяет этим требованиям, но большинство JSP-контейнеров эту проверку не будут выполнять.
JSP-контейнер буферизует данные (если директива jsp
специфицирует это, используя
атрибут buffer
), когда они высылаются от сервера клиенту.
Headers/"Шапки"
клиенту не высылаются, пока не вызван первый метод
flush
. Следовательно, ни одна из операций, имеющих отношение к шапкам, таких как
методы setContentType
, redirect
или error
, не является верной до тех пор, пока метод flush
не
начнёт выполняться и шапки не начнут высылаться.
Класс javax.servlet.jsp.JspWriter
буферизует вывод и высылает его. Класс
JspWriter
используется в методе _jspService
, как в следующем примере:
import javax.servlet.jsp.JspWriter;
static JspFactory _jspFactory = JspFactory.getDefaultFactory();
_jspService(<SRequest> request, <SResponse> response) {
// инициализация неявных переменных ...
PageContext pageContext = _jspFactory.createPageContext(
this,
request,
response,
false,
PageContext.DEFAULT_BUFFER,
false
);
JSPWriter out = pageContext.getOut();
// ....
// .... тело идёт здесь через "out"
// ....
out.flush();
}
Вы можете найти полный листинг
javax.servlet.jsp.JspWriter
в Главе JSP.9.
При включённой буферизации, Вы можете всё ещё использовать метод redirect
скриптлета в файле .jsp, вызывая
response.redirect(какой-то URL)
напрямую.
JSP-страница, использующая протокол HTTP, будет получать HTTP-запросы.
Контейнеры, соответствующие JSP 1.2, обязаны поддерживать простой протокол
прекомпиляции, а также некоторые базовые зарезервированные имена
параметров. Заметьте, что протокол прекомпиляции это понятие близкое, но не
то же самое, что компиляция JSP-страницы в Servlet-класс (Приложение JSP.A).
Все имена параметров запроса, начинающиеся с префикса "jsp"
,
зарезервированы спецификацией
JSP и не должны использоваться иначе, чем установлено данной спецификацией.
Все JSP-страницы должны игнорировать (и не зависеть от) параметры, начинающиеся
с "jsp_"
Запрос к JSP-странице, содержащий параметр запроса с именем "jsp_precompile"
, является
запросом прекомпиляции. Параметр "jsp_precompile"
может не
содержать значения или может содержать значения "true"
или "false"
.
В любом случае, такой запрос не должен отправляться JSP-странице.
Назначение запроса прекомпиляции в том, чтобы указать JSP-контейнеру, что нужно
прекомпилировать (предварительно откомпилировать) JSP-страницу в класс
реализации JSP-страницы. Это указание переправляется путём задания параметру
значения "true"
, или не задавая никакого значения, но учтите, что
запрос может быть проигнорирован.
Например:
1. ?jsp_precompile
2. ?jsp_precompile="true"
3. ?jsp_precompile="false"
4. ?foobar="foobaz"&jsp_precompile="true"
5. ?foobar="foobaz"&jsp_precompile="false"
1, 2 и 4 - действуют; запросы не будут направлены странице.
3 и 5 - верны; запрос не будет направлен странице.
6. ?jsp_precompile="foo"
- это неверно, и будет генерироваться ошибка HTTP error; 500 (Server error).