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

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

Стаття з групи Random UA
Рівень знань, необхідних для розуміння статті: ви вже більш-менш розібралися з Java Core та хотіли б подивитися на JavaEE-технології та web-програмування. Логічніше, якщо ви зараз вивчаєте квест Java Collections, де розглядаються близькі статті теми. Створення простого веб-додатку на сервлетах та jsp (частина 1) - 1Цей матеріал є логічним продовженням моєї статті Створення найпростішого веб-проекту IntelliJ Idea Enterprise . У ній я продемонстрував, як створити шаблон веб-проекту, що працює. Цього разу я покажу, як створити простий, але цілком симпатичний веб-додаток за допомогою технологій Java Servlet API та JavaServer Pages API. В нашому додатку буде головна сторінка з двома посиланнями:
  • на сторінку додавання користувача;
  • на сторінку перегляду списку користувачів.
Я як і раніше використовуватиму IntelliJ Idea Enterprise Edition, Apache Maven (просто підключимо кілька залежностей) і Apache Tomcat. Наприкінці «прикрасимо» наш додаток використовуючи фреймворк W3.CSS . Вважатимемо, що зараз у вас вже є порожній проект, який ми і будемо тут розвивати. Якщо ж нема — пройдіться за першою статтею і зробіть його. Це займе лише кілька хвабон :)

Трохи про структуру майбутнього додатку

Головна сторінка ( / ) у нас буде найзвичайнішою статичною html-сторінкою з шапкою та двома посиланнями/кнопками:
  • додати нового користувача (відправлятиме на адресау /add );
  • переглянути список користувачів (надсилає на адресау /list ).
Запити за цими адресаами Tomcat ловитиме і відправлятиме на один із двох сервлетів, які ми зробимо (мапінг ми розпишемо у файлі web.xml ). А сервлети, у свою чергу, будуть обробляти запити, готувати дані (або зберігати їх у разі додавання користувача), та передавати управління у відповідні jsp-файли, які вже «малюватимуть» результат. Дані будемо зберігати у звичайному списку (List).

Створимо статичну головну сторінку

Якщо у вас у папці web лежить index.jsp – видаляйте його. Замість нього в цій папці створимо простий html-файл з ім'ям index.html :
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>My super project!</title>
</head>
<body>
    <!-- header -->
    <div>
        <h1>Super app!<//h1>
    </div>

    <div>       <!-- content -->
        <div>    <!-- buttons holder -->
            <button onclick="location.href='/list'">List users<//button>
            <button onclick="location.href='/add'">Add user<//button>
        </div>
    </div>
</body>
</html>
Тут нічого складного. У title вказуємо заголовок нашої сторінки. У тілі сторінки ми маємо два основних дива: header (шапка) і content (контент). У контенті у нас холдер для наших кнопок, та й власне дві кнопки, які за натисканням відправляють на відповідні адресаи. Можете запустити проект і подивитися, як він зараз виглядає. Якщо натискати на кнопки, відкриваються сторінки з помилкою 404, тому що у нас їх поки що немає. Але це свідчить, що кнопки працюють. Зауважу, це не найуніверсальніший варіант, оскільки якщо у вас раптом відключений JavaScript, в браузері від цих кнопок ніякого сенсу. Але вважатимемо, що ніхто JavaScript не відключав:). Ясно, що можна було б обійтися простими посиланнями, але мені більше до вподоби кнопочки. Ви робіть, як вам більше подобається. І не дивіться, що в моїх прикладах буде багато div-ів. Потім ми їх наповнимо стилями, і все виглядатиме красивіше:).

Створюємо jsp-файли для відображення результату

У тій же директорії web створимо папку, куди складатимемо наші jsp-файли . Я назвав її views, а ви, знову-таки, можете поімпровізувати. У цій папці створимо два jsp-файли:
  • add.jsp - сторінка для додавання користувачів;
  • list.jsp — сторінка для відображення списку користувачів.
Проставимо відповідні заголовки сторінки. Щось на кшталт "Add new user" і "Users list", і поки так і залишимо.

Створимо два сервлети

