15 KiB
Платформа Java EE
- Набор стандартов и спецификаций для создания корпоративных приложений на Java
- Спецификации Java EE реализуются серверами приложений
- Apache Tomcat
- Sun/Oracle GlassFish
- BEA/Oracle WebLogic
- IBM WebSphere
- RedHat JBoss
Сервлеты
- Серверные сценарии написанные на Java
- Жизненным циклов сервлетов управляет веб-контейнер (он же контейнер сервлетов)
- В отличие от CGI запросы обрабатываются в отдельных потоках (а не процессах) на веб-контейнере
Преимущества
- Выполняются быстрее, чем CGI-сценарии
- Хорошая масштабируемость
- Надежность и безопасность
- Платформенно-независимы
- Множество инструментов мониторинга и отладки
Недостатки
- Слабое разделение уровня представления и бизнес-логики
- Возможны конфликты при параллельной обработке запросов
Обработка HTTP-запроса
- Браузер формирует HTTP-запрос и отправляет его на сервер
- Веб-контейнер создает объекты
HttpServletRequestиHttpServletResponse - Веб-контейнер вызывает метод
serviceу сервлета - Сервлет формирует ответ и записывает его в поток вывода
HttpServletResponse
Пример сервлета
package sample.servlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class SampleServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException {
String pageTitle = "Пример сервлета";
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
...
out.println("</html>");
}
}
Конфигурация сервлета
Конфигурирование происходит в WEB-INF/web.xml (есть конечно аннотации, но это слишком современно). В WEB-INF/classes должны лежать скомпилированные джава-классы
<!-- Регистрируем сервлет -->
<servlet>
<servlet-name>SampleServlet</servlet-name>
<servlet-class>org.itmo.sample.SampleServlet></servlet-class>
</servlet>
<!-- Маппинг -->
<servlet-mapping>
<servlet-name>SampleServlet</servlet-name>
<url-pattern>/sample</url-pattern>
</servlet-mapping>
Полный файл web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<!-- Регистрируем сервлет -->
<servlet>
<servlet-name>SampleServlet</servlet-name>
<servlet-class>org.itmo.sample.SampleServlet></servlet-class>
</servlet>
<!-- Маппинг -->
<servlet-mapping>
<servlet-name>SampleServlet</servlet-name>
<url-pattern>/sample</url-pattern>
</servlet-mapping>
</web-app>
Жизненный цикл сервлета
- Жизненным циклом управляет веб-контейнер
- Методы, управляющие жизненным циклом, должен вызывать только веб-контейнер
1. Загрузка класса сервлета
Проверяются пути: WEB-INF/classes/, WEB-INF/lib/*.jar, стандартные классы Java SE и классы веб-контейнера
<servlet>
<servlet-name>SampleServlet</servlet-name>
<servlet-class>org.itmo.sample.SampleServlet></servlet-class>
<!-- Этот параметр можно получить через унаследованный метод getInitParametr(paramName); -->
<init-param>
<param-name>say-hello</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
2. Создание экземпляра сервлета
Создание через конструктор new SampleServlet()
3. Вызов метода init()
init(config) - принимает config
4. Обработка HTTP-запросов
На каждый запрос в новом потоке (на самом деле там пул потоков) вызывается метод service()
5. Уничтожение сервлета
Вызов метода destroy()
Контекст сервлетов
- API, с помощью которого сервлет может взаимодействовать со своим контейнером
- Доступ к методам осуществляется через интерфейс
javax.servlet.ServletContext - У всех сервлетов внутри приложения общий контекст
- В контекст можно помещать общую для всех сервлетов информацию (методы
getAttributeиsetAttribute) - Если приложение - распределенное, то на каждом экземпляре JVM контейнером создается свой контекст
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class DemoServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter pw = res.getWriter();
ServletContext context = getServletContext();
String driverName = context.getInitParameter("dname");
pw.println("driver name: " + driverName);
pw.close();
}
}
В web.xml до сервлетов
<context-param>
<param-name>dname</param-name>
<param-value>org.postgresql.Driver</param-value>
</context-param>
HTTP-сессии
- Напомним, что HTTP - stateless
javax.servlet.HttpSession- интерфейс, позволяющий идентифицировать конкретного клиента (браузер) при обработке множества HTTP-запросов от него- Экземпляр
HttpSessionсоздается при первом обращении клиента к предложению и сохраняется некоторое (настраиваемое) время после последнего обращения - Идентификатор сессии либо помещается в
cookie, либо добавляется к URL. Если удалить этот идентификатор, то сервер создаст новую сессию - В экземпляр
HttpSessionможно помещать общую для этой сессии информацию (getAttributeиsetAttribute) - Сессия привязана к конкретному приложению, у разных приложений - разные сессии
- В распределенном окружении обеспечивается сохранение целостности данных в HTTP-сессии
public class SimpleSession extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
HttpSession session = request.getSession();
out.println("<html>");
...
out.println("session id: " + session.getId());
out.println("session timeout: " + session.getMaxInactiveInterval());
session.setMaxInactiveInterval(20*60);
out.println("new session timeout: " + session.getMaxInactiveInterval());
...
out.println("</html>");
}
}
Диспетчиризация запросов сервлетами
- Сервлеты могут делегировать обработку запросов другим ресурсам (сервлетам, JSP, HTML-страницам)
- Диспетчеризация осуществляется с помощью реализаций интерфейса
javax.servlet.RequestDispatcher - Два способа получения
RequestDispatcher- черезServletRequest(абсолютный или относительный URL) иServletContext(только абсолютный URL) - Два способа делегирования -
forwardиincludeforward- используетсяRequestDispatcher, сервер передает запрос другому сервлету или JSP-странице внутри себя, не говоря об этом браузеруinclude- используетсяresponse.sendRedirect(), сервер не передает запрос внутри себя, а говорит браузеру куда сходить (статус-код: 302), url в браузере меняется
public class MyServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
RequestDispatcher dispatcher = request.getRequestDispatcher("index.jsp");
dispatcher.forward(request, response);
}
}
Фильтры запросов
- Фильтры позволяют осуществлять пред- и постобработку запросов до и после передачи их ресурсу (сервлету, JSP или HTML-странице)
- Пример предобработки - допуск только авторизованных пользователей
- Пример постобработки - запись в лог времени обработки запроса
- Реализуют интерфейс
javax.servlet.Filter - Ключевой метод
doFilter - Метод
doFilterклассаFilterChainпередает управление следующему фильтру или целевому ресурсу, таким образом возможна реализация последовательностей фильтров
public class MyFilter implements Filter {
@Override
public void init(FilterConfig arg0) throws ServletException {}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
PrintWrite out = resp.getWriter();
out.print("filter is invoked before");
chain.doFilter(req, resp); // sends to next resource
out.print("filter is invoked after);
}
@Override
public void destroy() {}
}
Конфигурация похожа на конфигурацию сервлетов
<web-app>
<servlet>
<servlet-name>s1</servlet-name>
<servlet-class>HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>s1</servlet-name>
<url-pattern>/servlet1</url-pattern>
</servlet-mapping>
<filter>
<filter-name>f1</filter-name>
<filter-class>MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>f1</filter-name>
<url-pattern>/servlet1</url-pattern>
</filter-mapping>
</web-app>
[!important] Порядок сверху вниз, но если использовать аннотации, то порядок недетерминирован
Конфигурация аннотациями
Конфигурация можно писать прямо в Java коде. Tomcat сам сканирует классы и регистрирует компоненты
Сервлеты
@WebServlet - замена <servlet> и <servlet-mapping>
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
@WebServlet("/api/check")
public class CheckServlet extends HttpServlet {}
Продвинутый вариант
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.annotation.WebInitParam;
@WebServlet(
name = "MyCheckLogic", // Аналог <servlet-name>
urlPatterns = {"/api/check", "/run"},// Можно привязать сразу несколько URL!
loadOnStartup = 1, // Запустить при старте сервера
initParams = { // Замена <init-param>
@WebInitParam(name = "adminEmail", value = "admin@itmo.ru"),
@WebInitParam(name = "maxSize", value = "5000")
}
)
public class CheckServlet extends HttpServlet { ... }
Фильтры
@WebFilter - замена <filter> и <filter-mapping>
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.Filter;
@WebFilter(urlPatterns = "/*")
public class EncodingFilter implements Filter {}
Справка по параметрам/аттрибутам
Внешние данные от пользователя
getParameter()- Приходит от браузера/клиента по сети
- Возвращает
Stringилиnull - Находится в URL или в теле запроса
- Приоритет у первого query параметра
String user = request.getParameter("red");- Если несколько
String[] colors = request.getParameterValues("color"); - Никакого
setне существует
Внутренние данные приложения
getAttribute()иsetAttribute()- Записывается Java-кодом на сервере. Браузер ничего не знает
- Возвращает
Objectилиnull, поэтому требует явного каста - Внутри запроса
HttpServletRequest- живет 1 запрос- Используется для передачи из Model в View
request.setAttribute("myUser", new User()); User u = (User) request.getAttribute("myUser");
- Внутри сессии
HttpSession- живет пока юзер на сайтеHttpSession session = request.getSession();session.setAttribute("role", "ADMIN");String role = (String) session.getAttribute("role");
- Внутри контекста
ServletContext- живет пока работает серверServletContext context = getServletContext();context.setAttribute()иcontext.getAttribute()
Метаданные HTTP
getHeader()- Приходит от браузера в служебных строках
- Возвращает
Stringилиnull request.getHeader("Authorization");
Статические настройки из файла конфигурации
getInitParameter()- Читается сервером из файла
web.xmlили аннотаций - Возвращает
Stringилиnull String email = getServletConfig().getInitParameter("email");- для сервлетаString dbUrl = getServletContext().getInitParameter("dbUrl");- для всех сервлетов