JavaRush /Java блог /Java Developer /Часть 5. Сервлеты, Java servlet API. Пишем простое веб-пр...
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”!

Если все работает, давай разберем код. Чтобы из обычного класса сделать 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
Комментарии (88)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Andrew Karev Уровень 51
15 мая 2024
404
AhanSere Уровень 25
3 апреля 2024
Если у вас бесплатная Intellij IDEA Community Edition, как у многих наверное, можно в ней подключить плагин (File-Settings-Plugins) Smart Tomcat и будет так же все работать
Alexander Karpeev Уровень 51
28 января 2024
У меня вместо открытия страницы скачивается файл с названием Hello и содержимым hello. Так и должно быть?
Maxim B Уровень 42
11 декабря 2023
Приведенный пример для Tomcat 9. Для Tomcat 10.1.x нужны другие зависимости, иначе 404 pom.xml

<?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>servlet</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>6.0.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

</project>
src/main/java/MainServlet.java

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.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!");
        prin
Anonymous #2212601 Уровень 39
30 сентября 2023
Заработало после: 1. Смены 8080 на 8081 в двух местах в окне настройки конфигурации. 2. Игнорирования пункта "5" - с ним не работает (не нужно удалять лишний текст). 3. Смены папки на ресурсную по совету пользователя "GoldenAlf" на несколько комментариев ниже.
Kurama Уровень 50
19 мая 2023
Теперь при запуске приложения должен открыться браузер и выдать 404-ю ошибку. Это логично, ведь по адресу http://localhost:8080/ должен находиться сервлет с мапингом “/”, а у нашего единственного сервлета мапинг "/hello". Обращаемся к нему по адресу http://localhost:8080/hello, и получаем ожидаемый ответ — строку “Hello”! Ага, только вот по адресу http://localhost:8080/hello всё равно открывается
RichiBooch Уровень 35
10 мая 2023
Долго мучался и всё-таки решил проблему с 404-й ошибкой. Кажется мне, что с 5-м пунктом"Application context" не всё так просто. Я у себя просто его пропустил и всё заработало. Помогла мне вот эта статья https://javarush.com/groups/posts/305-sozdanie-prosteyshego-web-proekta-v-intellij-idea-enterprise-edition-poshagovo-s-kartinkami
Anonymous #3179849 Уровень 35
23 апреля 2023
В общем, я сделал все, что в комментариях, но не помогало. Единственное, что помогло - "Дорогие мои друзья если ошибка при 8080 на прямую пишет 405 или по путям ошибка 404 , не тратьте время как я - смотря индуских гуру.... просто в intelij в едит конфиГУРАТИОН ставим порт 8081 и сохраняем после чего запуска и хвала алаху по этому пути у вас будет ваша надпись гребаная... http://localhost:8081/hello" У меня это было связано с тем, что я через терминал на MacOS уже открыл мой Томкэт на порту 8080 через startup.sh. Как только закрыл его через shutdown.sh - заработало на родном 8080 порту. Кстати, если вы уже наплодили много разных портов, вот команда по уничтожению через терминал: lsof -n -i4TCP:8080 Где вместо 8080 пишите все порты, которые вы случайно создали.
Dima Makarov Уровень 42
13 марта 2023
Ребята, подкиньте плиз какую -нибудь ссылку на видео/текст вводный для чайников, потому как из статьи я понял только то, что это все очень интересно и полезно и больше ничего
GoldenAlf Уровень 28
7 февраля 2023
Windows 10. TomCat 11 Была ошибка 404 The requested resource [/] is not available Поменял в pom.xml javax на jakarta по советам ниже ... тоже не помогло. Решение: Я закинул свой класс Сервлета в папку Java ! НО ! Я не поставил метку на папку "Sources root" После выставление метки всё заработало. Ниже скрин настройки и на всякий случай напишу слова (Кликаем правой кнопкой мыши на нашу папку, где лежит класс сервлет, далее выбираем mark directory as - sources root ) и всё заработало, теперь сборщик знает в какой папке искать наши сервлеты. Ниже в коментах на всякий скину свой pom и код классов