Сервлети прийматимуть та оброблятимуть запити, які їм передаватиме Tomcat. У папці src/main/java створимо пакет app , у якому лежатимуть наші вихідники. Там у нас буде ще різних пакетів. Тому, щоб ці пакети не створювалися всередині один одного, давайте створимо в пакеті app якийсь клас (потім видалимо). Тепер створимо в пакеті app три різні пакети:
  • entities - тут лежатимуть наші сутності (сам клас, який описуватиме об'єкти користувачів);
  • model - тут буде наша модель (про це трохи згодом);
  • servlets – ну а тут будуть наші сервлети.
Після цього той клас із пакета app можна спокійно видаляти (якщо ви його, звичайно, створювали). У пакеті servlets створимо два класи:
  • AddServlet - оброблятиме запити, що надійшли за адресаою /add ;
  • ListServlet — оброблятиме запити, що надійшли за адресаою /list .

Підключення залежностей у Maven

Tomcat версії 9.* реалізує специфікації Servlet версії 4.0 та JavaServer Pages версії 2.3. Про це написано в офіційній документації 9-го Tomcat у першому абзаці в другому рядку. Це означає, що якщо ви, як і я, використовуєте цю версію Tomcat, то наш код, який ми напишемо та відправимо виконувати, буде використовувати саме вказані версії. Але нам хотілося б мати ці специфікації і в нашому проекті, щоб наш код, який їх використовує, хоча б успішно компілювався. А для цього нам треба їх підвантажити до себе у проект. Ось тут і приходить на допомогу Maven.

Загальне правило таке: якщо вам треба підключити щось до вашого проекту, використовуючи Maven:

  • йдете на сайт репозиторію від Maven;
  • шукайте там потрібну вам бібліотеку потрібної версії;
  • отримуєте код залежності, який треба вставити у ваш pom.xml;
  • вставляєте! :)
Отже, починаємо. Для початку, підготуємо pom-файл . Десь після запису /version , але до /project вставляєте наступне:
<dependencies>

</dependencies>
Тим самим ми вказали, що всередині цих тегів ми перерахуємо необхідні нам залежності. Тепер заходьте на mvnrepository.com, там нагорі буде поле пошуку. Для початку вбиваєте в пошук Servlet. Перший же результат, де понад сім тисяч використань, нам і підходить. Пам'ятаємо, що нам потрібна версія 4.0 (для 9 Tomcat, для інших версій, можливо, підійдуть і більш старі реалізації). Це досить свіжа версія, тому використання не так вже й багато, але нам потрібна саме вона. Відкриється сторінка, звідки можна взяти код цієї залежності для різноманітних менеджерів пакетів і навіть можна просто завантажити. Але оскільки ми хочемо підключити її за допомогою Maven, то код вибираємо на вкладці Maven. Копіюємо та вставляємо в наш pom-файл усередину розділу із залежностями. Якщо у правому нижньому кутку IDEA вилізе повідомлення, де запитають, чи хочемо ми включити автоімпорт, погоджуємось. Якщо випадково відмовабося, заходьте до «Налаштувань» та увімкніть автоімпорт вручну:Settings (Ctrl + Alt + S) -> Build, Execution, Deployment -> Maven -> Importing Це дозволить тримати pom-файл та файли налаштування IDEA для цього проекту синхронізованими. Тепер за тим же принципом знайдемо та підключимо JavaServer Pages версії 2.3 (у пошуку вбиваєте jsp). І якщо ми вже взялися за Maven, давайте відразу вкажемо йому, що наші вихідні коди відповідають синтаксису Java 8, і що компілювати їх потрібно в байткод тієї ж версії. Після всіх цих маніпуляцій наш pom.xml виглядатиме приблизно так:
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>ru.codegym.info.fatfaggy</groupId>
    <artifactId>my-super-project</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>1.8</maven.compile.source>
        <maven.compiler.target>1.8</maven.compile.target>
    </properties>

    <dependencies>
        <!-- Servlet API 4.0 for tomcat 9 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.0</version>
            <scope>provided</scope>
        </dependency>

        <!-- JavaServer Pages API 2.3 for tomcat 9 -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

</project>

Робимо з наших сервлетів справжні сервлети

Наразі пара створених нами сервлетів насправді є звичайними класами. Вони не мають ніякої функціональності. Але тепер ми підключабо до нашого проекту Servlet API, а, раз так, ми можемо використовувати класи звідти. Щоб зробити наші сервлети справжніми сервлетами, досить просто успадкувати їх від класу HttpServlet .

Мапінг або розмітка

