Сервлет в вопросах и ответах

Что такое сервлет?

Сервлет - это Java интерфейс, реализация которого позволяет расширить функциональные возможности сервера. Для взаимодействия с сервлетом используется принцип запроса-ответа. Технология Java Servlet определяет HTTP-ориентированные сервлет-классы. Для создания сервлетов необходимо использовать пакеты javax.servlet и javax.servlet.http, включающие необходимые интерфейсы и классы.

Интерфейс javax.servlet.Servlet включает три главных метода и два вспомогательных метода :

void init (FilterConfig config) throws ServletException;
void destroy();
void service (ServletRequest request, ServletResponse response);

// вспомогательные методы
public ServletConfig getServletConfig()  
public String getServletInfo()  

Контейнер сервлетов

Контейнер сервлетов — это сервер, который обеспечивает системную поддержку сервлетов (жизненный цикл сервлета) согласно принятым правилам и спецификациями. Контейнер сервлетов может функционировать как самостоятельный веб-сервер и взаимодействовать с другим веб-сервером, например Apache.

Самые распространенные реализации контейнеров сервлетов : Apache Tomcat, GlassFish, Jetty, JBoss, IBM WebSphere, Oracle Weblogic.

GenericServlet и HttpServlet

Java Servlet API, кроме собственно интерфейсов, также содержит несколько классов сервлетов, которые могут служить основой. Базовым для всех этих классов является абстрактный класс javax.servlet.GenericServlet.

public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable

Как видно из определения класс GenericServlet включает все методы интерфейсов Servlet и ServletConfig. Не реализованным методом остается лишь абстрактный метод service. Метод service вызывается при каждом обращении к сервлету.

Пример сервлета First, наследующего свойства GenericServlet

// Пример сервлета
import java.io.IOException;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/First")
public class First extends GenericServlet
{
    private static final long serialVersionUID = 1L;
       
    public First()
    {
        super();
    }
    @Override
    public void service(ServletRequest request, ServletResponse response)
                        throws ServletException, IOException
    {}
}

Класс javax.servlet.http.HttpServlet наследует свойства GenericServlet.

public abstract class HttpServlet extends GenericServlet implements java.io.Serializable

HttpServlet предоставляет ещё больше удобств для программиста и имеет много полезных методов :

protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
                 throws ServletException, java.io.IOException
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
                 throws ServletException, java.io.IOException
protected void doHead(HttpServletRequest req, HttpServletResponse resp)
                 throws ServletException, java.io.IOException
protected void doOptions(HttpServletRequest req, HttpServletResponse resp) 
                 throws ServletException, java.io.IOException
protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
                 throws ServletException, java.io.IOException
protected void doPut(HttpServletRequest req, HttpServletResponse resp) 
                 throws ServletException, java.io.IOException
protected void doTrace(HttpServletRequest req, HttpServletResponse resp) 
                 throws ServletException, java.io.IOException
protected void service(HttpServletRequest req, HttpServletResponse resp) 
                 throws ServletException, java.io.IOException
protected void service(ServletRequest req, ServletResponse res)
                 throws ServletException, java.io.IOException

Интерфейсы ServletRequest и HttpServletRequest

Запрос от браузера поступает серверу в виде объекта типа HttpServletRequest. Этот интерфейс наследует свойства интерфейса ServletRequest. Методы интерфейса ServletRequest позволяют получать дополнительную информацию, в том числе и о сервлете и деталях протокола HTTP запроса :

  • String getCharacterEncoding() – определение символьной кодировки запроса;
  • String getContentType() – определение MIME-типа (Multipurpose Internet Mail Extension) пришедшего запроса;
  • String getProtocol() – определение названия и версии протокола;
  • String getServerName(), getServerPort() – определение имени сервера, принявшего запрос, и порта, на котором запрос был принят сервером соответственно;
  • String getRemoteAddr(), getRemoteHost() – определение IP-адреса и имени клиента, от которого пришел запрос;
  • String getRemoteUser() – определение имени пользователя, выполнившего запрос;
  • ServletInputStream getInputStream(), BufferedReader getReader() – получение ссылки на поток, ассоциированный с содержимым полученного запроса. Первый метод возвращает ссылку на байтовый поток ServletInputStream, а второй – на объект BufferedReader. В результате можно прочитать любой байт из полученного объекта-запроса. Если метод getReader() был вызван после вызова getInputStream() для этого запроса, то генерируется исключение IllegalStateException и наоборот.

