JavaRush /Java Blog /Random-TK /Создание простого веб-приложения на сервлетах и jsp (част...
fatfaggy
Dereje
Киев

Создание простого веб-приложения на сервлетах и jsp (часть 2)

Toparda çap edildi
Продолжаю описывать процесс создания веб-applications используя сервлеты, jsp, мавен и томкат. Начало статьи, если необходимо.
Создаем сущности.
Создадим в пакете entities класс User, в котором создадим две приватные строковые переменные name и password. Создадим конструкторы (по умолчанию и такой, который бы принимал оба значения), геттеры/сеттеры, переопределим метод toString() на всякий случай, а так же методы equals() и hashCode(). public class User { private String name; private String password; public User() { } public User(String name, String password) { this.name = name; this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", password='" + password + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; if (name != null ? !name.equals(user.name) : user.name != null) return false; return password != null ? password.equals(user.password) : user.password == null; } @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + (password != null ? password.hashCode() : 0); return result; } } Теперь можем приступить к созданию списка пользователей, куда мы будем наших пользователей добавлять, и откуда будем их забирать для отображения. Но есть проблема. Объекты наших сервлетов мы не создаем, их создает за нас томкат. Методы, которые мы переопределяем в них - тоже за нас уже определены и добавить параметр мы не можем. Как же тогда создать общий список, который бы виден был в обоих наших сервлетах? Если мы просто в каждом сервлете создадим свой an object списка - то получится, что добавлять пользователей мы будем в один список, а выводить список пользователей сервлетом ListServlet - совершенно другой. Получается, что нам нужен такой an object, который был бы общим для обоих сервлетов. Если говорить обобщенно, то нам нужен такой an object, который был бы общим для всех классов в нашей программе; единственный an object на всю программу. Надеюсь, вы что-то да слышали про шаблоны проектирования. И возможно для кого-то это первая реальная необходимость использования шаблона Singleton в своей программе. Можете поизвращаться и запorть Howой-нибудь крутой синглтон, с двойными проверками и синхронизациями (да-да, у нас многопоточное приложение, так How сервлеты томкат запускает в разных потоках), но я буду использовать вариант с ранней инициализацией, так How в данном случае он вполне подходит.
Creation модели.
Создадим тогда класс (и реализуем в нем шаблон singleton) в пакете model, ну и назовем тоже вполне красочно Model. Создадим в нем приватный an object списка пользователей, и сделаем два метода: один для того, чтоб можно было добавить пользователя, а второй - пусть просто возвращает список строк (имен пользователей). Поскольку наш an object пользователя состоит из имени и пароля - то пароли пользователей мы "светить" не хотели бы, поэтому и возвращать будем только список их имен. public class Model { private static Model instance = new Model(); private List model; public static Model getInstance() { return instance; } private Model() { model = new ArrayList<>(); } public void add(User user) { model.add(user); } public List list() { return model.stream() .map(User::getName) .collect(Collectors.toList()); } }
Немного про mvc.
Раз уж вы слышали про singleton, значит возможно слышали и про другой шаблон проектирования - MVC (model-view-controller, or по-русски модель-представление-контроллер, or прям так How и на английском модель-вью-контроллер). Его суть в том, чтобы отделять бизнес-логику от представления. То-есть, отделять code, который определяет что делать от codeа, который определяет How отображать. View (представление or просто вьюхи) How-раз и отвечает за то, в Howом виде представлять Howие-то данные. В нашем случае вьюхи - это наши jsp странички. Именно поэтому я их и положил в папочку с названием views. Модель - это собственно сами данные, с которыми работает программа. В нашем случае это пользователи (список пользователей). Ну а контроллеры - связующее звено между ними. Берут данные из модели и передают их во вьюхи, ну or получают от томката Howие-то данные, обрабатывают их и передают модели. Бизнес-логика (то-есть что делать) должна быть описана в них, а не в модели or во вьюхе. Таким образом, каждый занимается своим делом:
  • модель хранит данные
  • вьюхи рисуют красивое представление данных
  • контроллеры занимаются обработкой данных
