User Professor Hans Noodles
Professor Hans Noodles
41 уровень

Часть 5. Сервлеты, Java servlet API. Пишем простое веб-приложение

Статья из группы Java Developer
Этот материал — часть цикла “Введение в Enterprise-разработку”. Предыдущие статьи: Часть 5. Сервлеты, Java servlet API. Пишем простое веб-приложение - 1Ты уже умеешь писать Java-приложения, которые выводят текст на консоль, но еще толком не знаешь, как создать свое первое веб-приложение? Отлично, устраивайся поудобнее. В этой статье мы познакомимся с сервлетами и напишем приложение, которым ты сможешь похвастать перед друзьями, не отправляя им джарник и не заставляя их качать джаву. Напишем веб-приложение. Если ты еще не знаком с подходами, которые используются в веб-программировании, советую начать чтение с первой статьи цикла “Введение в Enterprise-разработку”.

Что такое сервлет

Для начала разберемся, что такое сервлет и почему ты так часто слышишь о нем. Java Servlet API — стандартизированный API, предназначенный для реализации на сервере и работе с клиентом по схеме запрос-ответ. Сервлет — это класс, который умеет получать запросы от клиента и возвращать ему ответы. Да, сервлеты в Java — именно те элементы, с помощью которых строится клиент-серверная архитектура. Если помнишь, о ней мы уже говорили в одной из статей цикла. Не будем ходить вокруг да около: давай сразу напишем немного кода.

Что нужно для создания веб-приложения

Для комфортной работы с сервлетами в Java тебе понадобится Intellij IDEA Ultimate Edition. Она платная, но можно активировать 30 дней пробного периода или же пользоваться early access версией — она всегда бесплатная. Также установи сервер нашего приложения — Apache Tomcat. Tomcat — это контейнер сервлетов: именно он обрабатывает входящие запросы извне и передает их нашему приложению. Скачать Tomcat можно по этой ссылке.

Создаем первое веб-приложение

Если все готово, создадим Maven-проект. Если ты не знаком с Мавеном, обрати внимание на предыдущую статью. Ну что, начнем!
  1. В pom.xml добавим зависимость javax.servlet-api и установим packaging war:

    
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
       <modelVersion>4.0.0</modelVersion>
    
       <groupId>org.example</groupId>
       <artifactId>servlets</artifactId>
       <version>1.0-SNAPSHOT</version>
       <packaging>war</packaging>
    
       <dependencies>
           <dependency>
               <groupId>javax.servlet</groupId>
               <artifactId>javax.servlet-api</artifactId>
               <version>4.0.1</version>
           </dependency>
       </dependencies>
    </project>
    

    Класс простого сервлета:

    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    @WebServlet("/hello")
    public class MainServlet extends HttpServlet {
    
       @Override
       protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
           resp.setContentType("text/html");
           PrintWriter printWriter = resp.getWriter();
           printWriter.write("Hello!");
           printWriter.close();
       }
    }
    
  2. Для запуска приложения нужно создать Tomcat-конфигурацию:

    Часть 5. Сервлеты, Java servlet API. Пишем простое веб-приложение - 2 Часть 5. Сервлеты, Java servlet API. Пишем простое веб-приложение - 3

  3. Далее указываем, какую версию Tomcat мы будем использовать, URL, по которому можно обращаться к серверу и порт. У тебя должно получиться примерно так:

    Часть 5. Сервлеты, Java servlet API. Пишем простое веб-приложение - 4
  4. Осталось указать артефакт (собранный проект в jar-архив), который развернется в контейнере. Можно нажать кнопку Fix и выбрать war exploded: это значит, что после пересборки проекта артефакт будет автоматически помещаться в контейнер сервлетов. Часть 5. Сервлеты, Java servlet API. Пишем простое веб-приложение - 5

  5. Application context по умолчанию установлен servlets_war_exploded, а это значит, что к приложению нужно обращаться по адресу: http://localhost:8080/servlets_war_exploded.

    Зачем нам лишний текст? Удалим ненужное. Теперь адрес приложения у нас такой: http://localhost:8080.

    Часть 5. Сервлеты, Java servlet API. Пишем простое веб-приложение - 6

  6. Нажимаем ОК. Видим, что у нас появилась возможность запуска приложения:

    Часть 5. Сервлеты, Java servlet API. Пишем простое веб-приложение - 7

    Теперь при запуске приложения должен открыться браузер и выдать 404-ю ошибку. Это логично, ведь по адресу http://localhost:8080/ должен находиться сервлет с мапингом “/”, а у нашего единственного сервлета мапинг "/hello".

  7. Обращаемся к нему по адресу http://localhost:8080/hello, и получаем ожидаемый ответ — строку “Hello”!