При вызове сервлета (обращении к серверу), как правило, передаются параметры и их значения. Для разбора параметров и извлечения их значений применяются методы, описанные на странице Класс HttpServlet.

Интерфейсы ServletResponse и HttpServletResponse

Интерфейс ServletResponse предназначен для формирования и отправки данных клиенту. Все методы ServletResponse инструмента служат именно этой цели. Следующие методы позволяют получить ссылки на потоки вывода :

  • ServletOutputStream getOutputStream() – ссылка на поток ServletOutputStream для передачи бинарной информации;
  • PrintWriter getWriter() – ссылка на поток типа PrintWriter для передачи символьной информации.

Если метод getOutputStream() уже был вызван для этого ответа, то генерируется IllegalStateException.

Интерфейс HttpServletResponse наследует интерфейс ServletResponse и включает еще несколько полезных методов, которые описаны на странице Класс HttpServlet. На этой же странице представлен пример использования HttpServletResponse для формирования ответа клиенту.

Запуск сервлета

Контейнер сервлетов загружает сервлет при первом запросе клиента. В отдельных случаях, если сервлет объемный, необходимо его загрузить непосредственно на старте приложения, чтобы сократить время обработки запроса. Для этого следует использовать тег <load-on-startup> в дескрипторе приложения web.xml, который определяет необходимость загрузки сервлета при запуске.

<servlet>
    <servlet-name>servlet_name</servlet-name>
    <servlet-class>common.servlets.ServletName</servlet-class>
    <load-on-startup>3</load-on-startup>
</servlet>

Значение должно быть целочисленным. Если значение будет отрицательным, то сервлет будет загружен при запросе клиента, в остальных случаях сервлет загрузится на старте приложения.

Сервлетный фильтр

Согласно спецификации сервлетный фильтр представляет Java-класс, предназначенный для перехватывания запроса пользователя и позволяющий преобразовать содержание HTTP-запросов, HTTP-ответов и информацию, содержащуюся в заголовках HTML. Таким образом, сервлетный фильтр используется для предварительной обработки запроса и/или последующей обработки ответа сервлета.

Сервлетный фильтр должен реализовывать интерфейс javax.servlet.Filter, который определяет три метода :

void init (FilterConfig config) throws ServletException;
void destroy();
void doFilter (ServletRequest request,
               ServletResponse response, 
               FilterChain chain) throws IOException, ServletException;

В методе init() настраивается конфигурация фильтра. Метод doFilter выполняет основные функции фильтра. Контейнер сервлетов вызывает init() один раз, чтобы подготовить фильтр к работе, и вызывает doFilter() при поступлении запросов пользователя/браузера, которые фильтр должен перехватить. После того, как приложение заканчивает свою работу, вызывается метод destroy().

Сервлетный фильтр можно использовать для :

  • определения содержания запроса;
  • модификации заголовка и данных запроса;
  • модификации заголовка и данных ответа.

Сервлетный фильтр подключается в дескрипторе приложения web.xml. В следующем листинге определяется сервлетный фильтр hello, который может быть вызван при прямом обращении к нему. Пример использования данного фильтра можно посмотреть на странице Пример сервлета, hello!

<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>example.HelloWorld</servlet-class>
    <init-param>
        <param-name>name</param-name>
        <param-value>Остап</param-value>
    </init-param>
    <init-param>
        <param-name>mname</param-name>
        <param-value>Ибрагимович</param-value>
    </init-param>
</servlet>

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

Сервлетный фильтр может быть сконфигурирован так, что он будет работать с одним сервлетом, группой сервлетов или с определенной группой страниц. В следующем листинге дискриптора приложения web.xml фильтр FilterName подключается к сервлету ServletName и к страницам *.html.

<filter-mapping>
    <filter-name>FilterName</filter-name>
    <servlet-name>/ServletName</servlet-name>
</filter-mapping>