Это позволяет всем им быть достаточно простыми и поддерживаемыми. А не монструозной свалкой всего codeа в одном классе. MVC подходит не только для веб-программирования, но все-же в этой сфере он встречается очень часто (если не всегда). В нашем случае в качестве контроллеров будут выступать сервлеты. Да это очень поверхностное и даже грубое описание этого паттерна, но эта статья не о шаблонах проектирования, а о том, How сделать простенькое веб-приложение :) Кто хочет узнать больше - гугл знает все! :) Вернемся к нашим вьюхам.
Создаем форму добавления пользователя.
Добавим в файл add.jsp форму, состоящую из двух текстовых инпутов (один обычный, другой типа пароль) и кнопки для отправки данных на server.
Здесь у формы указан атрибут method со meaningм post. Это говорит о том, что данные из этой формы полетят на server в виде POST-requestа. Атрибут action не указан, значит request этот полетит по тому же addressу, по которому мы перешли на эту страничку (/add). Таким образом наш сервлет, который привязан к этому addressу, при получении GET-requestа возвращает эту jsp с формой добавления пользователей, а если получит POST-request - значит эта форма отправила туда свои данные (которые мы в методе doPost() вытащим из an object requestа, обработаем и передадим в модель на сохранение). Стоит обратить внимание, что у инпутов здесь указан параметр name (для поля с именем он имеет meaning name, а для поля с паролем - pass). Это довольно важный момент. Так How чтобы получить from request (внутри сервлета уже) эти данные (Name и пароль которые будут введены) - мы будем использовать именно эти name и pass. Но об этом чуть позже. Сама кнопка отправки данных у меня сделана снова же в виде button, а не инпутом, How это обычно принято. Не знаю насколько такой вариант универсален, но у меня в хроме работает :)
Обработка POST-requestа сервлетом.
Вернемся к сервлету AddServlet. Мы уже знаем, что чтобы наш сервлет умел "ловить" GET-requestы - мы переопределor метод doGet() из класса HttpServlet. Whatбы научить наш сервлет ловить еще и POST-звапросы - мы переопределяем еще и метод doPost(). Он получает аналогичные an objectы requestа и ответа от томката, с которыми мы и будем работать. Для начала вытащим from request параметры name и pass, которые отправила форма (если вы их в форме назвали по-другому - тогда именно те названия и пишете). После чего создадим an object нашего пользователя, используя полученные данные. Потом получим an object модели и добавим созданного пользователя в модель. @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("name"); String password = req.getParameter("pass"); User user = new User(name, password); Model model = Model.getInstance(); model.add(user); }
Передача данных во вьюху.
Теперь перейдем к сервлету ListServlet. У нас тут уже реализован метод doGet(), который просто передает управление во вьюху list.jsp. Если у вас этого еще нет - сделайте по аналогии с таким же методом из сервлета AddServlet. Теперь было бы неплохо получить из модели список имен пользователей и передать их во вьюху, которая их там получит и красивенько отобразит. Для этого воспользуемся снова же an objectом requestа, который мы получor от томката. К этому an objectу мы можем добавить атрибут, дав ему Howое-то Name ну и собственно сам an object, который бы мы хотели передать во вьюху. Благодаря тому, что при передаче процесса выполнения из сервлета во вьюху мы передаем туда эти же an objectы requestа и ответа, что получил сам сервлет, то и добавив наш список имен к an objectу requestа мы потом из этого an object requestа во вьюхе сможем наш список имен пользователей и получить. С классом ListServlet мы закончor, поэтому привожу code всего класса package app.servlets; import app.model.Model; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; public class ListServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Model model = Model.getInstance(); List names = model.list(); req.setAttribute("userNames", names); RequestDispatcher requestDispatcher = req.getRequestDispatcher("views/list.jsp"); requestDispatcher.forward(req, resp); } }
Выполнение java-codeа в jsp fileх.
Теперь перейдем к файлу list.jsp. Этот файл выполнится только когда ListServlet передаст сюда процесс выполнения. Кроме того, мы в том сервлете уже подготовor список имен пользователей из модели и передали сюда в an objectе requestа. Имея список имен мы можем пробежаться по нему циклом for и вывести каждое Name. Как я уже говорил, jsp файлы могут выполнять java-codeа (в принципе, этим и отличаются от статичных html страничек). Для того, чтобы выполнить Howой-то code - достаточно поставить в нужном нам месте конструкцию <% // java code %> Внутри такой конструкции мы получаем доступ к нескольким переменным: request - наш an object requestа, который мы передали из сервлета, где он назывался просто req responce - an object ответа, в сервлете назывался resp out - an object типа JspWriter (наследуется от обычного Writer), при помощи которого можем "писать" что-либо прямо в саму html страничку. Запись out.println("Hello world!") очень похожа на запись System.out.println("Hello world!"), но не стоит их путать! out.println() "пишет" в html страничку, а System.out.println - в системный вывод. Если вызвать внутри раздела с джава codeом jsp метод System.out.println() - то результаты увидите в консоли томката, а не на страничке, How возможно хотелось бы :) Про другие доступные an objectы внутри jsp можно поискать тут. Используя an object request мы можем получить список имен, который передавали из сервлета (мы прикрепor соответствующий атрибут к этому an objectу), а используя an object out - можем вывести эти имена. Сделаем это пока просто в виде html списка:
    <% List names = (List ) request.getAttribute("userNames"); if (names != null && !names.isEmpty()) { for (String s : names) { out.println("
  • " + s + "
  • "); } } %>
Если же хотим выводить список только в том случае, когда есть пользователи, а иначе выводить предупреждение, что пользователей пока нет - можем немного переписать этот участок: <% List names = (List ) request.getAttribute("userNames"); if (names != null && !names.isEmpty()) { out.println(" "); for (String s : names) { out.println("
  • " + s + "
  • "); } out.println("
    "); } else out.println("

    There are no users yet!

    "); %>
    Теперь, когда мы умеем передавать данные из сервлетов во вьюхи - можем немного улучшить наш AddServlet, чтобы выводилось уведомление об успешном добавлении пользователя. Для этого в методе doPost() после того, How добавor нового пользователя в модель - можем добавить Name этого пользователя в атрибуты an object req и передать управление обратно во вьюху add.jsp. А в ней уже сделать участок с джава codeом, где проверять есть ли такой атрибут в requestе, и если да - то выводить сообщение, что пользователь успешно добавлен. После этих изменений полный code сервлета AddServlet будет выглядеть примерно так: package app.servlets; import app.entities.User; import app.model.Model; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class AddServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { RequestDispatcher requestDispatcher = req.getRequestDispatcher("views/add.jsp"); requestDispatcher.forward(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("name"); String password = req.getParameter("pass"); User user = new User(name, password); Model model = Model.getInstance(); model.add(user); req.setAttribute("userName", name); doGet(req, resp); } } Тут в конце метода doPost() мы устанавливаем атрибут с именем добавленного в модель пользователя, после чего вызываем метод doGet(), в который передаем текущие request и ответ. А метод doGet() уже передает управление во вьюху, куда и отправляет an object requestа с прикрепленным именем добавленного пользователя в качестве атрибута. Осталось подправить саму add.jsp чтобы она выводила такое уведомление, если присутствует такой атрибут. Конечный вариант add.jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> Add new user

    Super app!

    <% if (request.getAttribute("userName") != null) { out.println("

    User '" + request.getAttribute("userName") + "' added!

    "); } %>

    Add user

    Тело pages состоит из div-a с шапкой, после чего div-контейнер для контента, в нем проверка существует ли атрибут с именем пользователя, потом div с формой добавления пользователей, ну и в конце футер с кнопкой возврата на главную page. Может показаться, что слишком много div-ов, но мы их потом используем, когда добавим стилей :) Ну и конечный вариант list.jsp <%@ page import="java.util.List" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> Users

    Super app!

    Users

    <% List names = (List ) request.getAttribute("userNames"); if (names != null && !names.isEmpty()) { out.println(" "); for (String s : names) { out.println("
  • " + s + "
  • "); } out.println("
    "); } else out.println("

    There are no users yet!

    "); %>
    Таким образом, мы имеем fully рабочее веб приложение, которое умеет хранить и добавлять пользователей, а так же выводить список их имен. Осталось лишь приукрасить... :)
    Добавление стилей. We use фреймворк W3.CSS.
    В данный момент наше приложение рабочее, но абсолютно вырвиглазное :) Нужно добавить фон, цвета текста и кнопок, стorзировать списки, сделать выравнивание, добавить отступов, в общем много чего. Если писать стor вручную - это может занять много времени и нервов. Поэтому я предлагаю воспользоваться CSS фреймворком W3.CSS. В нем уже есть готовые классы со стилями, осталось только расставить в нужных местах те css классы, которые мы хотим в этих местах применить. Для того, чтобы добавить их к себе на pages во-первых нам надо подключить файл со стилями. Это можно сделать двумя способами: 1. пройтись по нашим pageм и в разделе head вставить прямую ссылку на файл со стилями Такой вариант подходит, если у вас постоянное подключение к интернету. Тогда при открытии ваших страниц на локальном serverе - стor подтянутся из интернета. 2. Если же вы хотите иметь все стor у себя локально и не быть зависимым от интернет-соединения - можете просто скачать файл со стилями и поместить его где-нибудь внутри папочки web (например web/styles/w3.css), после чего пройтись по всем нашим страничкам (index.html, add.jsp, list.jsp) и вписать внутри раздела head ссылку на этот файл со стилями После этого просто пройтись по тегам и подописывать им те стor, которые вам понравятся. Я не буду останавливаться на этом подробно, а сразу дам свои готовые варианты трех моих файлов с раставленными классами стилей. index.html Super app!

    Super app!

    add.jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> Add new user

    Super app!

    <% if (request.getAttribute("userName") != null) { out.println("
    \n" + " ×\n" + "
    User '" + request.getAttribute("userName") + "' added!
    \n" + "
    "); } %>

    Add user

    list.jsp <%@ page import="java.util.List" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> Users list

    Super app!

    Users

    <% List names = (List ) request.getAttribute("userNames"); if (names != null && !names.isEmpty()) { out.println("
      "); for (String s : names) { out.println("
    • " + s + "
    • "); } out.println("
    "); } else out.println("
    \n" + " ×\n" + "
    There are no users yet!
    \n" + "
    "); %>
    Вот и все :) Если у вас остались Howие-то вопросы or есть Howие-то замечания, or же наоборот что-то не получается - оставьте комментарий. Ну и парочку скриншотов приложу что из этого всего получилось. Главная page applications Окно добавления пользователя Список пользователей И напоследок. Если будет желание попрактиковаться с этим проектом - можете попробовать:
    • сделать сервлет и jsp для удаления пользователя и еще пару для изменения/редактирования существующего пользователя. Получится настоящее CrUD веб приложение :) на сервлетах))
    • заменить список (List ) на работу с базой данных, чтоб добавленные пользователи не пропадали после перезапуска serverа :)
    Удачи!
    Teswirler
    TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
    GO TO FULL VERSION