image

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

Итак.

Предположим, что Вы уже где-то скачали пример с применением maven и Вам удалось задеплоить Ваш код на Tomcat (с этого обычно начинается познание сервлетов) любым способом (WAR-архивом или прямо из среды разработки). Вы имеете структуру приложения, в которой присутствует файл web.xml. C него и надо начинать создание страниц.

Первое и самое важное: машина не видит прямой связи между куском адресной строки и одноимённой страницей в Вашем проекте. localhost:8080/имя_WAR/test и test.jsp — не одно и то же. /test — это «url-метка» сервлета. По которой машина находит нужный Java-файл и тот уже указывает на test.jsp.

Путь от чтения кода машиной и до отображения страницы в браузере выглядит так:

webapp/WEB-INF/web.xml -> servlet
---> ru.user.project/web/ClassName -> request
---> page.jsp

Да, пока ничего не понятно, но мы ещё вернёмся к этой схеме. Если описать её простыми человеческими словами, то это будет выглядеть так:

Из файла web.xml через сервлет машина получает путь к Java-классу, который, в свою очередь, направляет машину на искомую страницу.

Это было лирическое отступление, переходим к коду.

Итак, мы имеем задеплоенный на Tomcat проект, главная страница которого открывается по вызову localhost:8080/имя_WAR (если мы деплоили WAR-файл).

Открываем web.xml. Этот файл сканируется Tomcat'ом в первую очередь. Здесь мы и зададим начало пути. Вот код нашего web.xml:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
		 http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <display-name>Test</display-name>

    <servlet>
        <servlet-name>testServlet</servlet-name>
        <servlet-class>ru.user.project.web.TestServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>testServlet</servlet-name>
        <url-pattern>/test</url-pattern>
    </servlet-mapping>

</web-app>

Сервлет связывает ссылку из адресной строки и Java-класс. Java-класс, в нашем случае, открывает JSP-страницу. Сервлет состоит из 2 составляющих:

<servlet>    //здесь прописан путь к Java-классу
        <servlet-name>testServlet</servlet-name>
        <servlet-class>ru.user.project.web.TestServlet</servlet-class>
    </servlet>
    <servlet-mapping>    //здесь прописан путь к куску адресной строки, вызывающей сервлет
        <servlet-name>testServlet</servlet-name>
        <url-pattern>/test</url-pattern>
    </servlet-mapping>

Прописываем <servlet-class>. По этому пути хранится Java-класс, который обработается при вызове адресной строки. Потом дописываем <servlet-mapping>. Это кусок адреса из адресной строки, привязанный к сервлету. Когда мы допишем к нашей первоначальной строке /test, начнётся магия. Но пока мы ещё не дописали остальную часть кода. Пишем Java-файл. Он у нас находится по адресу ru.user.project.web (для этого нужно создать папку web, если её нет).

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

/**
 * Created by promoscow on 17.07.17.
 */
public class TestServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.getRequestDispatcher("/test.jsp").forward(request, response);
    }
}

