Сервлеты — это классы Java, соответствующие API сервлетов Java, что позволяет классу Java отвечать на запросы. Хотя сервлеты могут отвечать на запросы любого типа, чаще всего они пишутся для ответа на веб-запросы. Чтобы сервлет стал пригодным для использования, его необходимо развернуть в контейнере сервлетов Java.
Хотя многие разработчики используют фреймворки сервлетов, такие как Java Server Pages(JSP) и Java Server Faces(JSF), обе эти технологии компилируют страницы в сервлеты Java за кулисами через контейнер сервлетов. Тем не менее, фундаментальные знания технологии сервлетов Java могут быть очень полезны для любого веб-разработчика Java.
В этом уроке мы рассмотрим следующие темы, чтобы получить общее представление о технологии сервлетов Java.
1. Первый сервлет
Наш первый сервлет — очень простой сервлет с минимальным количеством кода, чтобы вы могли сосредоточиться только на самом важном.
package com.howtodoinjava.servlets;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class MyFirstServlet extends HttpServlet {private static final long serialVersionUID = -1915463532411657451L;@Overrideprotected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException{response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();try {// Write some contentout.println("<html>");out.println("<head>");out.println("<title>MyFirstServlet</title>");out.println("</head>");out.println("<body>");out.println("<h2>Servlet MyFirstServlet at " + request.getContextPath() + "</h2>");out.println("</body>");out.println("</html>");} finally {out.close();}}@Overrideprotected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {//Do some other work}@Overridepublic String getServletInfo() {return "MyFirstServlet";}}
Чтобы зарегистрировать указанный выше сервлет в веб-контейнере, мы создадим файл записи web.xml.
Приведенный выше сервлет делает несколько важных вещей, которые нам, возможно, стоит изучить.
MyFirstServlet extends HttpServlet. Это обязательно, поскольку все сервлеты должны быть либо универсальным сервлетом, расширяющим javax.servlet.GenericServlet, либо HTTP-сервлетом, расширяющим javax.servlet.http.HttpServlet.
Переопределение методов doGet() и doPost(). Эти методы определены в классе HttpServlet. Всякий раз, когда приходит запрос GET или POST, он сопоставляется с соответствующим ему методом, например, если вы отправляете
HTTP-запрос GET к этому сервлету, затем вызывается метод doGet(). Существуют также некоторые другие полезные методы, которые можно переопределить для управления приложением во время выполнения, например, getServletInfo().
HttpServletRequest и HttpServletResponse являются параметрами по умолчанию для всех методов doXXX(). Мы узнаем больше об этих объектах в следующем разделе.
2. Жизненный цикл сервлета
Всякий раз, когда в нашем приложении загружается и используется сервлет, происходит ряд событий во время инициализации и уничтожения этого сервлета. Они называются событиями жизненного цикла(или методами) сервлета.
Три метода являются центральными в жизненном цикле сервлета. Это init(), service() и destroy(). Они реализуются каждым сервлетом и вызываются в определенное время средой выполнения.
init(): На этапе инициализации жизненного цикла сервлета веб-контейнер инициализирует экземпляр сервлета, вызывая метод init() и передавая объект, реализующий интерфейс javax.servlet.ServletConfig. Этот объект конфигурации позволяет сервлету получать доступ к параметрам инициализации имя-значение, определенным в файле web.xml веб-приложения. Он вызывается только один раз за время жизни этого экземпляра сервлета.
Определение метода init выглядит следующим образом:
public void init() throws ServletException {//custom initialization code}
service(): После инициализации экземпляр сервлета может обслуживать клиентские запросы. Веб-контейнер вызывает метод service() сервлета для каждого запроса. Метод service() определяет тип сделанного запроса и направляет его соответствующему методу для обработки запроса. Разработчик сервлета должен предоставить реализацию для этих методов.
Если запрос выполняется для метода, который не реализован сервлетом, вызывается метод родительского класса, что обычно приводит к возврату ошибки запрашивающей стороне.
Нет необходимости переопределять этот метод «почти» во всех ситуациях.
destroy(): Наконец, веб-контейнер вызывает метод destroy(), который выводит сервлет из эксплуатации. Мы должны вызвать этот метод, если вы хотите закрыть или уничтожить некоторые ресурсы файловой системы или сети до того, как сервлет выйдет из области действия. Метод destroy(), как и init(), вызывается только один раз в жизненном цикле сервлета.
public void destroy() {//}
Обычно в большинстве сценариев вам не нужно переопределять ни один из них в сервлете.
Если вы не очень любите конфигурации XML, а больше любите аннотации, то в Servlets API тоже есть что-то для вас. Вы можете использовать аннотацию @WebServlet, как в примере ниже, и тогда вам не нужно будет делать никаких записей в web.xml. Контейнер автоматически зарегистрирует ваш сервлет в среде выполнения и обработает его как обычно.
import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@WebServlet(name = "MyFirstServlet", urlPatterns = {"/MyFirstServlet"})public class MyFirstServlet extends HttpServlet {private static final long serialVersionUID = -1915463532411657451L;@Overrideprotected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException{//Do some work}@Overrideprotected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {//Do some other work}}
4. Упаковка и развертывание сервлета на сервере Tomcat
Если мы используем любую IDE(например, Eclipse), то упаковка и развертывание нашего приложения — это всего лишь один шаг. Щелкните правой кнопкой мыши по проекту > Запустить как > Запустить как сервер. Настройте сервер, если вы еще этого не сделали, и мы готовы к работе.
Если мы не используем никакую IDE, то нам нужно выполнить некоторую дополнительную работу, например, скомпилировать приложение из командной строки, использовать ANT для создания war-файла и т. д. Но я почти уверен, что в настоящее время все используют какую-либо IDE для разработки, поэтому я не буду тратить больше времени на этот раздел.
Когда вы развернете наш первый сервлет в tomcat и перейдете по URL-адресу «http://localhost:8080/servletexamples/MyFirstServlet» в браузере, вы получите следующий ответ.
5. Запись в ответ сервлета
Одна из причин, по которой сервлеты Java так полезны, заключается в том, что они позволяют отображать на веб-странице динамический контент. Контент может быть взят с самого сервера, базы данных, другого веб-сайта или любых других веб-доступных ресурсов. Сервлеты — это не статические веб-страницы; они динамичны, и это, возможно, их самая сильная сторона.
Давайте рассмотрим пример сервлета, который отвечает за отображение текущей даты и времени пользователю, а также его имени и некоторого пользовательского сообщения.
import java.io.IOException;import java.io.PrintWriter;import java.util.Date;import java.util.HashMap;import java.util.Map;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@WebServlet(name = "CalendarServlet", urlPatterns = {"/CalendarServlet"})public class CalendarServlet extends HttpServlet {private static final long serialVersionUID = -1915463532411657451L;@Overrideprotected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException{Map<String,String> data = getData();response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();try {// Write some contentout.println("<html>");out.println("<head>");out.println("<title>CalendarServlet</title>");out.println("</head>");out.println("<body>");out.println("<h2>Hello " + data.get("username") + ", " + data.get("message") + "</h2>");out.println("<h2>The time right now is : " + new Date() + "</h2>");out.println("</body>");out.println("</html>");} finally {out.close();}}//This method will access some external system as database to get user name, and his personalized messageprivate Map<String, String> getData(){Map<String, String> data = new HashMap<String, String>();data.put("username", "Guest");data.put("message", "Welcome to my world !!");return data;}}
Если вы запустите указанный выше сервлет в tomcat и перейдете по URL-адресу «http://localhost:8080/servletexamples/CalendarServlet» в браузере, вы получите следующий ответ.
6. Обработка запросов и ответов сервлета
Сервлеты позволяют легко создавать веб-приложения, которые придерживаются жизненного цикла запроса и ответа. Они способны предоставлять HTTP-ответы, а также обрабатывать бизнес-логику в том же теле кода. Возможность обрабатывать бизнес-логику делает сервлеты гораздо более мощными, чем стандартный HTML-код.
В реальных приложениях HTML-форма содержит параметры, которые отправляются сервлету. Затем сервлет обрабатывает эти параметры некоторым образом и публикует ответ, который может увидеть клиент. В случае объекта HttpServlet клиентом является веб-браузер, а ответом — веб-страница. Атрибут действия <form> указывает, что его следует использовать для обработки значений, содержащихся в форме.
Чтобы получить параметры запроса, вызовите метод getParameter() объекта HttpServletRequest, передав идентификатор входного параметра, который вы хотите получить.
После получения значений их можно обработать по мере необходимости. Затем готовится ответ для клиента, как мы обсуждали в предыдущих разделах. Этот ответ отправляется обратно клиенту с помощью объекта HttpServletResponse.
Базовое использование обработки запросов и ответов можно реализовать следующим образом:
Для отправки контента вам придется использовать объект PrintWriter, полученный из HttpServletResponse. Любой контент, записанный в него, будет записан в outputstream, а данные будут отправлены обратно клиенту.
7. Прослушивание событий контейнера сервлетов
Иногда полезно знать, когда в контейнере сервера приложений происходят определенные события. Эта концепция может быть полезна при самых разных обстоятельствах, но чаще всего она, скорее всего, будет использоваться для инициализации приложения при запуске или очистки после приложения при завершении работы. Прослушиватель сервлета может быть зарегистрирован в приложении, чтобы указать, когда оно было запущено или завершено. Таким образом, прослушивая такие события, сервлет имеет возможность выполнять некоторые действия, когда они происходят.
Чтобы создать слушателя, который выполняет действия на основе события контейнера, необходимо разработать класс, реализующий интерфейс ServletContextListener. Методы, которые необходимо реализовать, — это contextInitialized() и contextDestroyed(). Оба метода принимают ServletContextEvent в качестве аргумента и автоматически вызываются каждый раз, когда контейнер сервлета инициализируется или завершает работу соответственно.
Чтобы зарегистрировать прослушиватель в контейнере, можно использовать один из следующих методов:
1) Используйте аннотацию @WebListener. 2) Зарегистрируйте прослушиватель в дескрипторе развертывания приложения web.xml. 3) Используйте методы addListener(), определенные в ServletContext.
Обратите внимание, что ServletContextListener — не единственный листнер в API сервлета. Есть еще несколько, например
javax.servlet.ServletRequestListener
javax.servlet.ServletRequestAttrbiteListener
javax.servlet.ServletContextListener
javax.servlet.ServletContextAttributeListener
javax.servlet.HttpSessionListener
javax.servlet.HttpSessionAttributeListener
Они могут быть реализованы вашим классом-слушателем на основе выбора того, какие события вы хотите прослушивать; например, HttpSessionListener будет уведомляться каждый раз, когда создается или уничтожается новый сеанс пользователя.
8. Передача параметров в инициализацию сервлета
Большинству современных приложений необходимо задать некоторые параметры конфигурации, которые вы можете передать им при запуске приложения/контроллера. Сервлеты также могут получать параметры инициализации, которые они могут использовать для их полного построения перед обслуживанием своего первого запроса.
Разумеется, вы можете жестко закодировать значения конфигурации в самом сервлете, но изменение любого из них потребует повторной перекомпиляции всего приложения, а этого никто не захочет делать.
После установки параметр можно использовать в коде, вызвав getServletConfig().getInitializationParameter() и передав имя параметра, как показано в следующей строке кода:
String value = getServletConfig().getInitParameter("name");
9. Фильтры сервлетов
Веб-фильтры полезны для предварительной обработки запросов и вызова определенных функций при посещении заданного URL. Вместо того, чтобы напрямую вызывать сервлет, который существует по заданному URL, любой фильтр, содержащий тот же шаблон URL, будет вызван до сервлета. Это может быть полезно во многих ситуациях, возможно, наиболее полезно для выполнения регистрации, аутентификации или других служб, которые выполняются в фоновом режиме без взаимодействия с пользователем.
Фильтры должны реализовывать интерфейс javax.servlet.Filter. Методы, содержащиеся в этом интерфейсе, включают init(), destroy() и doFilter(). Методы init() и destroy() вызываются контейнером. Метод doFilter() используется для реализации задач для класса фильтра. Если вы хотите объединить фильтры в цепочку или если для заданного шаблона URL существует более одного фильтра, они будут вызваны в том порядке, в котором они настроены в дескрипторе развертывания web.xml.
Чтобы настроить файл web.xml для включения фильтра, используйте элементы XML <filter> и <filter-mapping> вместе с соответствующими тегами дочерних элементов.
Если вы хотите использовать аннотацию для настройки фильтров для определенных сервлетов, вы можете использовать аннотацию @WebFilter.
10. Загрузка двоичного файла с помощью сервлета
Загрузка файлов является важной задачей практически для любого веб-приложения. Чтобы загрузить файл, сервлет должен предоставить ответ того же типа, который соответствует типу загружаемого файла. Он также должен указать в заголовке ответа, что должно быть включено вложение, как показано ниже.
Вы можете получить ссылку на файл, который необходимо загрузить(хранить в файловой системе), вызвав метод ServletContext.getResourceAsStream() и передав путь к файлу. Это вернет объект InputStream, который можно использовать для чтения содержимого файла. Затем создается байтовый буфер, который будет использоваться для получения фрагментов данных из файла при его чтении.
Последняя реальная задача — прочитать содержимое файла и скопировать его в выходной поток. Это делается с помощью цикла while, который продолжит чтение из InputStream, пока все не будет обработано. Фрагменты данных считываются и записываются в выходной поток с помощью цикла. После этого вызывается метод flush объекта ServletOutputStream для очистки содержимого и освобождения ресурсов.
Иногда ваше приложение требует, чтобы сервлет передавал запросы другим сервлетам для завершения задачи, которую необходимо выполнить. Более того, запросы должны передаваться без перенаправления клиента на другой URL, т. е. URL в браузере не должен меняться.
Средства для этого встроены непосредственно в ServletContext, поэтому, получив ссылку на ServletContext, вы просто вызываете метод getRequestDispatcher(), чтобы получить объект RequestDispatcher, который можно использовать для отправки запроса.
При вызове метода getRequestDispatcher() передайте строку, содержащую имя сервлета, которому вы хотите передать запрос. После получения объекта RequestDispatcher вызовите его метод forward, передав ему объекты HttpServletRequest и HttpServletResponse. Метод forward выполняет задачу передачи запроса.
Хотя в некоторых сценариях вам не захочется уведомлять пользователя о том, что произошло перенаправление сервлета, как мы видели в предыдущем разделе, но в некоторых сценариях мы действительно хотим, чтобы это произошло. Вы хотите перенаправить браузер на другой URL, когда посещается определенный URL в вашем приложении.
Для этого вам необходимо вызвать метод sendRedirect() объекта HttpServletResponse.
httpServletResponse.sendRedirect("/anotherURL");
Это простое перенаправление, в отличие от цепочки сервлетов, не передает объект HttpRequest по целевому адресу.
13. Чтение/запись файлов cookie с использованием сервлетов
Многие приложения хотят сохранить текущее состояние истории просмотров пользователя на клиентской машине, чтобы при повторном возвращении пользователя в приложение он начал с того места, где остановился. Обычно для этого требования используются файлы cookie. Вы можете увидеть файлы cookie как данные на основе пар «ключ-значение», хранящиеся на клиентской машине. Когда приложение получает доступ в браузере, то приложение может читать или записывать эти значения.
Чтобы создать cookie, просто создайте новый объект javax.servlet.http.Cookie и присвойте ему имя и значение. После создания экземпляра cookie можно задать свойства, которые помогут настроить cookie. В примере этого рецепта вызываются методы setMaxAge() и setHttpOnly() cookie, устанавливающие время жизни cookie и гарантирующие, что он будет защищен от клиентских скриптов.
Начиная с API Servlet 3.0, появилась возможность пометить cookie как HTTP only. Это позволяет защитить cookie от клиентских скриптовых атак, что делает cookie более безопасным.
Cookie cookie = new Cookie("sessionId","123456789");cookie.setHttpOnly(true);cookie.setMaxAge(-30);response.addCookie(cookie);
Здесь response — это экземпляр HttpServletResponse, переданный методу doXXX().
Чтобы прочитать информацию cookie на сервере, используйте следующий код:
Cookie[] cookies = request.getCookies();for(Cookie cookie : cookies) {//cookie.getName();//cookie.getValue()}
Это все для этого руководства по технологии сервлетов. Не стесняйтесь оставлять комментарии/отзывы.