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
Комментарии (87)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
AhanSere
Уровень 25
3 апреля, 12:38
Если у вас бесплатная Intellij IDEA Community Edition, как у многих наверное, можно в ней подключить плагин (File-Settings-Plugins) Smart Tomcat и будет так же все работать
Alexander Karpeev CEO в Apple
28 января, 23:10
У меня вместо открытия страницы скачивается файл с названием Hello и содержимым hello. Так и должно быть?
Maxim B
Уровень 42
11 декабря 2023, 18:01
Приведенный пример для 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
Andrew_Samedi
Уровень 32
19 марта, 18:12
Блин, спасибо тебе огромное, я все перерыл, но про зависимости даже и не думал, сидел голову ломал почему 404 выдает, эти индусы в ютубе уже снятся))
Anonymous #2212601
Уровень 39
30 сентября 2023, 10:05
Заработало после: 1. Смены 8080 на 8081 в двух местах в окне настройки конфигурации. 2. Игнорирования пункта "5" - с ним не работает (не нужно удалять лишний текст). 3. Смены папки на ресурсную по совету пользователя "GoldenAlf" на несколько комментариев ниже.
Kurama
Уровень 50
19 мая 2023, 15:30
Теперь при запуске приложения должен открыться браузер и выдать 404-ю ошибку. Это логично, ведь по адресу http://localhost:8080/ должен находиться сервлет с мапингом “/”, а у нашего единственного сервлета мапинг "/hello". Обращаемся к нему по адресу http://localhost:8080/hello, и получаем ожидаемый ответ — строку “Hello”! Ага, только вот по адресу http://localhost:8080/hello всё равно открывается
RichiBooch
Уровень 35
10 мая 2023, 10:01
Долго мучался и всё-таки решил проблему с 404-й ошибкой. Кажется мне, что с 5-м пунктом"Application context" не всё так просто. Я у себя просто его пропустил и всё заработало. Помогла мне вот эта статья https://javarush.com/groups/posts/305-sozdanie-prosteyshego-web-proekta-v-intellij-idea-enterprise-edition-poshagovo-s-kartinkami
Alexei
Уровень 42
5 июня 2023, 17:23
Та же самая история. вес день мучался. что только не перепробовал. работает, только если не менять пункт про "Application context".
Anonymous #3179849
Уровень 35
23 апреля 2023, 12:08
В общем, я сделал все, что в комментариях, но не помогало. Единственное, что помогло - "Дорогие мои друзья если ошибка при 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, 08:46
Ребята, подкиньте плиз какую -нибудь ссылку на видео/текст вводный для чайников, потому как из статьи я понял только то, что это все очень интересно и полезно и больше ничего
GoldenAlf
Уровень 28
7 февраля 2023, 18:18
Windows 10. TomCat 11 Была ошибка 404 The requested resource [/] is not available Поменял в pom.xml javax на jakarta по советам ниже ... тоже не помогло. Решение: Я закинул свой класс Сервлета в папку Java ! НО ! Я не поставил метку на папку "Sources root" После выставление метки всё заработало. Ниже скрин настройки и на всякий случай напишу слова (Кликаем правой кнопкой мыши на нашу папку, где лежит класс сервлет, далее выбираем mark directory as - sources root ) и всё заработало, теперь сборщик знает в какой папке искать наши сервлеты. Ниже в коментах на всякий скину свой pom и код классов
GoldenAlf
Уровень 28
7 февраля 2023, 18:19
pom.xml Tomcat 11
<?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>DellMyPlees</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

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

    <dependencies>
        <!-- https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api -->
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>6.0.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>
GoldenAlf
Уровень 28
7 февраля 2023, 18:20
Класс MainServlet
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 jakarta.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");
        visitCounter = visitCounter == null ? 1 : visitCounter + 1;
        session.setAttribute("visitCounter", visitCounter);
        String username = req.getParameter("username");
        resp.setContentType("text/html");
        PrintWriter printWriter = resp.getWriter();
        if (username == null) {
            printWriter.write("Hello, Anonymous" + "");
        } else {
            printWriter.write("Hello, " + username + "");
        }
        printWriter.write("Page was visited " + visitCounter + " times.");
        printWriter.close();
    }
}
GoldenAlf
Уровень 28
7 февраля 2023, 18:21
Перенаправляем сервлет
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;

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

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        getServletContext().getRequestDispatcher("/hello").forward(req, resp);
    }
}
рост
Уровень 32
3 февраля 2023, 00:33
а что собственно я могу показать друзьям если страница открывается только на моем компьютере... или я опять чтото не так сделал?