Когда начинающий разработчик сталкивается с сервлетами, ему бывает очень сложно понять, как он работает и от чего зависит эта работа. Всё потому, что все примеры и видеоуроки рассчитаны на людей, понимающих природу сервлетов и что за чем следует. Поэтому я решил написать руководство по созданию самых простых сервлетов. Возможно, эта статья кому-нибудь поможет.
Итак.
Предположим, что Вы уже где-то скачали пример с применением 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)
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-приложения.alek_sys
19.07.2017 00:20P.S. Если все таки Spring — то есть супер вещь Spring Initializr, он может сгенерить (если выбрать Web зависимость) готовый и настроенный Maven / Gradle Спринг проект, в котором все сразу будет работать и который можно будет запустить из Idea или командной строки.
javawotan
19.07.2017 09:59Вот бы кто про Spring написал и рассказал, что там к чему.
grobokop
19.07.2017 11:23кто бы написал, как это всё на живую выкатывать.
xpendence
19.07.2017 11:24Вы о чём?
grobokop
19.07.2017 11:30да вот как раз разбираюсь в спринг. Я сумел поставить томкат, написал хелло ворлд первый. А теперь есть желание узнать как это всё ставится на живой сервер. Особенно где и как, в конфигурации убирается localhost:8080/имя_WAR/test имя_WAR. чтобы было не test.com/имя_WAR/test, а test.com/test
alek_sys
19.07.2017 12:11Зависит от проекта. Если это Spring без извращений — то это Spring MVC и там в пути war не будет, если задеплоить проект как root.war / root.jar. Если проект новый и там Spring Boot — то ничего деплоить не нужно, просто запустить полученный jar через java -jar . Boot предоставляет настенный и готовый к запуску embedded Tomcat.
Scott_Maison
19.07.2017 12:13В tomcat'e это сродни магии. На сколько помню, один из вариантов переименовать имя_WAR.war в ROOT.war.
Проще поставить nginx перед tomcat'ом и настроить в нем проксирование запросов на localhost:8080/имя_WAR/javawotan
19.07.2017 14:46А вот, кстати, Apache Server не подойдет вместо nginx? Я промудохался 2 дня, ничего особо не вышло, с этими бесконечными файлами конфигураций.
Scott_Maison
19.07.2017 15:23Давно не работал с apache, но вроде как mod_proxy должен подойти.
По поводу nginx: для такой задачи конфиг у него будет не очень большой. На хабре есть статья на эту тему.
AstarothAst
19.07.2017 19:08В свое время я начал с того, что выкинул Томкат и взял embedded jetty — жизнь упростилась в разы, чего и вам желаю :)
zirix
19.07.2017 17:45Вот https://habrahabr.ru/post/262323/
Пробежался по тексту, вроде адекватный/современный мануал.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/javawotan
19.07.2017 18:33Спасибо, годные посты. Но все же хочется найти обзорную лекцию про спринг, он все таки огромный и что там за что отвечает разобраться трудно.
alek_sys
20.07.2017 01:29По мотивам этой статьи написал мой взгляд на то, как начать писать на Spring
lpre
19.07.2017 12:24Если уж есть желание работать с сервлетами на Java, то советую изучить аннотации из последних спецификаций. Конфигурация и деплоймент значительно упростятся (например, не нужно вручную мапить сервлеты в web.xml).
darkslave
19.07.2017 14:49Вброшу, так сказать, свои пару слов:
1. Начиная с servlet-api 3.0, файл web.xml стал необязательным и указание сервлетов, фильтров, слушателей осуществляется соответствующими аннотациями — WebServlet, WebFilter, WebListener и т.д.
2. Если нужно определить синонимы урлов страниц (aka чистые ссылки), то это стоит выделить в отдельный сервлет или вовсе переложить обязанность на фронтенд веб-сервер (например, nginx).
3. По поводу выкладки на сервер. На продуктивной среде автодеплой war-ников в целях безопасности обычно выключают, в server.xml прописываем свои приложения, а выкладку делаем руками или шелл-скриптами. Для тестовой (часто обновляемой) среды можно использовать CI систему.
anmipo
19.07.2017 21:30В интересное время живём: статья про «Создание сервлетов для чайников» вполне может оказаться не для начинающих, а про интернет вещей.
izzholtik
На этой неделе ещё не было!