Тепер було б непогано розповісти Tomcat, щоб запити з адресаи /add оброблялися нашим сервлетом AddServlet , і, відповідно, запити за адресаою /list оброблялися сервлетом ListServlet . Саме цей процес і називається мапінгом (розміткою) . Робиться це у файлі web.xml за таким принципом:
  • спочатку описуємо сервлет (даємо якесь ім'я та вказуємо шлях до самого класу);
  • Потім прив'язуємо цей сервлет до конкретної адресаи (вказуємо ім'я сервлета, яке ми йому тільки-но дали і вказуємо адресау, запити з якої варто надсилати на цей сервлет).
Описуємо сервлет:
<servlet>
    <servlet-name>add</servlet-name>
    <servlet-class>app.servlets.AddServlet</servlet-class>
</servlet>
Тепер прив'язуємо його на адресау:
<servlet-mapping>
    <servlet-name>add</servlet-name>
    <url-pattern>/add</url-pattern>
</servlet-mapping>
Як бачимо, servlet-name в обох випадках однакове. Завдяки цьому Tomcat знає, що якщо надійшов запит на адресау /add , його потрібно передати в сервлет app.servlets.AddServlet . З другим сервлет проробляємо те ж саме. У результаті наш web.xml має приблизно такий зміст:
<?xml version="1.0" encoding="UTF-8"?>
<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">

    <!-- add servlet -->
    <servlet>
        <servlet-name>add</servlet-name>
        <servlet-class>app.servlets.AddServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>add</servlet-name>
        <url-pattern>/add</url-pattern>
    </servlet-mapping>

    <!-- list servlet -->
    <servlet>
        <servlet-name>list</servlet-name>
        <servlet-class>app.servlets.ListServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>list</servlet-name>
        <url-pattern>/list</url-pattern>
    </servlet-mapping>
</web-app>
До речі, ми не створабо розмітку для головної сторінки (за адресаою / ). Справа в тому, що в цьому випадку він нам і не потрібний. Наша головна сторінка – простий html-файл , який просто відображає дві кнопки. Там немає динамічного контенту, тому нам немає сенсу заводити під нього окремий сервлет, на який будуть передаватися запити з адресаи / , і який нічого не робитиме, крім передачі виконання на якусь jsp (яку теж довелося б завести), яка й малювала б нам дві кнопки. Нам це не потрібно, нас влаштовує статичний ресурс. Tomcat коли отримає запит, перевірить, що немає жодного сервлета, який зміг би обробити запит на таку адресау, а потім побачить що на цій адресаі власне лежить вже готовийhtml-файл , який він успішно і віддасть. Можемо запустити нашу програму знову (перезапустити сервер або задеплоїти повторно, як вам більше хочеться) і переконатися, що головна сторінка малюється, нічого не зламалося, коли натискаємо на кнопки — то переходи відбуваються, але поки що теж пишеться помилка. До речі, якщо до цього у нас була помилка 404, то тепер 405. Значить мапінг спрацював, сервлети знайшлися, але тільки не знайшлося в них потрібних способів щоб обробити запит. Якщо у вас на цьому етапі все одно 404я помилка, хоча все зроблено правильно - можливо вам варто поправити конфігурацію деплою в ідеї. для цього треба зайти в Edit configurations (там угорі біля кнопки запуску), там перейти у правій частині вікна на вкладку Deployment і зробити так, щоб у Application context було вказано просто /

Короткий ліричний відступ: що відбувається під капотом?

