1. 概述
在这篇文章中,我们将探讨Java web开发的核心部分——Servlet。
2. Servlet与容器
简单来说,Servlet就是一个处理请求、处理这些请求并返回响应的类。
例如,我们可以使用Servlet通过HTML表单收集用户输入,查询数据库中的记录,并动态创建网页。
Servlet受一个名为Servlet容器的Java应用程序控制。当运行在Web服务器上的应用程序接收到请求时,服务器会将请求传递给Servlet容器,后者再将请求转发到目标Servlet。
3. Maven依赖
要在我们的web应用中添加Servlet支持,需要添加javax.servlet-api
依赖:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
最新的Maven依赖可以在这里找到。
当然,我们还需要配置一个Servlet容器来部署我们的应用;这里是如何在Tomcat上部署WAR文件的一个好起点。
4. Servlet生命周期
让我们来看看定义Servlet生命周期的一系列方法。
4.1. init()
init()
方法设计成只被调用一次。如果Servlet实例不存在,Web容器会:
- 加载Servlet类
- 创建Servlet类的实例
- 调用
init()
方法初始化它
init()
方法必须成功完成,Servlet才能接收任何请求。如果init()
方法抛出ServletException
或在Web服务器定义的时间范围内未返回,Servlet容器无法将Servlet投入服务。
public void init() throws ServletException {
// Initialization code like set up database etc....
}
4.2. service()
在init()
方法成功完成后,才会调用这个方法。
容器调用service()
方法来处理来自客户端的请求,解析HTTP请求类型(如GET、POST、PUT、DELETE等),然后根据需要调用doGet
、doPost
、doPut
、doDelete
等方法。
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
// ...
}
4.3. destroy()
由Servlet容器调用以将Servlet移出服务。
这个方法只在Servlet的service()
方法中的所有线程退出或超时后调用。容器调用此方法后,将不会再对Servlet调用service()
方法。
public void destroy() {
//
}
5. 示例Servlet
首先,为了将上下文根从javax-servlets-1.0-SNAPSHOT
更改为/,请在$CATALINA_HOME\conf\server.xml
的Host
标签下添加:
<Context path="/" docBase="javax-servlets-1.0-SNAPSHOT"></Context>
现在,让我们设置一个完整的示例,展示如何使用表单处理信息。
首先,定义一个映射为*/calculateServlet*的Servlet,它将捕获表单POST的信息并使用RequestDispatcher返回结果:
@WebServlet(name = "FormServlet", urlPatterns = "/calculateServlet")
public class FormServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String height = request.getParameter("height");
String weight = request.getParameter("weight");
try {
double bmi = calculateBMI(
Double.parseDouble(weight),
Double.parseDouble(height));
request.setAttribute("bmi", bmi);
response.setHeader("Test", "Success");
response.setHeader("BMI", String.valueOf(bmi));
request.getRequestDispatcher("/WEB-INF/jsp/index.jsp").forward(request, response);
} catch (Exception e) {
request.getRequestDispatcher("/WEB-INF/jsp/index.jsp").forward(request, response);
}
}
private Double calculateBMI(Double weight, Double height) {
return weight / (height * height);
}
}
如上所述,标记为@WebServlet
的类必须继承javax.servlet.http.HttpServlet
类。值得注意的是,@WebServlet
注解仅从Java EE 6开始可用。
@WebServlet
注解在部署时由容器处理,并在指定的URL模式下提供对应的Servlet。通过注解定义URL模式,我们可以避免使用名为web.xml
的传统部署描述符来映射Servlet。
如果我们希望不使用注解映射Servlet,可以使用传统的web.xml
:
<web-app ...>
<servlet>
<servlet-name>FormServlet</servlet-name>
<servlet-class>com.root.FormServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FormServlet</servlet-name>
<url-pattern>/calculateServlet</url-pattern>
</servlet-mapping>
</web-app>
接下来,创建一个基本的HTML表单:
<form name="bmiForm" action="calculateServlet" method="POST">
<table>
<tr>
<td>Your Weight (kg) :</td>
<td><input type="text" name="weight"/></td>
</tr>
<tr>
<td>Your Height (m) :</td>
<td><input type="text" name="height"/></td>
</tr>
<th><input type="submit" value="Submit" name="find"/></th>
<th><input type="reset" value="Reset" name="reset" /></th>
</table>
<h2>${bmi}</h2>
</form>
最后——为了确保一切按预期工作,我们也编写一个快速测试:
public class FormServletLiveTest {
@Test
public void whenPostRequestUsingHttpClient_thenCorrect()
throws Exception {
HttpClient client = new DefaultHttpClient();
HttpPost method = new HttpPost(
"http://localhost:8080/calculateServlet");
List<BasicNameValuePair> nvps = new ArrayList<>();
nvps.add(new BasicNameValuePair("height", String.valueOf(2)));
nvps.add(new BasicNameValuePair("weight", String.valueOf(80)));
method.setEntity(new UrlEncodedFormEntity(nvps));
HttpResponse httpResponse = client.execute(method);
assertEquals("Success", httpResponse
.getHeaders("Test")[0].getValue());
assertEquals("20.0", httpResponse
.getHeaders("BMI")[0].getValue());
}
}
6. Servlet、HttpServlet和JSP
重要的是要理解,Servlet技术并不局限于HTTP协议。
实际上,几乎总是如此,但Servlet
是一个通用接口,而HttpServlet
是该接口的扩展,增加了HTTP特定的支持,如doGet
和doPost
等。
最后,Servlet技术也是其他许多web技术的主要驱动,如JavaServer Pages (JSP),Spring MVC等。
7. 总结
在这篇简短的文章中,我们介绍了Java web应用中Servlet的基础知识。
示例项目可以直接下载并运行,作为GitHub项目:https://github.com/eugenp/tutorials/tree/master/web-modules/javax-servlets。