JavaRush /Java блог /Random UA /Створення простого веб-додатку на сервлетах та jsp (части...
fatfaggy
26 рівень
Киев

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

Стаття з групи Random UA
Продовжую описувати процес створення веб-програми використовуючи сервлети, 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; } } Тепер можемо приступити до створення списку користувачів, куди ми будемо додавати наших користувачів, і звідки їх забиратимемо для відображення. Але є проблема. Об'єкти наших сервлетів ми не створюємо, їх за нас створює томкат. Методи, які ми перевизначаємо у них – теж за нас уже визначено і додати параметр ми не можемо. Як же тоді створити загальний список, який було б видно в обох наших сервлетах? Якщо ми просто в кожному сервлет створимо свій об'єкт списку - то вийде, що додавати користувачів ми будемо в один список, а виводити список користувачів сервлет ListServlet - зовсім інший. Виходить, що нам потрібний такий об'єкт, який був би спільним для обох сервлетів. Якщо говорити узагальнено, то нам потрібний такий об'єкт, який був би спільним для всіх класів у нашій програмі; єдиний об'єкт всю програму. Сподіваюся, ви щось і чули про шаблони проектування. І можливо для когось це перша реальна необхідність використання шаблону Singleton у своїй програмі. Можете поплутатися і запиляти який-небудь крутий синглтон, з подвійними перевірками і синхронізаціями (так-так, у нас багатопотоковий додаток, так як сервлети томкат запускає в різних потоках), але я використовуватиму варіант з ранньою ініціалізацією, так як в даному випадку він цілком підходить.
Створення моделі.
Створимо тоді клас (і реалізуємо в ньому шаблон singleton) у пакеті model, та й назвемо теж цілком яскраво Model. Створимо в ньому приватний об'єкт списку користувачів, і зробимо два методи: один для того, щоб можна було додати користувача, а другий - нехай повертає список рядків (імен користувачів). Оскільки наш об'єкт користувача складається з імені та пароля - то паролі користувачів ми "світити" не хотіли б, тому і повертатимемо лише список їхніх імен. 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, або російською модель-представлення-контролер, або прямий як і на англійському модель-в'ю-контролер). Його суть у тому, щоб відокремлювати бізнес-логіку від вистави. Тобто, відокремлювати код, який визначає що робити від коду, який визначає як відображати. View (подання або просто завірюхи) якраз і відповідає за те, в якому вигляді представляти якісь дані. У нашому випадку завірюхи - це наші jsp сторінки. Саме тому я їх і поклав у татко під назвою views. Модель – це власне самі дані, з якими працює програма. У нашому випадку, це користувачі (список користувачів). Ну а контролери - сполучна ланка між ними. Беруть дані з моделі і передають їх у завірюхи, чи отримують від томката якісь дані, обробляють їх і передають моделі. Бізнес-логіка (тобто що робити ) повинна бути описана в них, а не в моделі або у завірюсі. Таким чином, кожен займається своєю справою:
  • модель зберігає дані
  • завірюхи малюють гарне представлення даних
  • контролери займаються обробкою даних