<filter-mapping>
    <filter-name>FilterName</filter-name>
    <url-pattern>*.html</url-pattern>
</filter-mapping>

Конфигурация сервлета, ServletConfig

Интерфейс javax.servlet.ServletConfig используется для передачи конфигурационной информации сервлету. Каждый сервлет имеет свой собственный ServletConfig, за создание которого отвечает контейнер сервлетов.

В следующем листинге в методе инициализации init в консоль выводятся параметры name и mname, определенные в дескрипторе приложения (см. пример выше).

@Override
public void init (ServletConfig config) throws ServletException  
{  
    System.out.println ("name = " + config.getInitParameter("name") +
                        "mname = " + config.getInitParameter("mname"));
}

Контекст сервлета, ServletContext

Для доступа из сервлета к параметрам WEB-приложения необходимо использовать интерфейс javax.servlet.ServletContext. Объект ServletContext является уникальным и доступен всем сервлетам.

ServletContext позволяет получить доступ к параметрам WEB-приложения, определенным в дескрипторе web.xml тегом <context-param> :

<context-param>
    <param-name>email</param-name>
    <param-value>admin@email.com</param-value>
</context-param>

Объект ServletContext можно получить с помощью метода getServletContext() интерфейса ServletConfig.

public void init (ServletConfig config) throws ServletException  
{  
    ServletContext sc = config.getServletContext();  
	System.out.println ("Email = " + sc.getInitParameter("email"));
}  

Интерфейс ServletContext определяет доступ к следующим функциям для работы с аттрибутами :

  • public Object getAttribute(String name)
  • public java.util.Enumeration getAttributeNames()
  • public void setAttribute(String name, Object object)
  • public void removeAttribute(String name)

Роль аттрибутов может выполнять объект любого класса. Цель данных функций связана с пересылкой между несвязанными друг с другом сервлетами разных объектов.

Отличия ServletConfig и ServletContext

  • Объект ServletConfig является уникальный для каждого сервлета, а ServletContext определен для всего приложения;
  • ServletConfig используется для получения параметров инициализации сервлета, а ServletContext для получения параметров инициализации приложения для всех сервлетов;
  • Объект ServletConfig не позволяет устанавливать параметры/атрибуты, в то время как их можно установить в объекте ServletContext, которые становятся доступными всем сервлетам.

То есть, можно сказать, что ServletConfig индивидуален для каждого сервлета, а ServletContext - для WEB-приложения и доступен всем сервлетам.

Интерфейс RequestDispatcher

Интерфейс RequestDispatcher используется для работы с дополнительными ресурсами, к которым относятся другой сервлет, страница JSP или документ HTML. Как правило, данный интерфейс используется для внутренней коммуникации между сервлетами в одном контексте. Доступ к RequestDispatcher можно получить с помощью метода getRequestDispatcher(String url) интерфейса ServletContext.

RequestDispatcher getRequestDispatcher(java.lang.String path) 

RequestDispatcher реализует два метода :

  • void forward(ServletRequest request, ServletResponse response) — передача запроса/управления другому ресурсу на сервере;
  • void include(ServletRequest request, ServletResponse response) — включение контента дополнительного ресурса в ответ.

Отличия методов forward() и sendRedirect()

Метод forward() интерфейса RequestDispatcher используется для передачи запроса другому ресурсу внутри сервлета. То есть действие выполняется в один шаг. Метод sendRedirect() интерфейса ServletResponse является двухшаговым. В этом методе WEB-приложение возвращает ответ клиенту со статусом кода 302 (redirect) и с ссылкой для отправки запроса. Браузер отправляет полностью новый запрос по полученной ссылке. То есть, forward() обрабатывается внутри контейнера, а sendRedirect() обрабатывается в браузере.

Для организации перехода внутри одного и того же приложения необходимо использовать forward(), т.к. данный метод реагирует быстрее, чем sendRedirect(), использующий дополнительный сетевой ресурс.

При использовании метода forward() адрес URL в строке остается прежним, т.к. браузер не знает о фактически обрабатываемом ресурсе. В методе sendRedirect() адрес URL изменяется на пробрасываемый ресурс.

  Рейтинг@Mail.ru