1.1 interface Servlet
Сегодня начинаем новую и интересную тему — cервлеты. Именно добавление сервлетов в Java привело к тому, что Java сейчас — де-факто стандарт для больших серверных приложений. 80% всего корпоративного ПО в мире пишется на Java. А в Китае так и все 100%. Так что же такое сервлеты?
Сервлет — это именно то, что превращает Java-программу в веб-сервис и позволяет ей обрабатывать запросы от клиентов. А дело было так…
В 90-е годы, сразу после появления всемирной паутины, появились веб-клиенты (браузеры) и веб-сервера. Веб-сервера обычно просто раздавали через интернет файл, которые у них хранились: html-страницы, скрипты, картинки и т. п.
В какой-то момент все пришли к выводу, что нужно бы сделать обе стороны поумнее. В HTML-страницы добавили JavaScript, а серверам добавили плагины — специальные скрипты, которые вызывались в ответ на определенные запросы и позволяли сделать поведение сервера гибче и умнее.
Так вот сервлет — это такой Java-плагин, который встраивался в Java веб-сервер
и позволял ему выполнять Java-код при запросе на определенные страницы. А уже этот Java-код, представленный классом, унаследованным от класса Servlet, делал то, что задумали его разработчики.
И как ты уже знаешь, самый популярный Java веб-сервер — это Tomcat. Названный, кстати, в честь кота Тома из мультфильма “Том и Джери”.
Как же Tomcat взаимодействует с сервлетами? На самом деле этот процесс стандартизирован и называется жизненным циклом сервлета. В нем сервлет — это загружаемый объект, а веб-сервер — это контейнер сервлетов.
Если сервлет еще не загружен, то:
- Класс сервлета загружается контейнером.
- Контейнер создает экземпляр класса (объект) сервлета.
- Контейнер вызывает метод
init()
у объекта сервлета. Метод вызывается только один раз.
Стандартный цикл работы — обслуживание клиентского запроса:
- Каждый запрос обрабатывается в отдельном потоке.
- Контейнер вызывает метод
service()
у сервлета и передает туда объекты ServletRequest и ServletResponse. - Для завершения работы сервлета вызывается метод
destroy()
у объекта сервлета. Вызывается он всего один раз.
Причин завершения работы сервлета может быть очень много:
- Программист перезапускает веб-сервер, нужно корректно завершить работу всех сервлетов.
- Программист загружает новую версию сервлета, старую нужно правильно выгрузить.
- И так далее.
Запомни главное: веб-сервер и его сервлеты должны без сбоев и перезагрузки работать месяцами, обслуживая тысячи запросов в минуту. Поэтому код и загрузки, и работы, и выгрузки сервлета всегда нужно писать очень качественно.
1.2 Класс HttpServlet
Класс Servlet существует для стандартизации работы сервлета и контейнера. Непосредственно с этим классом программисты не работают. Ну или работают очень редко. Чаще всего используется класс HttpServlet
, унаследованный от Servlet’а.
У этого класса есть несколько методов, которые нам пригодятся. Ты будешь часто их использовать:
Метод | Описание | |
---|---|---|
1 | init() |
Вызывается один раз при загрузке сервлета |
2 | destroy() |
Вызывается один раз при выгрузке сервлета |
3 | service(HttpRequest, HttpResponse) |
Вызывается для каждого нового запроса к сервлету |
4 | doGet(HttpRequest, HttpResponse) |
Вызывается для каждого нового GET-запроса к сервлету |
5 | doPost(HttpRequest, HttpResponse) |
Вызывается для каждого нового POST-запроса к сервлету |
6 | doHead(HttpRequest, HttpResponse) |
Вызывается для каждого нового HEAD-запроса к сервлету |
7 | doDelete(HttpRequest, HttpResponse) |
Вызывается для каждого нового DELETE-запроса к сервлету |
8 | doPut(HttpRequest, HttpResponse) |
Вызывается для каждого нового PUT-запроса к сервлету |
Методы init()
и destroy()
унаследованы от класса Servlet. Поэтому если ты решишь переопределить их в своем сервлете, тебе нужно будет так же вызвать их реализацию из базового класса. Для этого используется команда super.имяМетода()
.
Пример сервлета:
public class FirstHttpServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Получаем параметр “secret” из запроса
String secret = request.getParameter("secret");
// Кладем параметр “secret” в Http-сессию
HttpSession session = request.getSession(true);
session.setAttribute("secret", secret);
// Печатаем HTML в качестве ответа для браузера
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
out.println("<html>");
out.println("<head>");
out.println("<title>Заголовок</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Пример сервлета "+ secret +"</h1>");
out.println("</body>");
out.println("</html>");
} finally {
out.close();
}
}
}
1.3 Метод service(HttpServletRequest, HttpServletResponse)
Если смотреть на обработку клиентского запроса с точки зрения сервлета, то дела обстоят примерно так.
Для каждого клиентского запроса контейнер (веб-сервер) создает объекты HttpServletRequest
и HttpServletResponse
, а затем вызывает метод service(HttpServletRequest request, HttpServletResponse response)
у соответствующего сервлета. В него передаются эти объекты, чтобы метод мог взять нужные данные из request’а
и положить результат работы в response
.
У метода service()
есть реализация по умолчанию. Если ее не переопределить, то выполняться будет именно она. Вот что он делает.
Метод service()
определяет из request’а тип HTTP-метода (GET, POST, …) и вызывает метод соответствующий запросу.
Метод | Описание | |
---|---|---|
1 | service(HttpRequest, HttpResponse) |
Вызывается для каждого нового запроса к сервлету |
2 | doGet(HttpRequest, HttpResponse) |
Вызывается для каждого нового GET-запроса к сервлету |
3 | doPost(HttpRequest, HttpResponse) |
Вызывается для каждого нового POST-запроса к сервлету |
4 | doHead(HttpRequest, HttpResponse) |
Вызывается для каждого нового HEAD-запроса к сервлету |
5 | doDelete(HttpRequest, HttpResponse) |
Вызывается для каждого нового DELETE-запроса к сервлету |
6 | doPut(HttpRequest, HttpResponse) |
Вызывается для каждого нового PUT-запроса к сервлету |
В своем классе ты можешь или переопределить один метод service()
, или не трогать его, но тогда переопределять методы doGet()
, doPost()
, … по необходимости.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