Часть 5. Сервлеты, Java servlet API. Пишем простое веб-приложение - 8Если все работает, давай разберем код. Чтобы из обычного класса сделать http-сервлет, его нужно унаследовать от класса HttpServlet. Над классом указываем аннотацию @WebServlet(), в которой привязываем (мапим) сервлет к конкретному пути (“/hello”). Эта аннотация появилась только в Java Servlet API 3.0, поэтому в интернете очень много примеров, где мапинг сервлетов происходит через XML-файл. Сейчас это не обязательно. Чтобы обрабатывать GET-запросы, переопределяем метод doGet(). Обрати внимание на аргументы метода — HttpServletRequest и HttpServletResponse. С объекта HttpServletRequest мы можем взять всю необходимую информацию о запросе, в HttpServletResponse можем записать наш ответ и установить необходимые заголовки.

Работа с параметрами и сессией

Усовершенствуем наш сервлет, чтобы он мог обрабатывать параметры запроса и работать с сессией:

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/hello")
public class MainServlet extends HttpServlet {

   @Override
   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       HttpSession session = req.getSession();
       Integer visitCounter = (Integer) session.getAttribute("visitCounter");
       if (visitCounter == null) {
           visitCounter = 1;
       } else {
           visitCounter++;
       }
       session.setAttribute("visitCounter", visitCounter);
       String username = req.getParameter("username");
       resp.setContentType("text/html");
       PrintWriter printWriter = resp.getWriter();
       if (username == null) {
           printWriter.write("Hello, Anonymous" + "<br>");
       } else {
           printWriter.write("Hello, " + username + "<br>");
       }
       printWriter.write("Page was visited " + visitCounter + " times.");
       printWriter.close();
   }
}
Сейчас сервлет работает с сессией, увеличивая счетчик visitCounter при каждом посещении страницы. Если атрибут visitCounter еще не создан (при первом посещении страницы), метод getAttribute() вернет null, поэтому нужно проводить проверку на null. То же касается и параметров запроса. Если пользователь не передал параметр username, его значение будет null. В таком случае поприветствуем пользователя как анонимного. Чтобы передать параметр в GET-запросе, используются path-variables, то есть нужно обратиться по ссылке http://localhost:8080/hello?username=Pavel. Подробней об http-запросах можно почитать в предыдущей статье цикла. Теперь у нашего приложения есть минимальная логика, но немного раздражает 404-я ошибка в root-пути. Чтобы исправить ее, создадим еще один сервлет и замапим его на начальную страницу @WebServlet("/"). Задача этого сервлета — перенаправлять запросы на путь “/hello”. Сделать это можно двумя способами: с помощью forward или redirect. Пожалуй, стоит разобраться, в чем между ними разница. forward — делегирует обработку запроса другому сервлету на сервере, клиент при этом не задействуется. Для этого в метод doGet() нового сервлета нужно добавить такой код:

getServletContext().getRequestDispatcher("/hello").forward(req, resp);
В этом коде мы обращаемся к контексту сервлетов, из него достаем диспетчер запросов нужного сервлета и просим его обработать конкретный запрос с указанными параметрами (req, resp). redirect — возвращает клиенту адрес, по которому нужно обратиться для обработки его запроса. Большинство браузеров переходит на переданную ссылку автоматически. Для реализации редиректа нужно добавить этот код:

resp.sendRedirect(req.getContextPath() + "/hello");
Мы в HttpServletResponse вызываем метод redirect() и передаем ему адрес, на который клиенту нужно обратиться. Важная деталь: http-параметры нужно также добавить в конце полного пути редиректа, что не очень удобно. В нашей ситуации предпочтительнее использовать forward, а бывает так, что лучше — redirect. Если будешь понимать разницу в их работе, не ошибешься с выбором. Код нового сервлета выглядит так:

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/")
public class IndexServlet extends HttpServlet {

   @Override
   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        getServletContext().getRequestDispatcher("/hello").forward(req, resp);
       resp.sendRedirect(req.getContextPath() + "/hello");
   }
}

Итог

