Одной из самых приятных особенностей Java является ее богатая, многогранная природа. Конечно, создание традиционных настольных и даже мобильных приложений — это хорошо. Но что, если вы хотите оставить свой текущий фон позади и начать наступать на веб-ландшафт? Хорошая новость заключается в том, что язык поставляется с полноценным Servlet API, который позволяет создавать надежные веб-приложения без особых хлопот.
Вопрос, который должен звучать у вас в голове сейчас: «Как?», Я прав?
Быстрое и грязное введение в сервлеты Java
Введите сервлеты , определенный тип Java-программы, выполняемой в рамках веб-контейнера (также называемые сервлет-контейнерами , Tomcat и Jetty являются основными примерами), которая позволяет обрабатывать запросы клиентов и ответы сервера простым и эффективным способом. Это не место и не время, чтобы скучать вам до академических определений о том, что такое сервлет. Достаточно сказать, что сервлеты создаются и уничтожаются своими серверами, а не разработчиком, и действуют как промежуточный уровень между клиентами (обычно веб-браузерами) и другими приложениями, работающими на сервере (например, базами данных).
Сервлеты являются мощными существами, которые, помимо прочего, могут получать данные от клиента, как правило, с помощью запросов GET и POST, обрабатывать файлы cookie и параметры сеанса, обрабатывать данные с помощью дополнительных прикладных уровней и отправлять выходные данные клиенту в обоих случаях. текстовые и двоичные форматы (HTML, XML, PDF, JPG, GIF и т. д.), во многих случаях с использованием файла Java Server Pages (JSP).
Лучший способ начать использовать сервлеты — на конкретном примере. Таким образом, в следующих нескольких строках я собираюсь создать простое веб-приложение, которое позволит клиентам регистрироваться с помощью простой HTML-формы. Данные, представленные клиентами, будут собираться сервлетом и проверяться на базовом уровне с помощью статических помощников.
Использование модели клиент-сервер HTTP
Чтобы иметь четкое представление о том, как будет структурировано клиентское приложение, вот как это будет сделано:
Ничего неожиданного, правда? Но обо всем по порядку.
Первый шаг, который мы должны сделать для создания приложения, — это определить так называемый дескриптор развертывания (DD). Как следует из названия, оно определяет, как приложение должно быть развернуто в конкретной среде. Неудивительно, что когда речь идет о веб-приложениях, дескриптор развертывания представляет собой простой XML-файл с именем web.xml
и является частью стандартной спецификации Java EE. В данном конкретном случае это будет выглядеть так:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.1" 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" metadata-complete="false"> </web-app>
Как показано выше, web.xml
вообще не содержит директив и определяет только версию Спецификации сервлета Java (3.1), которую мы будем использовать в приложении. Конечно, он может содержать гораздо больше контента, включая директивы отображения сервлетов , параметры инициализации , список файлов приветствия и несколько дополнительных настроек. Но, чтобы весь процесс разработки был простым, я сохраню файл таким простым.
Поскольку образец приложения использует API сервлетов, а также использует страницы сервера Java для отображения данных о клиентах, нам необходимо получить все необходимые JAR-файлы. Maven сделает за нас тяжелую работу, поэтому вот как будет определен файл POM:
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.electricalweb.customerapplication</groupId> <artifactId>customerapplication</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>Customer Application</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>javax.el</groupId> <artifactId>el-api</artifactId> <version>2.2</version> </dependency> </dependencies> <build> <finalName>Customer Application</finalName> </build> </project>
В двух словах, файл pom.xml
определяет все зависимости, необходимые для работы с сервлетами: JSP, Стандартная библиотека тегов Java (JSTL) и Язык выражений Java (JEL).
На данный момент нам удалось создать файлы конфигурации приложения клиента. Однако в своем текущем состоянии приложение буквально ничего не делает. Хлоп! Поскольку основная цель здесь — разрешить клиентам регистрироваться с использованием формы HTML, следующее, что нам нужно сделать, — это определить файлы JSP, которым поручено отображать вышеупомянутую форму и отображать данные клиента после успешного завершения процесса регистрации. Это именно та тема, которая рассматривается в следующем разделе.
Создание регистрации и приветствия
Уровень представления будет состоять из двух файлов JSP — в контексте MVC они называются представлениями . Первый будет отвечать за отображение формы регистрации и отображение возможных ошибок, вызванных после проверки введенных данных. Второй будет типичная страница welcome
, на которой будут показаны данные, предоставленные клиентом после успешного завершения процесса регистрации.
Вот первый файл JSP:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Customer Sign Up</title> </head> <body> <h1>Customer Sign Up</h1> <c:if test="${violations != null}"> <c:forEach items="${violations}" var="violation"> <p>${violation}.</p> </c:forEach> </c:if> <form action="${pageContext.request.contextPath}/processcustomer" method="post"> <label for="firstname">First Name : </label> <input type="text" name="firstname" id="firstname" value="${firstname}"> <label for="lastname">Last Name:</label> <input type="text" name="lastname" id="lastname" value="${lastname}"> <label for="email">Email: </label> <input type="text" name="email" id="email" value="${email}"> <input type="submit" name="signup" value="Sign Up"> </form> </body> </html>
Файл содержит простой HTML с несколькими настройками. В этом прелесть технологии JSP в сочетании с JSTL и JEL. Обратите внимание, как легко проверить нарушения валидации и зациклить их в верхней части файла, используя стандартные теги, такие как <c:if>
и <c:forEach>
.
Атрибут action
формы регистрации указывает на следующий URL: ${pageContext.request.contextPath}/processcustomer
. Короче говоря, это означает, что каждый раз, когда клиент пытается зарегистрироваться, данные будут отправляться processcustomer
процесса независимо от контекстного пути приложения. Поэтому независимо от того, по какому URL-адресу будет доступна форма регистрации, URL-адрес действия всегда будет соответствовать этому. Это достигается благодаря функциональности объектов, доступных из файла JSP, таких как request
.
Ниже мы увидим, как сервлет отображается на URL processcustomer
процесса и как он контролирует введенные данные. Но прежде чем я определю соответствующий сервлет, вот файл JSP, который отображает страницу welcome
:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Customer Data</title> </head> <body> <h1>Thanks for signing up with us!</h1> <h2>You provided the following data:</h2> <p><strong>First Name:</strong> ${firstname}</p> <p><strong>Last Name:</strong> ${lastname}</p> <p><strong>Email: </strong>${email}</p> </body> </html>
Все идет нормально. После того, как уровень представления уже настроен, следующим шагом является создание сервлета, отвечающего за сбор данных о клиентах из запросов POST, и проверку данных в основном виде.
Построение контроллера клиента
Поверьте, определение сервлета, способного захватывать данные, введенные в предыдущей форме регистрации, очень просто. Все, что нам нужно сделать, это создать подкласс собственного класса HttpServlet
и реализовать его методы doGet()
или doPost()
(или оба при необходимости). В этом конкретном случае сервлет клиента будет перехватывать данные, поступающие из запросов POST.
Его определение таково:
@WebServlet(name = "CustomerController", urlPatterns = "/processcustomer") public class CustomerController extends HttpServlet { @Override protected void doPost( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { RequestCustomer customer = RequestCustomer.fromRequestParameters(request); customer.setAsRequestAttributes(request); List<String> violations = customer.validate(); if (!violations.isEmpty()) { request.setAttribute("violations", violations); } String url = determineUrl(violations); request.getRequestDispatcher(url).forward(request, response); } private String determineUrl(List<String> violations) { if (!violations.isEmpty()) { return "/"; } else { return "/WEB-INF/views/customerinfo.jsp"; } } private static class RequestCustomer { private final String firstName; private final String lastName; private final String email; private RequestCustomer(String firstName, String lastName, String email) { this.firstName = firstName; this.lastName = lastName; this.email = email; } public static RequestCustomer fromRequestParameters( HttpServletRequest request) { return new RequestCustomer( request.getParameter("firstname"), request.getParameter("lastname"), request.getParameter("email")); } public void setAsRequestAttributes(HttpServletRequest request) { request.setAttribute("firstname", firstName); request.setAttribute("lastname", lastName); request.setAttribute("email", email); } public List<String> validate() { List<String> violations = new ArrayList<>(); if (!StringValidator.validate(firstName)) { violations.add("First Name is mandatory"); } if (!StringValidator.validate(lastName)) { violations.add("Last Name is mandatory"); } if (!EmailValidator.validate(email)) { violations.add("Email must be a well-formed address"); } return violations; } } }
Первое, на что следует обратить внимание, это использование @WebServlet(name = "CustomerController", urlPatterns = "/processcustomer")
. Он указывает контейнеру сервлета использовать класс CustomerController
для обработки HTTP-запросов к URL-адресу /processcustomer
. Можно добиться того же поведения, включив директивы отображения сервлета в web.xml
, как показано здесь , но, поскольку я использую спецификацию 3.1 сервлета, нет необходимости прибегать к директивам отображения в дескрипторе развертывания. Просто простые аннотации уровня класса обрабатывают процесс отображения для нас.
В этом случае я назвал сервлет CustomerController
как цепляюсь за базовую схему Model — View — Controller, поэтому сервлет ведет себя как обычный контроллер действий. Тем не менее, можно назвать это как-нибудь еще, если соглашение об именах является единым для всего приложения. Как правило, рекомендуется использовать имя класса сервлета в качестве значения для @WebServlet
аннотации @WebServlet
. В противном случае некоторые контейнеры сервлетов не смогут выполнить сопоставление и выдают уродливую ошибку HTTP status 404, сообщающую, что запрошенный ресурс (сам сервлет) недоступен.
Тем не менее, сам класс CustomerController
выполняет несколько простых задач. Прежде всего, он собирает данные, введенные в форму регистрации, с помощью реализации собственного интерфейса HttpServletRequest
, который содержит значения, соответствующие полям имени, lastname
и email
формы регистрации. Во-вторых, он устанавливает эти значения в качестве атрибутов запроса, чтобы их можно было повторно отображать либо в представлении формы регистрации, либо в представлении результатов. Наконец, данные проверяются с помощью нескольких статических помощников, которые проверяют наличие пустых строк и правильных адресов электронной почты.
Сами валидаторы являются простыми классами, проверяющими пару желаемых свойств, например, является ли имя непустым и адрес электронной почты выглядит, ну, в общем, как адрес электронной почты. Если вы хотите узнать, как они реализованы, вы можете посмотреть их здесь .
Результат процесса проверки в основном контролирует поток приложения: если данные недействительны, клиент перенаправляется через объект диспетчера запросов в представление формы регистрации, и ошибки тут же отображаются. В противном случае страница welcome
отображается, так как клиент успешно зарегистрировался.
На данный момент мы создали полнофункциональное веб-приложение, которое позволяет клиентам регистрироваться с помощью формы HTML, базового сервлета и пары файлов JSP. Возможно, самой большой ошибкой здесь является использование статических помощников для проверки данных клиента вместо обращения к стандартному подходу, такому как проверка объектов домена и даже целых графов объектов с использованием Java Bean Validation . Детали того, как это сделать, останутся темой следующей статьи.
Запуск приложения
Если вы чем-то похожи на меня, вы должны стремиться запустить приложение на своей собственной платформе разработки, и вы сами сможете убедиться, что оно работает так же приятно, как я и обещал вам за чистую монету. Для этого просто следуйте инструкциям ниже:
-
В качестве предварительных условий вам понадобятся Git (обязательно выберите версию, которая работает с вашей операционной системой), Maven и контейнер сервлета (среди прочего, Apache Tomcat , Jetty или JBoss Wildfly ). Возможно, ваша IDE поставляется с одной или несколькими из этих систем, и в этом случае вы можете использовать встроенные версии.
-
Используйте Git, чтобы клонировать репозиторий приложений на локальный компьютер и импортировать его в IDE, предпочтительно в виде проекта Maven.
-
Разверните весь проект в контейнер сервлета и запустите его. Развертывание проекта означает создание файла WAR (веб-архива) или разнесенного WAR (также известного как развернутое развертывание ) и размещение его в папке развертывания контейнера сервлета по умолчанию. Во многих случаях ваша IDE будет развертывать проект автоматически для вас, когда вы нажимаете кнопку « Выполнить» , так что не стоит портить себе жизнь, пытаясь выполнить эту задачу вручную, если вам не нужно. Подробности смотрите в документации вашей IDE (здесь это для IntelliJ IDEA . Как только проект будет правильно развернут, и если все пойдет правильно, откроется браузер по умолчанию, и вы увидите форму регистрации клиента.
-
Попробуйте отправить форму без каких-либо значений или даже с некоторыми отсутствующими значениями (здесь достаточно места для экспериментов). Вы увидите ошибки, отображаемые в верхней части формы в соответствии с ошибочными данными. Наконец, если вы любезны и предоставите обязательные данные, вы увидите страницу приветствия.
Не стесняйтесь погладить себя по спине. Поздравляем! Вы разработали полнофункциональное веб-приложение на Java.
Последние мысли
К этому моменту вы приобрели все навыки, необходимые для создания собственного веб-приложения на Java, и, что самое главное, вообще не прибегая к сложностям какой-либо среды. Все, что вам нужно, это использовать API Servlet , использовать какую-то технологию рендеринга, такую как JSP и обычная Java. Разве это не здорово?
Стоит отметить, что реализация класса CustomerController
подчеркивает достоинства и недостатки, проявляемые сервлетами: с одной стороны, это показывает, в двух словах, как легко обрабатывать параметры запроса и отправлять ответы обратно клиенту в различных форматах. Но эта функциональность имеет свою цену: обе реализации интерфейсов HttpServletRequest
и HttpServletResponse
представляют собой простые сервисные локаторы . Это по сути не плохо, так как локаторы — это просто держатели данных. К сожалению, всякий раз, когда вы добавляете сервлет в область своего приложения, к нему присоединяются эти реализации.
Если вы хотите попробовать приложение для клиента и поэкспериментировать с ним, просто клонируйте репозиторий из GitLab . Не стесняйтесь оставлять мне свои комментарии. Я постараюсь ответить как можно скорее.