Це дозволяє всім їм бути досить простими та підтримуваними. А не монструозним звалищем всього коду в одному класі. MVC підходить не тільки для веб-програмування, але все-таки в цій сфері він зустрічається дуже часто (якщо не завжди). У нашому випадку як контролери виступатимуть сервлети. Так це дуже поверховий і навіть грубий опис цього патерну, але ця стаття не про шаблони проектування, а про те, як зробити просте веб-додаток :) Хто хоче дізнатися більше - гугл знає все ! :) Повернемося до наших в'юхів.
Створюємо форму додавання користувача.
Додамо у файл add.jsp форму, що складається з двох текстових інпутів (один звичайний, інший типу пароль) та кнопки для надсилання даних на сервер.
Тут у форми вказано атрибут method зі значенням post. Це говорить про те, що дані з цієї форми полетять на сервер у вигляді запиту POST. Атрибут action не вказаний, отже запит цей полетить за тією ж адресаою, якою ми перейшли на цю сторінку (/add). Таким чином, наш сервлет, який прив'язаний до цієї адресаи, при отриманні GET-запиту повертає цю jsp з формою додавання користувачів, а якщо отримає POST-запит - значить ця форма відправила туди свої дані (які ми в методі doPost() витягнемо з об'єкта запиту , обробимо та передамо в модель на збереження). Варто звернути увагу, що у інпутів тут вказано параметр name (для поля з ім'ям він має значення name, а для поля з паролем – pass). Це досить важливий момент. Так як щоб отримати з запиту (всередині сервлета вже) ці дані (ім'я та пароль, які будуть введені) - ми будемо використовувати саме ці name і pass. Але про це трохи згодом. Сама кнопка відправки даних у мене зроблена знову ж таки у вигляді button, а не інпутом, як це зазвичай прийнято. Не знаю, наскільки такий варіант універсальний, але у мене в хромі працює :)
Обробка POST-запиту сервлетом.
Повернемося до сервлета AddServlet. Ми вже знаємо, щоб наш сервлет умів "ловити" GET-запити - ми перевизначабо метод doGet() з класу HttpServlet. Щоб навчити наш сервлет ловити ще й POST-звапроси – ми перевизначаємо ще й метод doPost(). Він отримує аналогічні об'єкти запиту та відповіді від томкату, з якими ми і працюватимемо. Для початку витягнемо із запиту параметри name і pass, які відправила форма (якщо ви їх у формі назвали по-іншому - тоді саме ті назви і пишете). Після цього створимо об'єкт нашого користувача, використовуючи отримані дані. Потім отримаємо об'єкт моделі та додамо створеного користувача до моделі. @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. Тепер було б непогано отримати з моделі список імен користувачів і передати їх у завірюху, яка їх там отримає і гарно відобразить. Для цього скористаємося знову ж таки об'єктом запиту, який ми отримали від томкату. До цього об'єкту ми можемо додати атрибут, давши йому якесь ім'я та й власне сам об'єкт, який би ми хотіли передати у завірюху. Завдяки тому, що при передачі процесу виконання з сервлета у завірюху ми передаємо туди ці ж об'єкти запиту та відповіді, що отримав сам сервлет, то і додавши наш список імен до об'єкта запиту, ми потім з цього об'єкта запиту у в'юсі зможемо наш список імен користувачів і отримати. З класом ListServlet ми закінчабо, тому наводжу код всього класу 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-код в jsp файлух.
Тепер перейдіть до файлу list.jsp. Цей файл виконається лише коли ListServlet передасть процес виконання. Крім того, ми в тому сервлеті вже підготували список імен користувачів із моделі та передали сюди в об'єкті запиту. Маючи список імен, ми можемо пробігтися по ньому циклом for і вивести кожне ім'я. Як я вже говорив, jsp файли можуть виконувати java-код (в принципі, цим і відрізняються від статичних html сторінок). Для того, щоб виконати якийсь код, достатньо поставити в потрібному нам місці конструкцію. <% // java код %> Усередині такої конструкції ми отримуємо доступ до кількох змінних: request - наш об'єкт запиту, який ми передали з сервлета, де він називався просто req responce - об'єкт відповіді, у сервлеті називався resp out - об'єкт типу JspWriter (успадковується від звичайного Writer), за допомогою якого можемо "писати" щось прямо в саму html сторінку. Запис out.println("Hello world!") дуже схожий на запис System.out.println("Hello world!"), але не варто їх плутати! out.println() "пише" в html сторінку, а System.out.println - у системний висновок. Якщо викликати всередині розділу з джава кодом jsp метод System.out.println() - то результати побачите в консолі томкату, а не на сторінці, як можна хотілося б :) Про інші доступні об'єкти всередині jsp можна пошукати тут. Використовуючи об'єкт request, ми можемо отримати список імен, який передавали з сервлета (ми прикріпабо відповідний атрибут до цього об'єкта), а використовуючи об'єкт 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() після того, як додали нового користувача в модель - можемо додати ім'я цього користувача в атрибути об'єкта req і передати керування назад у завірюху add.jsp. А в ній вже зробити ділянку з джава кодом, де перевіряти чи є такий атрибут у запиті, і якщо так - виводити повідомлення, що користувач успішно доданий. Після цих змін повний код сервлету 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(), який передаємо поточні запит і відповідь. А метод doGet() вже передає управління у завірюху, куди і відправляє об'єкт запиту з прикріпленим ім'ям доданого користувача як атрибут. Залишилося підправити саму add.jsp щоб вона виводила таке повідомлення, якщо є такий атрибут. Кінцевий варіант add.jsp Тіло сторінки складається з div-a з шапкою, після чого div-контейнер для контенту, в ньому перевірка чи існує атрибут з ім'ям користувача, потім div з формою додавання користувачів, ну і в кінці футер з кнопкою повернення на головну сторінку. Може здатися, що дуже багато div-ів, але ми їх потім використовуємо, коли додамо стилів :) Ну і кінцевий варіант list.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

    <%@ 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!

    "); %>
    Таким чином, ми маємо повністю робочий веб-додаток, який вміє зберігати і додавати користувачів, а також виводити список їх імен. Залишилося лише прикрасити...
    Додавання стилів. Використовуємо фреймворк W3.CSS.
    В даний момент наш додаток робочий, але абсолютно вирвіглазний :) Потрібно додати фон, кольори тексту та кнопок, стилізувати списки, зробити вирівнювання, додати відступів, загалом багато чого. Якщо писати стилі вручну – це може зайняти багато часу та нервів. Тому я пропоную скористатися CSS фреймворком W3.CSS . У ньому вже є готові класи зі стилями, залишилося тільки розставити у потрібних місцях ті css класи, які ми хочемо застосувати в цих місцях. Для того щоб додати їх до себе на сторінки по-перше нам треба підключити файл зі стилями. Це можна зробити двома способами: 1. пройтися по наших сторінках та в розділі head вставити пряме посилання на файл зі стилями Такий варіант підходить, якщо у вас є постійне підключення до інтернету. Тоді при відкритті ваших сторінок на локальному сервері стилі підтягнуться з інтернету. 2. Якщо ж ви хочете мати всі стилі у себе локально і не бути залежним від інтернет-з'єднання - можете просто завантажити файл зі стилями і помістити його десь усередині папки web (наприклад web/styles/w3.css), після чого пройтися по всіх наших сторінках (index.html, add.jsp, list.jsp) і вписати всередині розділу head посилання на цей файл зі стилями Після цього просто пройтися тегами і підписувати їм ті стилі, які вам сподобаються. Я не зупинятимуся на цьому докладно, а відразу дам свої готові варіанти трьох моїх файлів з розставленими класами стилів. index.html add.jsp list.jsp Super app!

    Super app!

    <%@ 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

    <%@ 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" + "
    "); %>
    Ось і все :) Якщо у вас залишабося якісь питання або є якісь зауваження, або навпаки щось не виходить - залиште коментар. Ну і парочку скріншотів докладу що з цього вийшло. Головна сторінка програми Вікно додавання користувача список користувачів І на останок. Якщо буде бажання попрактикуватися з цим проектом – можете спробувати:
    • зробити сервлет та jsp для видалення користувача та ще пару для зміни/редагування існуючого користувача. Вийде справжній CrUD веб додаток :) на сервлетах))
    • замінити список (List ) працювати з базою даних, щоб додані користувачі не пропадали після перезапуску сервера :)
    Успіхів!
    Коментарі
    ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
    ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