Всім привіт! У цій статті ви познайомитеся з таким базовим поняттям веб-розробки, як сервлети, і зможете написати простий додаток з їх використанням. Щоб уникнути зайвих дій, ми не починатимемо з нуля, і продовжимо роботу над нашим додатком з моєї попередньої статті про Hibernate . Однак, оскільки ми тільки починаємо знайомитися з сервлетами, я прибрав із програми все, що пов'язано з класом Auto, і залишив лише клас User та дії з ним. Структура проекту виглядатиме так: Отже, сервлети! Вікіпедія говорить: "Сервлет є інтерфейсом Java, реалізація якого розширює функціональні можливості сервера. Сервлет взаємодіє з клієнтами за допомогою принципу запит-відповідь." І це справді так. Тут ми вперше стикаємося з поняттям "клієнт-серверна архітектура програми". Суть її цілком проста і уміщається на одній картинці (взята звідси ).
Клієнт звертається до сервера за допомогою надсилання HTTP-запиту. Сервер формує необхідні дані (наприклад, отримує їх із бази даних) та повертає клієнту. Найпростіший приклад: в якійсь соціальній мережі ви натискаєте кнопку "Друзі" і відправляєте таким чином запит серверу. Сервер уточнює у базі даних список ваших друзів і повертає його вам (клієнту). Список HTTP-запитів досить великий, але якщо ви ніколи з ними не стикалися, то для кращого розуміння краще прочитати про них, наприклад, тут . Наше завдання полягає в наступному: Створити CRUD-додаток із використанням сервлетів. Програма повинна вміти створювати, змінювати та видаляти користувачів з бази даних, використовуючи для цього сервлет, що обробляє запити HTTP. Наш додаток зі статті про Hibernate вже вміло робити це, проте він справлявся прямо з Java-коду, точніше - з методу main(). Тут же запити буде надсилати саме клієнт, тобто Ви :) Перше, що нам потрібно зробити, це додати нові залежності в наш файл 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>com.itis4</groupId>
<artifactId>UsersDaoProject</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
</plugin>
</plugins>
</build>
<dependencies>
<!-- PostgreSQL -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4.1212.jre7</version>
</dependency>
<!-- Hibernate 5.2.6 Final -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.6.Final</version>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.4.RELEASE</version>
</dependency>
</dependencies>
</project>
Ми додали 3 залежності:
- Сама бібліотека javax.servlet-api;
- Бібліотека тегів JSTL. Вона буде потрібна для створення клієнтської сторони, а саме сторінок JSP;
- Spring-WebMVC. Нам знадобиться один клас Spring'a, про який ми поговоримо трохи згодом.
- У разі відсутності сервлета у контейнері.
- Клас сервлету завантажується контейнером.
- Контейнер створює екземпляр класу сервлету.
- Контейнер викликає метод init(). Цей метод ініціалізує сервлет і викликається в першу чергу до того, як сервлет зможе обслуговувати запити. За весь життєвий цикл метод init() викликається лише один раз.
- Обслуговування запиту клієнта. Кожен запит обробляється у окремому потоці. Контейнер викликає метод service() для кожного запиту. Цей метод визначає тип запиту, що прийшов, і розподіляє його у відповідний цьому типу метод для обробки запиту. Розробник сервлету має надати реалізацію цих методів. Якщо надійшов запит, метод якого не реалізований, викликається метод батьківського класу і зазвичай завершується поверненням помилки ініціатору запиту.
- Якщо контейнер необхідно видалити сервлет, він викликає метод destroy(), який знімає сервлет з експлуатації. Подібно до методу init(), цей метод теж викликається одного разу за весь цикл сервлету.
package servlets;
import models.User;
import services.UserService;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
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 UserSimpleServlet extends HttpServlet {
private UserService service = new UserService();
public void init(ServletConfig servletConfig) {
try {
super.init(servletConfig);
} catch (ServletException e) {
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
List<User> users = service.findAllUsers();
req.setAttribute("users", users);
RequestDispatcher dispatcher = req.getRequestDispatcher("/showUsers.jsp");
dispatcher.forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String name = req.getParameter("name");
int age = Integer.parseInt(req.getParameter("age"));
User user = new User(name, age);
service.saveUser(user);
resp.sendRedirect("/users");
}
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int id = Integer.parseInt(req.getParameter("id"));
User user = service.findUser(id);
user.setName(req.getParameter("name"));
user.setAge(Integer.parseInt(req.getParameter("age")));
service.updateUser(user);
resp.sendRedirect("/users");
}
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
int id = Integer.parseInt(req.getParameter("id"));
service.deleteUser(service.findUser(id));
resp.sendRedirect("/users");
}
}
Як бачите, він містить у собі метод init(), про який писалося вище, і реалізує 4 методи, що збігаються з чотирма HTTP-запитами - doGet(), doPost(), doPut() та doDelete(). Кожен із них дозволить нам, відповідно, отримувати, створювати, редагувати та видаляти користувачів. Методи приймають на вхід об'єкти класів javax.servlet.http.HttpServletRequest і javax.servlet.http.HttpServletResponse - тобто запит, що надсилається на сервер, та відповідь, яку отримає клієнт. Усередині методів виконуються необхідні методи класу UserService, формується відповідь клієнту, після чого здійснюється перенаправлення на адресау /users. Наприклад, у методі doGet() ми отримуємо список усіх користувачів. Далі ми створюємо об'єкт класу RequestDispatcher, який дозволяє включати об'єкти в Http-запит, а також перенаправляти його до певного ресурсу (наприклад, клієнтської JSP-сторінці). У методі doPut() (оновлення даних користувача) ми обробляємо HTTP-запит, витягуємо з нього параметри id, name і age, знаходимо користувача із зазначеним id, присвоюємо йому ті name і age, які прийшли разом з ним у запиті, і повертаємось на сторінку /users. Однак, щоб усі ці методи коректно працювали, нам необхідно здійснити налаштування сервлету. Для цього ми використовуємо файл web.xml у папці WEB-INF. нам необхідно здійснити налаштування сервлету. Для цього ми використовуємо файл web.xml у папці WEB-INF. нам необхідно здійснити налаштування сервлету. Для цього ми використовуємо файл web.xml у папці WEB-INF.
<?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">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>UserSimpleServlet</servlet-name>
<servlet-class>servlets.UserSimpleServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserSimpleServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<servlet-name>UserSimpleServlet</servlet-name>
</filter-mapping>
</web-app>
Всі теги в цьому файлі, в принципі, інтуїтивно зрозумілі, але пройдемося по них послідовно. <welcome-file-list> - вказана стартова JSP-сторінка, яка відкриватиметься першою під час запуску програми. У нашому випадку, це сторінка index.jsp. <servlet> - реєстрація нашого класу UserSimpleServlet як сервлет. <servlet-mapping> – дуже важливий тег. Він визначає URL, які будуть оброблятися сервлетом. У нашому випадку це взагалі всі URL, тому ми вказуємо просто "/". Але, наприклад, якби у нас була програма з користувачами та їх машинами, то можна було б створити другий сервлет - SimpleAutoServlet. Тоді мапінгом для користувача-сервлета було б "/users" (тобто запити, що стосуються обробки користувачів), а для авто-сервлета - "/autos". І, нарешті, <filter>. Він визначає в собі об'єкт класу org.springframework.web.filter.HiddenHttpMethodFilter. Стаття не стосується Spring'a, тому не розповідатиму про нього в подробицях. Скажу лише, що до нашого додатку він прикручений лише як додаткова фіча. Справа в тому, що для створення сторони клієнта ми будемо використовувати JSP-сторінки. Наші дані відображатимуться на сторінці як таблиця зі списком користувачів. Всередині JSP-сторінок використовуватимуться HTML-теги <form/>. А для надсилання даних з <form/> можуть бути використані лише HTTP-запити GET та POST. Тобто для всіх трьох операцій – оновлення, видалення та створення користувача – нам довелося б використовувати лише POST-запити. Використання PUT та DELETE запитів було б нам недоступним. І, в принципі, це цілком нормально і нескладно реалізувати, але клас HiddenHttpMethodFilter дозволяє нам їх використати. Таким чином, читачеві буде більш очевидно буде видно різниця між операціями в додатку. Зрештою, переходимо до клієнтської частини. Вона представлена п'ятьма JSP-сторінками. index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Здравствуйте!</title>
</head>
<body>
Если вы хотите начать работу с базой данных пользователей - <br>
нажмите кнопку ниже:
<form action = "users" method="get">
<input type="submit" value="Начать работу с базой данных">
</form>
</body>
</html>
addUser.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Добавить нового пользователя</title>
</head>
<body>
<form action = "/users" method="post">
<input required type="text" name="name" placeholder="Ім'я">
<input required type="text" name="age" placeholder="Возраст">
<input type="submit" value="Сохранить">
</form>
</body>
</html>
deleteUser.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Удалить пользователя</title>
</head>
<body>
Вы действительно хотите удалить пользователя ${param.id}?
&lform action="/users/${param.id}" method="post">
<input type="hidden" name="id" value="${param.id}">
<input type="hidden" name="_method" value="delete">
<input type="submit" value="Удалить">
</form>
</body>
</html>
showUsers.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Список пользователей</title>
</head>
<body>
<table border="2">
<tr>
<td>ID</td>
<td>Ім'я</td>
<td>Возраст</td>
<td>Действия</td>
</tr>
<c:forEach items="${users}" var = "user">
<tr>
<td>${user.getId()}</td>
<td>${user.getName()}</td>
<td>${user.getAge()}</td>
<td>
<form action = "updateUser.jsp" method="post">
<input type="hidden" name="id" value="${user.getId()}">
<input type="hidden" name="name" value="${user.getName()}">
<input type="hidden" name="age" value="${user.getAge()}">
<input type="submit" value="Изменить" style="float:left">
</form>
<form action="deleteUser.jsp" method="post">
<input type="hidden" name="id" value="${user.getId()}">
<input type="submit" value="Удалить" style="float:left">
</form></td>
</tr>
</c:forEach>
</table>
<form action = "addUser.jsp">
<input type="submit" value="Добавить нового пользователя">
</form>
</body>
</html>
updateUser.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Изменить данные пользователя</title>
</head>
<body>
Редактировать пользователя
<form action="/users/${param.id}" method="post">
<input type="hidden" name = "id" value="${param.id}">
<input type="text" name="name" value="${param.name}" placeholder=${param.name}>
<input type="text" name="age" value="${param.age}" placeholder=${param.age}>
<input type="hidden" name="_method" value="put">
<input type="submit" value="Обновить">
</form>
</body>
</html>
Сторінка JSP (Java Server Page) містить текст двох типів: статичні вихідні дані, які можуть бути оформлені в одному з текстових форматів (HTML, SVG, WML або XML), та JSP-елементи, які конструюють динамічний вміст. Для розуміння того, що таке JSP я дозволю собі скопіпастить шматочок дуже гарної статті одного автора ( звідси ). "По суті JSP при першому зверненні перетворюється на сервлет і працює вже як сервлет. Це дуже важливо зрозуміти. JSP НЕ Є сторінкою на кшталт HTML-сторінки— програмісту-початківцю важливо чітко усвідомлювати, що це ще один сервлет — просто його висновок не треба програмувати. Його можна просто намалювати. І у потрібні місця підставити дані. Але т.к. JSP-сторінка хоч якось нагадує HTML, то дизайнерові буде простіше. І я ще раз наполегливо говорю початківцям — JSP є СЕРВЛЕТОМ. Її підготовка з усіма даними відбувається на сервері. Саме там підставляються усі дані. А користувачеві в браузер приходить вже готова HTML-сторінка, на якій ніяких ознак JAVA немає." Ви можете самі переконатися в тому, що JSP-сторінка дійсно є сервлетом, адже на кожній сторінці вказано метод, який необхідно виконати. Наприклад, на стартовій сторінці на сторінці index.jsp вказано, що при натисканні на кнопку "Почати роботу з базою даних" буде виконано method="get". Решту JSP складає звичайна статична HTML-розмітка, тому докладно зупинятися на них не будемо - це тема окремої статті, яких чимало на просторах Інтернету. ми створабо наш додаток, залишилося випробувати його у справі! Для цього нам знадобиться згаданий вище контейнер сервлетів Apache Tomcat. Завантажити кота можназ офіційного сайту (я використовую 8 версію). Далі нам потрібно створити в IDEA конфігурацію для запуску нашої програми через Tomcat. Для цього відкриваємо вкладку "Edit Configurations", створюємо нову конфігурацію та вибираємо Tomcat Server Local. У вкладці Application Server вказуємо шлях до папки, де лежить Tomcat Далі переходимо на вкладку Deployment. Тут здійснюється налаштування розгортання нашої програми на локальному сервері. Наживаємо "+", вибираємо "Artifact"-> Ім'я Вашого Проекту: war (ми будемо збирати додаток у war-файл). Ось, загалом, і все! На сторінці "Server" ви можете побачити, що програма буде працювати за адресаою "http://localhost:8080/". Збережіть цю конфігурацію і якось назвіть (у мене назва конфіга - "Tommy"). Далі, на вкладці Maven в IDEA (праворуч) скористаємося war-плагіном для складання нашого проекту в war-файл (Plugins -> war -> war:war). Після того, як складання було здійснено, запускаємо програму та чекаємо. Успіх! Стартова сторінка запустилася. Тепер натисніть кнопку "Почати роботу з базою даних". Наша JSP сторінка index.jsp сформує GET-запит, який буде оброблений сервером. Сервер сформує відповідь і поверне її нам у вигляді списку всіх існуючих користувачів (якщо вони, звичайно, є у БД). А ось і вони! Теж працює! Ось ми і написали наш перший додаток із використанням сервлетів. Як бачите, все виявилося не так вже й складно :) Як домашнє завдання Ви можете, наприклад, повернути в додаток функціонал роботи з автомобілями з попередньої статті. Тобто. створити для автомобілів окремий сервлет та jsp-сторінки та навчити наш додаток виводити список автомобілів користувача, додавати йому нові машини, а також редагувати та видаляти їх. PS Сервлети та JSP - технології досить давні, і на просторах інтернету часто можна зустріти коментарі на кшталт "кому потрібна ця старість?". Відповідь досить проста - вона потрібна насамперед тим, хто працюватиме на справжніх проектах, в яких цілком можливо купа написаного з їх використанням коду. І перепилювати "старі" на щось нове,"Head First Servlets and JSP" (лише англійською). Її писали самі автори, як і знамениту супер-книгу " Head First Java " , що багатьом може бути гарантом якості:) Сподіваюся, ця стаття була корисна читачам! Якщо Ви хотіли б побачити нові статті – не забудьте підтримати автора у конкурсі, поставивши "Подобається". А краще - "Дуже подобається" :) Дякую за увагу, та успіхів у навчанні!
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