Зарегистрируйтесь, чтобы продолжить обучение

Сервлеты Java: Веб-технологии

Javalin — это не единственный фреймворк на Java, вместе со Spring Boot их десятки. Несмотря на это многообразие, все они базируются на механизме сервлетов (servlets). В этом уроке мы обсудим, что это такое и почему об этом нужно знать.

Клей между фреймворками и веб-серверами

Любой веб-фреймворк работает не сам по себе. Для запуска написанного на нем приложения нужен веб-сервер. Такой веб-сервер загружает приложение внутрь себя и запускает. Из этого следует два вывода:

  • Такой веб-сервер должен быть написан на том же языке — например, в Java среди основных веб-серверов выделяют Tomcat и Jetty
  • Веб-сервер и фреймворк должны знать друг о друге, чтобы они могли работать совместно

Теперь представьте, что у нас есть десятки фреймворков и десятки веб-серверов. Как им всем знать друг о друге? В худшем случае нам пришлось бы писать код для совместимости каждого с каждым. Это было бы пустой тратой ресурсов команд разработчиков, а создание нового фреймворка было бы очень затруднено.

К счастью, эта проблема решилась еще в конце девяностых, когда появилась спецификация сервлетов. С тех пор все веб-сервера ориентируются только на сервлеты, а фреймворки, используют сервлеты для своей работы «под капотом». Все, что мы писали на Javalin, внутри фреймворка превращается в сервлеты:

Архитектура Сервлетов

Эта спецификация описывает способ написания универсального веб-приложения под Java, в котором код состоит из сервлетов.

Каждый сервлет – это класс, отвечающий за определенный маршрут. Во время работы веб-сервер принимает входящие запросы и передает их сервлетам, отвечающим за запрошенные адреса. Затем сервлеты возвращают ответы, которые отправляются клиентам.

Можно ли сделать вид, что для работы с фреймворками мы не обязаны ничего знать про сервлеты? С одной стороны, да, ведь сервлеты существуют только где-то внутри. На каком-то уровне их можно не замечать. С другой стороны, игнорировать сервлеты не стоит. Они встречаются везде: то в выводах в логах, то в настройках. Даже в Spring Boot настройки часто работают с механизмом сервлетов напрямую. Кроме того, о них могут спросить на собеседовании.

Первый сервлет

Рассмотрим создание полноценного приложения с помощью сервлетов на примере репозитория java-servlet-gradle. Начнем с класса сервлета:

package io.hexlet.servlet;

import java.io.IOException;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@WebServlet(name = "MainServlet", urlPatterns = "/about")
public class MainServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        res.setContentType("text/plain");
        res.getWriter().write("Hello, Simple Servlet!");
    }
}

Здесь мы видим переопределенный метод doGet(). Он вызывается на Get-запрос по адресу /about, указанному в аннотации. Внутри есть доступ к объектам запроса и ответа. В нашем примере данные запроса не используются, а вот в ответе отдается строчка текста. Перечислим моменты, на которые нужно обратить внимание:

  • Jakarta (Jakarta EE) — это набор спецификаций и компонентов, предоставляющих решения для различных прикладных приложений. Туда входят и сервлеты и многое другое, что встречается в реальных проектах
  • Сервлет наследуется от класса HttpServlet, импортированного из Jakarta
  • Сервлет нужно пометить аннотацией @WebServlet с указанием имени и маршрута, за который этот сервлет отвечает

Так можно добавить любое количество сервлетов:

src/main/java
└── io
    └── hexlet
        └── servlet
            ├── MainServlet.java
            └── UsersServlet.java

Чтобы запустить веб-сервер с нашим приложением, понадобится кое-что еще установить и настроить. Ниже урезанная версия файла build.gradle.kts:

plugins {
    id("java")
    // Плагин, который устанавливает и автоматически настраивает веб-сервер
    id("org.gretty") version "4.1.0"
    // И другие плагины
}

dependencies {
    // Необходимые классы
    implementation("jakarta.servlet:jakarta.servlet-api:6.0.0")
}

gretty {
    // Эта настройка веб-сервера указывает, что нужно работать от корня
    // По умолчанию базовый путь равен названию проекта
    contextPath = '/'
}

После этого можно запустить приложение:

