JSP (JavaServer Pages)¶
JSP - Это технология для динамических веб-страниц: HTML-шаблон со вставками Java-кода, тегов и выражений. - отделяет представление (HTML) от логики приложения (Java-код) - JSP автоматически компилируется в сервлет при первом обращении, поэтому сервлет вручную писать не нужно (см. Java Servlets)
JSP и сервлеты¶
JSP - это тот же сервлет, только написанный «наизнанку»: в сервлете HTML печатается из Java-кода, в JSP Java-код вставляется в HTML.
| Сервлет | JSP | |
|---|---|---|
| Что пишешь | Java-класс, HTML через out.println() |
HTML-страницу с тегами и выражениями |
| Удобно для | Логики: разбор запроса, вызов сервисов, выбор ответа | Представления: вёрстка, вывод данных |
| Изменения | Нужна перекомпиляция и передеплой | Контейнер сам перекомпилирует изменённую страницу |
| Первый запрос | Быстрый | Медленнее: трансляция и компиляция |
| Ошибки в коде | Видны при компиляции | Видны только при первом обращении к странице |
Область применения - слой представления (View). В связке с сервлетом-контроллером это Model 2 (см. Model 1 и Model 2). Сейчас JSP - legacy: вместо него берут Thymeleaf, JSF (Facelets) или SPA на фронтенде.
Жизненный цикл JSP¶
Жизненный цикл JSP почти совпадает с жизненным циклом сервлета, потому что JSP в итоге компилируется в сервлет.
- Трансляция - контейнер преобразует JSP в Java-файл сервлета.
- Компиляция - Java-файл компилируется в
.class. - Загрузка - класс сервлета загружается в контейнер, создаётся экземпляр.
- Инициализация - вызывается
jspInit(), после неё JSP готова обрабатывать запросы. - Обработка запросов - на каждый запрос вызывается
_jspService(), он формирует ответ. - Уничтожение - JSP больше не нужна или сервер останавливается: контейнер вызывает
jspDestroy().
Шаги 1-2 выполняются при первом запросе и повторяются, когда файл JSP изменился.
Структура JSP-страницы¶
| Элемент | Синтаксис | Что делает |
|---|---|---|
| Комментарий | <%-- ... --%> |
Не попадает в HTML-ответ, в отличие от <!-- --> |
| Директива | <%@ page ... %>, <%@ include ... %>, <%@ taglib ... %> |
Настройка страницы при трансляции |
| Объявление | <%! ... %> |
Поле или метод класса сервлета |
| Скриптлет | <% ... %> |
Произвольный Java-код внутри _jspService() |
| Выражение | <%= ... %> |
Вычислить и вывести в ответ |
<%-- Это комментарий JSP, не попадает в HTML-выход --%>
<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<%@ include file="header.jsp" %>
<%! private int counter = 0; %>
<% counter++; // изменение переменной %>
<p>Сообщение: <%= message %></p>
Java-код внутри JSP¶
Правила записи:
- код скриптлетов попадает в метод _jspService(): переменные из <% %> - локальные, свои у каждого запроса
- объявления <%! %> становятся полями класса. Экземпляр один на все запросы, поэтому counter из примера выше
общий для всех потоков и меняется без синхронизации
- выражение <%= %> пишется без ; в конце: оно целиком подставляется в out.print(...)
- импорт классов - директивой <%@ page import="java.util.List" %>
Стандартные (неявные) переменные - объекты, которые доступны в скриптлетах и выражениях без объявления:
| Переменная | Тип | Что это |
|---|---|---|
request |
HttpServletRequest |
Текущий запрос |
response |
HttpServletResponse |
Ответ |
out |
JspWriter |
Вывод в ответ: out.println(...) |
session |
HttpSession |
Сессия пользователя |
application |
ServletContext |
Контекст приложения |
config |
ServletConfig |
Настройки этого сервлета |
pageContext |
PageContext |
Доступ ко всем областям видимости |
page |
Object |
Сам экземпляр сервлета, this |
exception |
Throwable |
Только на странице ошибок с isErrorPage="true" |
JavaBeans в JSP¶
Через JavaBean в JSP передают и выводят данные, отделяя бизнес-логику от представления.
Как использовать в JSP:
1) Создать класс бина по соглашениям JavaBean.
2) Импортировать его директивой <%@ page import="..." %>.
3) Создать или найти экземпляр и задать свойства: <jsp:useBean> и <jsp:setProperty>.
4) Вывести свойства: <jsp:getProperty> или EL ${user.name}.
<%@ page import="com.example.User" %>
<jsp:useBean id="user" class="com.example.User" scope="session" /> <%-- найти в сессии или создать --%>
<jsp:setProperty name="user" property="name" value="Алексей" />
<jsp:setProperty name="user" property="age" value="30" />
<p>Имя: <jsp:getProperty name="user" property="name" /></p>
<p>Возраст: <jsp:getProperty name="user" property="age" /></p>
Стандартные теги JSP¶
Стандартные теги (actions) заменяют типовой Java-код в скриптлетах.
| Тег | Что делает |
|---|---|
<jsp:useBean id="user" class="..." scope="session" /> |
Найти бин в области видимости или создать новый |
<jsp:setProperty name="user" property="name" value="..." /> |
Задать свойство бина |
<jsp:getProperty name="user" property="name" /> |
Вывести свойство бина |
<jsp:include page="header.jsp" /> |
Вставить ответ другой страницы во время запроса |
<jsp:forward page="welcome.jsp" /> |
Передать запрос другой странице (forward) |
<jsp:param name="..." value="..." /> |
Параметр для include и forward |
<jsp:include> и директива <%@ include %> различаются моментом: директива вставляет текст файла при трансляции,
тег - результат выполнения страницы на каждом запросе.
Expression Language (EL)¶
EL - Это упрощённый синтаксис ${...} для доступа к данным: свойствам бинов, коллекциям, атрибутам запроса и
сессии.
- ${user.name} вызывает user.getName()
- имя ищется по областям видимости по порядку: page → request → session → application
- вместе с JSTL (<c:if>, <c:forEach>) убирает Java-код из страницы
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<jsp:useBean id="user" class="com.example.User" scope="session" />
<jsp:setProperty name="user" property="name" value="Алексей" />
<jsp:setProperty name="user" property="age" value="30" />
<html>
<head>
<title>Пример JSP с EL и стандартными тегами</title>
</head>
<body>
<h2>Информация о пользователе:</h2>
<p>Имя: ${user.name}</p>
<p>Возраст: ${user.age}</p>
<c:if test="${user.age >= 18}">
<p>Вы совершеннолетний.</p>
</c:if>
<h3>Список покупок:</h3>
<c:forEach var="item" items="${shoppingList}">
<p>${item.name}</p>
</c:forEach>
</body>
</html>
Note
URI http://java.sun.com/jsp/jstl/core - из Java EE. В Jakarta Tags 3.0 (Jakarta EE 10) пишут
uri="jakarta.tags.core".
Настройка JSP в web.xml¶
В web.xml для JSP есть элемент <jsp-config> с настройками для всех страниц приложения.
<jsp-property-group>- группа настроек для набора страниц, чтобы не дублировать их<url-pattern>- к каким страницам применяется группа<el-ignored>- игнорировать выражения EL<scripting-invalid>- запретить скриптлеты<is-xml>- страницы в XML-синтаксисе<trim-directive-whitespaces>- удалять пустые строки, которые остаются от директив
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<el-ignored>false</el-ignored>
<scripting-invalid>true</scripting-invalid> <!-- только EL и теги, без Java-кода -->
<trim-directive-whitespaces>true</trim-directive-whitespaces>
</jsp-property-group>
</jsp-config>
Model 1 и Model 2¶
Две архитектурные модели веб-приложения на Java EE. Обе вырастают из шаблона MVC, разница в том, кто принимает запрос.
Model 1 - Это модель, где запрос принимает сама JSP-страница. Представление и бизнес-логика объединены в одном компоненте: логика в скриптлетах или в JavaBeans, которые JSP вызывает напрямую.
<%@ page import="com.example.User" %>
<%@ page import="com.example.UserService" %>
<%
UserService userService = new UserService(); // бизнес-логика прямо в странице
User user = userService.getUserById(request.getParameter("id"));
%>
<html>
<head>
<title>User Details</title>
</head>
<body>
<h1>User Details</h1>
<p>Name: <%= user.getName() %></p>
<p>Email: <%= user.getEmail() %></p>
</body>
</html>
Model 2 - Это MVC на сервлетах и JSP: запрос принимает сервлет-контроллер, JSP только отображает. - Controller - сервлет: разбирает запрос, вызывает модель, выбирает представление - Model - JavaBeans и сервисные классы с бизнес-логикой - View - JSP: берёт данные из атрибутов запроса и выводит их
Как проходит запрос:
1) Сервлет получает запрос и его параметры.
2) Вызывает сервис и получает данные.
3) Кладёт данные в атрибут запроса: request.setAttribute("user", user).
4) Делает forward на JSP (см. Диспетчеризация запросов).
5) JSP достаёт атрибут и формирует HTML.
@WebServlet("/userDetails")
public class UserController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String userId = request.getParameter("id");
UserService userService = new UserService();
User user = userService.getUserById(userId);
request.setAttribute("user", user); // данные для представления
RequestDispatcher dispatcher = request.getRequestDispatcher("userDetails.jsp");
dispatcher.forward(request, response);
}
}
<%@ page import="com.example.User" %>
<%
User user = (User) request.getAttribute("user"); // то же через EL: ${user.name}
%>
<html>
<head>
<title>User Details</title>
</head>
<body>
<h1>User Details</h1>
<p>Name: <%= user.getName() %></p>
<p>Email: <%= user.getEmail() %></p>
</body>
</html>
Реализация на Java EE. Сервлеты и JSP из коробки дают всё для Model 2. Фреймворки строят Model 2 поверх них:
в Spring MVC контроллер - один DispatcherServlet (front controller), он раздаёт запросы методам @Controller (см.
Spring Web MVC).
Note
В исходной версии Spring MVC был назван фреймворком «для управления зависимостями». Зависимостями управляет Spring Core (IoC-контейнер), а Spring MVC - реализация Model 2 для веба.
| Model 1 | Model 2 | |
|---|---|---|
| Кто принимает запрос | JSP-страница | Сервлет-контроллер |
| Где бизнес-логика | В JSP: скриптлеты или бины, вызванные из страницы | В модели: сервисы и бины, их вызывает контроллер |
| Роль JSP | Всё сразу | Только представление |
| Плюсы | Просто реализовать, быстрая разработка | Чистое разделение компонентов, код читаемее и проще в поддержке, логика тестируется отдельно |
| Минусы | Для крупных приложений код становится сложным и трудно поддерживаемым, логику трудно тестировать | Сложнее реализовать |
| Когда | Простые приложения | Крупные и сложные системы |
Итого: разделение бизнес-логики и представления делает код модульнее и проще в сопровождении. Поэтому Model 2 стала стандартом, и на ней построены все MVC-фреймворки.