Класс надо унаследовать от HttpServlet и переопределить метод doGet(); В переопределённом методе мы пишем название строки, на которую будет осуществлён переход (в нашем случае, это "/test.jsp".

Таким образом, при вызове адреса localhost:8080/имя_WAR/test Tomcat находит нужный <url-pattern>, выясняет, что он принадлежит testServlet, далее видит, что этому testServlet принадлежит Java-файл TestSevlet, исполняет его, а в Java-файле идёт переход на test.jsp. Осталось написать test.jsp:

<%--
  Created by IntelliJ IDEA.
  User: promoscow
  Date: 17.07.17
  Time: 23:22
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Test</title>
</head>
<body>
Hey there! It's test servlet page!
</body>
</html>

Теперь, когда пользователь допишет /test к изначальному адресу, выполнится алгоритм, описанный в начале статьи (помните, я обещал к ней вернуться?) и браузер покажет содержимое файла test.jsp. Также, можно, например, написать в стартовом файле (например, index.html) ссылку:

<a href="test">Test page</a>

И произойдёт вышеописанная цепь событий, которая вызовет, в итоге, страницу test.jsp.

Надеюсь, эта статья поможет барахтающимся в поисках здравого смысла начинающим разработчикам написать первый сервлет, а уже в дальнейшем к этому пониманию постепенно будет присоединяться всё остальное (как это обычно бывает).
Поделиться с друзьями
-->

Комментарии (24)


  1. izzholtik
    18.07.2017 18:32
    +1

    На этой неделе ещё не было!


  1. FSA
    18.07.2017 22:00
    +1

    Больше похоже на небольшой рандомный кусок из какого-то руководства, чем на полезную статью.


    1. Lungo
      20.07.2017 10:33

      При чем руководство это, судя по всему, немножно устаревшее


  1. sshikov
    18.07.2017 23:12

    А ничего что jsp это тоже сервлет?


  1. alek_sys
    19.07.2017 00:15
    +3

    Дорогие начинающие Java-разработчики (и автор?), пользуясь случаем очень хочется сказать пару вещей. Во первых, не обращайте внимания на комментарии, некоторые люди легко забывают что сами когда-то продирались через дебри мутной и пугающей документации для сервлет контейнеров, чтобы понять что вообще там к чему. Во-вторых, не думайте сильно сервлетами и прочими низкоуровневыми абстракциями.

    Первая реакция, которая возникает, когда пытаешься задеплоить Hello World в сервлет контейнер, даже следуя такому простому и понятному руководству, как в этой статье — это ужас. Особенно после простых, как бревно, Node.js, PHP, Python. И наверное возникает мысль «Зачем люди так делают?!». Так вот, в целом — не делают. Сейчас на уровне сервлетов пишут и мыслят только разработчики фреймворков, для обычных разработчиков знать и понимать их полезно, но возможно не в самую первую очередь.

    Так вот, если вы смотрите на то, чтобы начать web-разработку на Java в 2017, я бы порекомендовал следующее:
    1. Особо не погружайтесь в дебри JSP или сервлетов. Берите простой и понятный веб-фреймворк, типа Spark, Ninja, Jooby — там все очень удобно и понятно. Плюс современные и приличные движки для шаблонов, а не (кхм) JSP.

    Вот пример на Spark:

    import static spark.Spark.*;
    
    public class HelloWorld {
        public static void main(String[] args) {
            get("/hello", (req, res) -> "Hello World");
        }
    }
    


    2. Не разбирайтесь (пока) в тонкостях сервлет-контейнеров и деплоя WAR, любой фреймворк даст embedded сервер, который запустится в командной строке

    3. Даже не смотрите (пока) на сервера приложений и Java EE, там сразу слишком много новых концепций. Если хочется современного энтерпрайза — можно посмотреть на Spring, начиная с малого. Вопреки популярному заблуждению, он простой, легкий, и удобный для новичков — если брать Spring Boot.

    Современная Java это все-таки не совсем ужас-ужас, вот эти все килобайты XML и JSP сейчас, к счастью, не нужны чтобы начать писать web-приложения.


    1. alek_sys
      19.07.2017 00:20

      P.S. Если все таки Spring — то есть супер вещь Spring Initializr, он может сгенерить (если выбрать Web зависимость) готовый и настроенный Maven / Gradle Спринг проект, в котором все сразу будет работать и который можно будет запустить из Idea или командной строки.


    1. xpendence
      19.07.2017 11:24

      Огромное спасибо за наводку.


  1. Scott_Maison
    19.07.2017 09:59

    Эм… Так ведь 2017 год на дворе.
    Уж извините, не смог удержаться.


  1. javawotan
    19.07.2017 09:59

    Вот бы кто про Spring написал и рассказал, что там к чему.


    1. grobokop
      19.07.2017 11:23

      кто бы написал, как это всё на живую выкатывать.


      1. xpendence
        19.07.2017 11:24

        Вы о чём?


        1. grobokop
          19.07.2017 11:30

          да вот как раз разбираюсь в спринг. Я сумел поставить томкат, написал хелло ворлд первый. А теперь есть желание узнать как это всё ставится на живой сервер. Особенно где и как, в конфигурации убирается localhost:8080/имя_WAR/test имя_WAR. чтобы было не test.com/имя_WAR/test, а test.com/test


          1. alek_sys
            19.07.2017 12:11

            Зависит от проекта. Если это Spring без извращений — то это Spring MVC и там в пути war не будет, если задеплоить проект как root.war / root.jar. Если проект новый и там Spring Boot — то ничего деплоить не нужно, просто запустить полученный jar через java -jar . Boot предоставляет настенный и готовый к запуску embedded Tomcat.


          1. Scott_Maison
            19.07.2017 12:13

            В tomcat'e это сродни магии. На сколько помню, один из вариантов переименовать имя_WAR.war в ROOT.war.
            Проще поставить nginx перед tomcat'ом и настроить в нем проксирование запросов на localhost:8080/имя_WAR/


            1. javawotan
              19.07.2017 14:46

              А вот, кстати, Apache Server не подойдет вместо nginx? Я промудохался 2 дня, ничего особо не вышло, с этими бесконечными файлами конфигураций.


              1. Scott_Maison
                19.07.2017 15:23

                Давно не работал с apache, но вроде как mod_proxy должен подойти.
                По поводу nginx: для такой задачи конфиг у него будет не очень большой. На хабре есть статья на эту тему.


          1. AstarothAst
            19.07.2017 19:08

            В свое время я начал с того, что выкинул Томкат и взял embedded jetty — жизнь упростилась в разы, чего и вам желаю :)


    1. zirix
      19.07.2017 17:45

      Вот https://habrahabr.ru/post/262323/
      Пробежался по тексту, вроде адекватный/современный мануал.


      1. zirix
        19.07.2017 17:52

        Пример hello world и демо проекты (справа ссылки) https://projects.spring.io/spring-boot/
        И сами доки на спринг бут: https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/


        1. javawotan
          19.07.2017 18:33

          Спасибо, годные посты. Но все же хочется найти обзорную лекцию про спринг, он все таки огромный и что там за что отвечает разобраться трудно.


          1. alek_sys
            20.07.2017 01:29

            По мотивам этой статьи написал мой взгляд на то, как начать писать на Spring


  1. lpre
    19.07.2017 12:24

    Если уж есть желание работать с сервлетами на Java, то советую изучить аннотации из последних спецификаций. Конфигурация и деплоймент значительно упростятся (например, не нужно вручную мапить сервлеты в web.xml).


  1. darkslave
    19.07.2017 14:49

    Вброшу, так сказать, свои пару слов:
    1. Начиная с servlet-api 3.0, файл web.xml стал необязательным и указание сервлетов, фильтров, слушателей осуществляется соответствующими аннотациями — WebServlet, WebFilter, WebListener и т.д.
    2. Если нужно определить синонимы урлов страниц (aka чистые ссылки), то это стоит выделить в отдельный сервлет или вовсе переложить обязанность на фронтенд веб-сервер (например, nginx).
    3. По поводу выкладки на сервер. На продуктивной среде автодеплой war-ников в целях безопасности обычно выключают, в server.xml прописываем свои приложения, а выкладку делаем руками или шелл-скриптами. Для тестовой (часто обновляемой) среды можно использовать CI систему.


  1. anmipo
    19.07.2017 21:30

    В интересное время живём: статья про «Создание сервлетов для чайников» вполне может оказаться не для начинающих, а про интернет вещей.