./gradlew appRun
2023-08-19 21:03:52.194:INFO :oejs.Server:main: jetty-11.0.15; built: 2023-04-11T18:37:53.775Z; git: 5bc5e562c8d05c5862505aebe5cf83a61bdbcb96; jvm 20.0.2+9-78
2023-08-19 21:03:52.224:INFO :oejs.AbstractConnector:main: Started ServerConnector@7af1cd63{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
2023-08-19 21:03:52.243:INFO :oejs.Server:main: Started Server@5cf8edcf{STARTING}[11.0.15,sto=0] @1461ms
2023-08-19 21:03:52.639:INFO :oejss.DefaultSessionIdManager:main: Session workerName=node0
2023-08-19 21:03:52.664:INFO :oejsh.ContextHandler:main: Started o.a.g.JettyWebAppContext@186cb891{/,/,file:///Users/kirillmokevnin/repos/java-servlet-gradle/build/inplaceWebapp/,AVAILABLE}
2023-08-19 21:03:52.673:INFO :oag.JettyConfigurerImpl:main: Jetty 11.0.15 started and listening on port 8080
2023-08-19 21:03:52.689:INFO :oag.JettyConfigurerImpl:main:  runs at:
2023-08-19 21:03:52.690:INFO :oag.JettyConfigurerImpl:main:   http://localhost:8080/

> Task :appRun
Press any key to stop the server.
<===========--> 87% EXECUTING [25s]
> :appRun

После этого приложение станет доступно в браузере по адресу http://localhost:8080. Если открыть страницу https://localhost:8080/about, то мы увидим текст Hello, Simple Servlet!.

Шаблонизатор Java Server Pages (JSP)

Кроме сервлетов, Jakarta содержит в себе JSP. Это простой шаблонизатор, который можно использовать для генерации HTML-страниц на сервере. Для примера создадим другой сервлет и посмотрим, как использовать JSP:

package io.hexlet.servlet;

import java.io.IOException;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@WebServlet(name = "UsersServlet", urlPatterns = "/users")
public class UsersServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String message = "Hello from Jakarta Servlet!";
        String[] users = {"Mike", "Nina"};
        req.setAttribute("message", message);
        req.setAttribute("users", users);

        req.getRequestDispatcher("/WEB-INF/users.jsp").forward(req, resp);
    }
}

Общая структура сервлета не поменялась, но добавилось несколько деталей:

  • Данные, которые мы хотим передать в шаблон, нужно записывать в объект запроса через метод setAttribute()
  • Нужно явно указать, какой шаблон использовать

Для хранения шаблонов используется стандартная директория src/main/webapp/WEB-INF:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
    <head>
        <title>JSP Example</title>
    </head>
    <body>
        <h1><%= request.getAttribute("message") %></h1>
        <ul>
            <c:forEach items="${users}" var="user">
                <li>${user}</li>
            </c:forEach>
        </ul>
    </body>
</html>

В шаблоне можно отметить четыре важных элемента:

  • Первой строчкой идет c:forEach — директива для работы JSP-тегов (они начинаются с префикса c)
  • Запрос и ответ доступны в шаблоне напрямую через переменные request и response
  • Для вывода данных используется синтаксис <%= %>, внутри которого можно вызывать Java-код
  • Для управляющих конструкций используется синтаксис на нестандартных тегах, которые превращаются в обычный HTML после обработки

Для работы JSP нужно добавить несколько зависимостей:

implementation("jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api:3.0.0")
implementation("org.glassfish.web:jakarta.servlet.jsp.jstl:3.0.1")

Самостоятельная работа

  1. Склонируйте приложение java-servlet-gradle на свой компьютер
  2. Изучите склонированное приложение
  3. Добавьте в него еще один сервлет, который будет выводить список всех автомобилей из массива при GET-запросе на адрес /cars. Для генерации HTML используйте jsp-шаблон
  4. Запустите приложение и попробуйте выполнить несколько запросов

Дополнительные материалы

  1. Класс HttpServlet
  2. Класс HttpServletRequest, представляющий HTTP-запрос
  3. Класс HttpServletResponse, представляющий HTTP-ответ

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 25 000 ₸ в месяц
Разработка приложений на языке Java
10 месяцев
с нуля
Старт 26 декабря

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»