Ви, напевно, вже встигли задуматися, яким чином наша програма працює у Tomcat? Що там взагалі відбувається? І де метод main() ? Як тільки ви вбиваєте в браузері localhost:8080 і переходите на цю адресау, браузер відправляє на цю адресау запит за протоколом http . Сподіваюся, ви вже знаєте, що запити можуть бути різних «типів», найпопулярніші — GET і POST . На кожен запит має бути відповідь. GET-запит очікує, що у відповідь йому віддадуть готовий html-код , який повернеться в браузер, а браузер вже цей код красиво замінить на будь-які літери, кнопочки, формочки. POST-запиттрохи цікавіше, оскільки він із собою ще несе інформацію. Наприклад, у формі реєстрації або авторизації користувача ви ввели свої дані та натиснули «надіслати». У цей момент на сервер відправився POST-запит із власною інформацією всередині. Сервер цю інформацію прийняв, обробив і повернув якусь відповідь (наприклад, html-сторінку з вашим профілем). Принципова відмінність між ними в тому, що GET-запити призначені лише для отримання даних із сервера, а POST-запитищо не вміє обробляти такий тип, Tomcat кидає клієнту у відповідь код 405. Що і сталося тільки-но у нас. Але якщо знайшовся і підходящий сервлет, і він має підходящий метод, Tomcat створює об'єкт цього сервлета, запускає їх у новому треді (thread ), що дозволяє сервлет працювати в окремому потоці, а Tomcat продовжує працювати і далі у своєму, приймати і відправляти запити. Крім того, Tomcat створює ще два об'єкти: один типу HttpServletRequest (коротко я його називатиму далі запитом), а другий — типу HttpServletResponse(Зватиму відповіддю). У перший об'єкт вона поміщає всі дані, що прийшли у запиті від клієнта, таким чином з цього об'єкта всі ці дані можна буде витягнути. Ну і після всього цього передає ці два об'єкти в відповідний метод того сервлета, який запущений в окремому потоці. Як тільки сервлет закінчить роботу і в нього буде готова відповідь, яку треба відправити клієнту, він піднімає прапор Tomcat'у, мовляв, «я закінчив, все готове». Tomcat приймає відповідь і надсилає її клієнту. Це дозволяє Tomcat не відволікаючись приймати запити та надсилати відповіді, а всю роботу роблять сервлети, які крутяться в окремих потоках. Відповідно, коли ми пишемо код сервлета, ми визначаємо ту роботу, яка виконуватиметься. І так, можете вважати, що метод main()знаходиться в самому Tomcat (так, він написаний на Java), і коли ми запускаємо Tomcat, запускається метод main().

Ловимо сервлетами GET-методи та відправляємо найпростіші відповіді

В даний момент у наших сервлетах немає відповідних методів (GET), тому Tomcat нам повертає помилку 405. Зробимо їх! У класі HttpServlet , від якого ми успадкували наші сервлети, визначено різні методи. Для того, щоб задати якийсь код для методів, ми їх просто випереджаємо. У разі нам треба перевизначити метод doGet() в обох сервлетах.
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

}
Як бачимо, цей метод приймає два об'єкти: req (запит) та resp (відповідь). Це ті самі об'єкти, які створить та наповнить нам Tomcat, коли викличе відповідний метод у цьому сервлеті. Спочатку давайте зробимо найпростіші відповіді. Для цього візьмемо об'єкт resp і отримаємо з нього об'єкт PrintWriter -а, яким можна складати відповіді. Ну і за допомогою нього виведемо якийсь простий рядок.
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    PrintWriter writer = resp.getWriter();
    writer.println("Method GET from AddServlet");
}
Щось подібне зробимо і в сервлеті ListServlet , після чого запустимо наш сервер знову. Як бачимо, все працює! При натисканні на кнопки відкриваються сторінки з тим текстом, який ми записали PrintWriter- ом. Ось тільки ті наші jsp , які ми підготували для формування сторінок з відповідями, ніяк не використовуються. Це тому, що виконання до них просто не доходить. Сервелет сам у нас зараз формує відповідь і закінчує роботу, сигналізуючи Tomcat, що у нього готова відповідь клієнту. Tomcat просто бере цю відповідь і відправляє її назад клієнту. Передаємо управління з сервлетів в jsp Змінимо код наших методів таким чином:
  • отримуємо з об'єкта запиту об'єкт диспетчера запитів, куди передаємо адресау jsp сторінки, якою хочемо передати управління;
  • використовуючи отриманий об'єкт - передаємо управління у вказану jsp сторінку, і не забуваємо вкласти туди ті об'єкти запиту та відповіді, які ми отримали від Tomcat.
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    RequestDispatcher requestDispatcher = req.getRequestDispatcher("views/add.jsp");
    requestDispatcher.forward(req, resp);
}
У тілі jsp-сторінок (всередині тега body) можна щось написати, щоб ми могли чітко бачити, яка сторінка відображається. Після цього перезапускаємо сервер та перевіряємо. Кнопочки на головній сторінці натискаються, сторінки відкриваються, а значить, запити в сервлети передаються, після чого керування передається в jsp сторінки, які вже і малюються. На цьому все. У наступній частині статті ми займемося функціональністю нашої програми.

Що ще почитати:

Створення найпростішого веб-проекту в IntelliJ Idea Enterprise. Покроково, з картинками


Мій чат
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