Твое первое веб-приложение готово. В следующей статье ты узнаешь, как развернуть его без использования Intellij IDEA. Мы написали приложение, которое обрабатывает только GET-запросы. Остальные http-методы обрабатываются аналогичным образом — переопределяя соответствующие методы родительского класса. Используя такие простые сервлеты, можно строить сложные многофункциональные веб-приложения. Конечно, используя большие фреймворки типа Spring это делать намного проще. Но если очень хочется вникнуть подробнее во все возможности сервлетов, можешь почитать официальную спецификацию. Часть 6. Контейнеры сервлетов Часть 7. Знакомство с паттерном MVC (Model-View-Controller) Часть 8. Пишем небольшое приложение на spring-boot
Комментарии (46)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ СДЕЛАТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Max Уровень 25
30 июля 2021
Для тех, кто вышел из зоны комфорта (не имеет Intellij IDEA Ultimate Edition 😜). Либо вариант предложенный Raphael c EAP, либо: собираем проект в war и просто кладём его в папку Tomcat ~\Apache Software Foundation\Tomcat 9.0\webapps либо делаем деплой через веб-морду самого томкат.
Яна Тен Уровень 25, Москва, Россия
29 июня 2021
ох, чуть с ума не сошла😂Спасибо комментариям, помогли. Итог: если у вас на jdk15 и tomcat 10 не отображает Hello!, то: 1. Добавляем плагин

<plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.2.3</version>
        <configuration>
            <failOnMissingWebXml>false</failOnMissingWebXml>
        </configuration>
    </plugin>
2. Меняем бибилиотеку javax на jakarta:

        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>5.0.0</version>
            <scope>provided</scope>
        </dependency>
Ну, и в main не забываем javax на jakarta поменять
Anonymous #2686522 Уровень 1, Новосибирск
11 июня 2021
У вас написано: "Чтобы передать параметр в GET-запросе, используются path-variables, то есть нужно обратиться по ссылке http://localhost:8080/hello?username=Pavel". Но ведь это не PathVariable, а RequestParam. PathVariable пишется после слэша, а после вопросительного знака перечисляются RequestParam.
Miasnick-off Уровень 30, Москва, Россия
23 мая 2021
При запуске после настроенного pom.xml и созданного класса сервлета согласно указаниям из статьи выдает такую ошибку: Error:java: error: release version 5 not supported Стоит удалить класс сервлета, как все норм запускается (открывается браузер с 404-ой ошибкой). maven 3.8.1 tomcat-9.0.46 Java Servlet API 4.0.1 Что это сущность такая с версией 5 никак не пойму, уже всю голову сломал. Может кто сталкивался с такой проблемой?
Артур Уровень 18, Москва
21 апреля 2021
По какой-то причине у меня отсутствуют Artifact`ы, не понимаю в чем причина. Сталкивался ли кто-то с такой проблемой? Подскажите кто уже решил или понимает что не так.
Anonymous #2491313 Уровень 35
19 апреля 2021
Нифига не получается. Делаю deploy через maven plugin tomcat7. Он вроде как его деплоит во внешний запущенный tomcat10. Но при попытке открыть приложение пишет 404. При этом действительно в папке webapps приложение есть и папка WEB-INF/classes/ и мой класс в ней тоже присутствуют. Путь в @WebServlet пробовал в / ставить - бесполезно. Если попробовать запустить приложение через tomcat7:run то вообще tomcat говорит что мой класс не является сервлетом.
Valua Sinicyn Уровень 41, Харьков, Украина
15 марта 2021
Библиотека javax была переименована в jakarta.
Vitaly Radugin Уровень 0
12 марта 2021
Вот полный туториал для тех, кто не смог настроить конфигурацию. https://www.youtube.com/watch?v=8lh3jElhcDs
КиберНуб Уровень 23, Казань, Россия
18 февраля 2021
либо я тупой, либо тут ну очень сокращенно написано
Raphael Уровень 41, Москва, Россия
4 февраля 2021
Для комфортной работы с сервлетами в Java тебе понадобится Intellij IDEA Ultimate Edition. Она платная, но ... можно пользоваться early access версиейона всегда бесплатная Early Access версии - или - IntelliJ IDEA Early Access Program (EAP) - это сборки IntelliJ IDEA Community и IntelliJ IDEA Ultimate которые находятся в активной разработке, но их также можно использовать для любых целей, включая разработку проприетарного и коммерческого ПО. Данные версии могут быть нестабильными и содержать ошибки, а некоторая функциональность может работать некорректно. Используя EAP-версии, вы принимаете на себя возможные риски и соглашаетесь с тем, что продукт может иметь недостатки. Пользуемся 😃