Описание тега servlets
Сервлеты
Сервлет - это интерфейс прикладного программирования Java (API), работающий на сервере, который может перехватывать запросы, сделанные клиентом, и соответственно генерировать / отправлять ответ. Хорошо известный пример - HttpServlet
который предоставляет методы для перехвата HTTP- запросов с использованием популярных методов HTTP, таких как GET
а также POST
. Вы можете настроитьHttpServlet
s для прослушивания определенного шаблона URL-адреса HTTP, который настраивается в web.xml
, или совсем недавно с Java EE 6, с @WebServlet
аннотация. Многие веб-фреймворки Java EE построены на основе сервлетов, таких как JSF, JAX-RS, Spring MVC, Struts, Wicket и т. Д. См. Также В чем разница между JSF, Servlet и JSP?
Жизненный цикл
Когда сервлет запрашивается в первый раз или когда веб-приложение запускается, контейнер сервлета создает его экземпляр и сохраняет его в памяти в течение всего времени существования веб-приложения. Один и тот же экземпляр будет повторно использоваться для каждого входящего запроса, URL-адрес которого соответствует шаблону URL-адреса сервлета. Вы можете получить доступ к запрошенным данным по HttpServletRequest
и обработать ответ HttpServletResponse
. Оба объекта доступны как аргументы метода внутри любого из переопределенных методовHttpServlet
, Такие как doGet()
для предварительной обработки запроса и doPost()
для постобработки запроса. См. Также Как работают сервлеты? Создание экземпляров, сеансы, общие переменные и многопоточность.
Установка
Для запуска сервлетов вам необходимо:
- JDK (JRE достаточно, только если на сервере есть собственный компилятор).
- Контейнер сервлета.
- Необязательно, IDE с поддержкой Java EE (интегрированный редактор разработки).
Есть несколько контейнеров сервлетов.
Существуют также серверы приложений Java EE, которые, в свою очередь, также содержат контейнер сервлетов, помимо других API-интерфейсов Java EE, таких как JSF, JPA, EJB и т. Д. См. Также Что такое Java EE?
Для установки контейнера сервлетов обычно достаточно просто загрузить файл zip/gz и распаковать его в любом месте по вашему выбору.
Как правило, вы также хотели бы использовать среду IDE, такую как Eclipse, IntelliJ или Netbeans, поэтому вам не нужно вручную компилировать и создавать исходные файлы с помощьюjavac
вновь и вновь. У достойных IDE есть плагины для беспрепятственной интеграции контейнера сервлетов и импорта необходимых API Java EE в путь сборки проекта. См. Также Как импортировать API javax.servlet в мой проект Eclipse?
Hello World #1 (постобработка запроса)
Последующая обработка запроса, такая как отправка и проверка формы POST, является наиболее распространенным вариантом использования сервлета. Вaction
атрибут HTML <form>
может указывать на URL-адрес сервлета и method="post"
запустит сервлет doPost()
метод, в котором у вас есть полная свобода управления HTTP-запросом и ответом.
Предполагая, что в /WEB-INF/hello.jsp
что выглядит так...
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Servlet Hello World</title>
<style>.error { color: red; } .success { color: green; }</style>
</head>
<body>
<form action="hello" method="post">
<h1>Hello</h1>
<p>
<label for="name">What's your name?</label>
<input id="name" name="name" value="${fn:escapeXml(param.name)}">
<span class="error">${messages.name}</span>
</p>
<p>
<label for="age">What's your age?</label>
<input id="age" name="age" value="${fn:escapeXml(param.age)}">
<span class="error">${messages.age}</span>
</p>
<p>
<input type="submit">
<span class="success">${messages.success}</span>
</p>
</form>
</body>
</html>
(в fn:escapeXml()
заключается в защите вашей страницы от XSS при повторном отображении ввода, управляемого пользователем; если JSTL не работает, возможно, ваш контейнер сервлетов не поддерживает его из коробки (например, Tomcat); вы можете установить его, просто сбросивjstl-1.2.jar
в /WEB-INF/lib
, см. также информационную страницу JSTL)
... вот как com.example.controller.HelloServlet
класс должен выглядеть:
package com.example.controller;
import java.io.IOException;
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("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Preprocess request: we actually don't need to do any business stuff, so just display JSP.
request.getRequestDispatcher("/WEB-INF/hello.jsp").forward(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Postprocess request: gather and validate submitted data and display the result in the same JSP.
// Prepare messages.
Map<String, String> messages = new HashMap<String, String>();
request.setAttribute("messages", messages);
// Get and validate name.
String name = request.getParameter("name");
if (name == null || name.trim().isEmpty()) {
messages.put("name", "Please enter name");
} else if (!name.matches("\\p{Alnum}+")) {
messages.put("name", "Please enter alphanumeric characters only");
}
// Get and validate age.
String age = request.getParameter("age");
if (age == null || age.trim().isEmpty()) {
messages.put("age", "Please enter age");
} else if (!age.matches("\\d+")) {
messages.put("age", "Please enter digits only");
}
// No validation errors? Do the business job!
if (messages.isEmpty()) {
messages.put("success", String.format("Hello, your name is %s and your age is %s!", name, age));
}
request.getRequestDispatcher("/WEB-INF/hello.jsp").forward(request, response);
}
}
Скомпилируйте код и поместите его в /WEB-INF/classes
папка. В этом конкретном случае файл класса должен оказаться в/WEB-INF/classes/com/example/controller/HelloServlet.class
. IDE, такая как Eclipse, Netbeans или IntelliJ, сделает все это автоматически, когда вы создадите динамический веб-проект.
Обратите внимание, что @WebServlet
аннотация работает только с контейнерами, поддерживающими Java EE 6 / Servlet 3.0 (Tomcat 7, Glassfish 3, JBoss AS 6 и т. д.), и если /WEB-INF/web.xml
файл присутствует, то его <web-app>
Объявление root также должно соответствовать версии Servlet 3.0.
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
</web-app>
Если вы используете / ориентируетесь на более старую версию сервлета, такую как Servlet 2.5, вам следует удалить аннотацию и сопоставить сервлет в /WEB-INF/web.xml
файл следующим образом, который фактически делает то же самое:
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.example.controller.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
В любом случае, он в основном сообщает контейнеру сервлета, что он должен выполнять следующие действия:
HelloServlet helloServlet = new HelloServlet(); // Construct servlet.
helloServlet.init(servletConfig); // Initialize servlet with config.
helloServlet.init(); // Initialize servlet without config.
servlets.put("/hello", helloServlet); // Add to servlet mapping.
Разверните веб-приложение и перейдите по http://localhost:8080/contextname/hello (без расширения.jsp!), Чтобы открыть страницу Hello World. Когда вы открываете такую страницу, вводя URL-адрес в адресной строке или переходя по ссылке или закладке, затем HTTPGET
запрос будет запущен, и сервлет doGet()
будет вызван метод. Когда форма сmethod="post"
отправляется по URL-адресу сервлета, затем HTTP POST
запрос будет запущен, и сервлет doPost()
будет вызван метод.
Обратите внимание, что JSP помещен в /WEB-INF
папка, чтобы предотвратить прямой доступ к JSP, когда пользователь вводит его URL-адрес в адресной строке браузера. Это обязательно, когда требуется вызвать сервлетdoGet()
перед отображением JSP, например, когда необходимо предварительно загрузить некоторые данные.
Hello World #2 (предварительная обработка запроса)
Предварительная обработка запроса, такая как предварительная загрузка списка, который должен быть немедленно представлен в "обычном" запросе GET (который используется, когда вы переходите по ссылке / закладке или вводите URL-адрес в адрес браузера самостоятельно), является менее известным вариантом использования для сервлет. Хотя он также широко используется в реальном мире, средний учебник по базовым сервлетам, найденный в Интернете, вообще не объясняет этого. Однако это довольно просто: вам просто нужно реализовать бизнес-задачу вdoGet()
метод вместо в doPost()
.
Вот базовый начальный пример, в котором мы получаем список продуктов из базы данных, чтобы его можно было сразу представить, когда конечный пользователь открывает страницу продукта в интернет-магазине. ТолькоProductService
class в приведенном ниже примере - это еще один настраиваемый класс, не описанный в этой вики, поскольку он выходит за рамки области применения, но его list()
метод должен быть достаточно простым. В приведенном ниже примере предполагается, что это EJB, но это может быть что угодно, см. Также, например, этот пост.
package com.example.controller;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.example.business.ProductService;
import com.example.model.Product;
@WebServlet("/products")
public class ProductServlet extends HttpServlet {
@EJB
private ProductService productService;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Preprocess request: load list of products for display in JSP.
List<Product> products = productService.list();
request.setAttribute("products", products);
request.getRequestDispatcher("/WEB-INF/products.jsp").forward(request, response);
}
}
Вот как /WEB-INF/products.jsp
надо смотреть:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Our Products</title>
</head>
<body>
<h1>Products</h1>
<table>
<tr>
<th>ID</th>
<th>Name</th>
<th>Description</th>
<th>Price</th>
</tr>
<c:forEach items="${products}" var="product">
<tr>
<td>${product.id}</td>
<td><c:out value="${product.name}" /></td>
<td><c:out value="${product.description}" /></td>
<td><fmt:formatNumber value="${product.price}" type="currency" /></td>
</tr>
</c:forEach>
</table>
</body>
</html>
(в <c:out>
защищает вашу страницу от XSS при повторном отображении ввода, управляемого пользователем, он фактически делает то же самое, что иfn:escapeXml()
)
Разверните веб-приложение и перейдите по http://localhost:8080/contextname/products. (без расширения.jsp!). Это вызоветdoGet()
метод сервлета, который загружает продукты из БД, сохраняет их в области запроса и пересылает запрос / ответ для представления результатов.
Чтобы продвинуться дальше, вы можете фильтровать продукты на основе параметра запроса, который получается из формы поиска GET, как показано ниже:
<form action="products">
<input type="text" name="query" />
<input type="submit" value="Search" />
</form>
или гиперссылку (или закладку) следующего содержания:
<a href="products?query=java">Search for products with keyword "java"</a>
с
String query = request.getParameter("query");
List<Product> products = productService.find(query);
// ...
Так же работают поисковые системы вроде Google!
Стиль кодирования и рекомендации
НЕ звоните
doGet()
метод отdoPost()
метод или наоборот, или пусть они оба вызывают какой-нибудь другой общий метод, напримерprocessRequest()
. Это не верно. У каждого из этих двух HTTP-методов есть четкая ответственность: предварительная обработка или пост-обработка HTTP-запроса. Если вы собираетесь подключать все методы HTTP, вы должны переопределитьservice()
метод. См. Также шаблон переднего контроллера.НЕ выводите HTML в сервлет, используя
out.print()
заявления. Это только усложняет обслуживание. Код HTML относится к JSP, где вы можете писать HTML так, как вы хотите, без возни с методами Java и строками в кавычках. С другой стороны, НЕ используйте скриптлеты. (встроенный необработанный код Java) внутри файлов JSP. Это только усложняет обслуживание. Код Java относится к классам Java, в которых вы можете писать Java так, как вы хотите, не возясь с некрасивыми<% %>
вещи. См. Также Как избежать использования кода Java в файлах JSP?Не использовать
<jsp:useBean>
если вы уже используете сервлет для обработки модели. Это приведет только к путанице и проблемам с обслуживанием, потому что<jsp:useBean>
использует другой уровень подхода MVC, чем когда вы используете сервлеты. Это либо сервлеты, либо<jsp:useBean>
, не оба.
Характеристики
- Java EE 7: спецификация Java Servlet 3.1 (JSR 340)
- Java EE 6: спецификация Java Servlet 3.0 (JSR 315)
- Java EE 5: спецификация Java Servlet 2.5 (JSR 154)
Интернет-ресурсы и учебные пособия
- Официальная документация Java EE
- Википедия: Java-сервлеты
- ZEEF: сервлеты
- Руководства по Oracle Java EE
- http://www.coreservlets.com/ учебные пособия