1 什么是Servlet?
servlet
是一个运行在Web
服务器中的小型Java
程序。servlet
接收并响应来自Web
客户端的请求,通常跨越HTTP
(超文本传输协议)。要实现这个接口,您可以编写一个扩展javax.servlet.GenericServlet
的通用servlet
,或者一个扩展javax.servlet.http.HttpServlet
的HTTP servlet
。这个接口定义了一些方法来初始化servlet
、服务请求以及从服务器中删除servlet
。这些方法称为生命周期方法,按以下顺序调用:构造servlet
,然后用init
方法初始化。从客户端到服务方法的任何调用都将得到处理。servlet
从服务中取出,然后使用destroy
方法销毁,然后垃圾收集并结束。除了生命周期方法之外,这个接口还提供了getServletConfig
方法,servlet
可以使用它来获取任何启动信息,以及getServletInfo
方法,getServletInfo
方法允许servlet
返回自身的基本信息,比如作者、版本和版权。
2 Servlet的入门程序和原理分析
- ① 首先创建一个
WEB
项目,具体参见Tomcat。 - ② 定义一个类,实现
Servlet
接口。
package com.hc.servlet;
import javax.servlet.*;
import java.io.IOException;
public class ServletDemo implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init running!");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service running!");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("destroy running!");
}
}
- ③
web.xml
中配置Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>servletDemo</servlet-name>
<servlet-class>com.hc.servlet.ServletDemo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo</servlet-name>
<url-pattern>/testServlet</url-pattern>
</servlet-mapping>
</web-app>
Servlet
的执行原理如下:
3 Servlet中的生命周期方法
看看入门程序的运行结果:
void init(ServletConfig config)
:由servlet
容器调用,以指示servlet
正在被放入服务中。servlet
容器在实例化servlet
之后恰好调用init
方法一次,且只调用一次。(servlet
的init
方法,只执行一次,说明一个servlet
在内存中只存在一个对象,servlet
是单例的)
void service(ServletRequest req, ServletResponse res)
:由servlet
容器调用,以允许servlet
响应请求。这个方法只有在servlet
的init
()方法成功完成后才会被调用。每次访问Servlet
时,Service
方法都会被调用一次。void destroy()
:由servlet
容器调用,以指示servlet
正在退出服务。这个方法只有在servlet
的服务方法中的所有线程都退出或经过一个超时时间之后才会被调用。在servlet
容器调用这个方法之后,它将不会在这个servlet
上再次调用服务方法。
注:getServletConfig
方法,servlet
可以使用它来获取任何启动信息,以及getServletInfo
方法,getServletInfo
方法允许servlet
返回自身的基本信息,比如作者、版本和版权。
4 注解配置Servlet
删掉servlet
在web.xml
中的配置,然后在类中配置注解:
package com.hc.servlet;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
//@WebServlet("/testServlet")
//@WebServlet("/servlet/testServlet") //多层路径
//@WebServlet({"/testServlet01", "/testServlet02", "/testServlet03"}) // 定义多个访问路径
//@WebServlet("testServlet01.do") // 扩展名匹配,不能有"/"
@WebServlet("*.do") // *表示任意以".do"结尾的路径访问,都将进入该servlet
public class ServletDemo implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init running!");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service running!");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("destroy running!");
}
}
5 Servlet的继承体系
实现Servlet
接口需要重写5个生命周期方法,但是在实际的开发中可能只涉及到service()
方法。因此,GenericServlet
接口将Servlet
接口中其他的方法做了默认空实现,只将service()
方法作为抽象方法,实现GenericServlet
接口就可以只重写service()
方法。然而,在处理浏览器传过来的请求时,单一的重写GenericServlet
接口中的service()
方法依旧需要处理大量的判断语句,HttpServlet
进一步对这些判断进行了封装,HttpServlet
类中的service()
方法源码如下:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}
if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
因此,HttpServlet
可以单独处理不同请求的服务,便于后端代码的书写。
6 HTTP传输协议
6.2 请求消息数据格式
下面分别展示了一个登录表单以GET
或POST
请求时的请求数据:
总结如下:
6.3 响应消息数据格式
响应体如下:
总结如下:
7 Request对象
7.1 Request对象的继承体系
当客户端向服务器端发送请求时,服务器为本次请求创建request
对象,并在调用Servlet
的service
方法时,将该对象传递给service
方法。Request
对象中封装了客户端发送过来的所有的请求数据。
7.2 Request功能
7.3 测试Request对象
在地址栏输入:http://localhost:8080/servletDemo01?username=hc&password=123456
package com.hc.servlet;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/servletDemo01")
public class ServletDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("do ServletDemo01");
System.out.println("======================");
String contextPath = req.getContextPath();
System.out.println(contextPath);
StringBuffer requestURL = req.getRequestURL();
System.out.println(requestURL.toString());
String header = req.getHeader("user-agent");
System.out.println(header);
System.out.println("======================");
String username = req.getParameter("username");
System.out.println(username);
System.out.println("======================");
req.setAttribute("age", 23);
req.getRequestDispatcher("/servletDemo02").forward(req, resp);
}
}
package com.hc.servlet;
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 java.io.IOException;
@WebServlet("/servletDemo02")
public class ServletDemo02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("do ServletDemo02");
System.out.println("======================");
String password = req.getParameter("password");
System.out.println(password);
int age = (int) req.getAttribute("age");
System.out.println(age);
}
}
8 Response对象
8.1 Response对象的继承体系
8.2 Response对象功能
8.3 测试Response对象
在地址栏输入:http://localhost:8080/servletDemo01
package com.hc.servlet;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/servletDemo01")
public class ServletDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("/servletDemo02");
// ServletOutputStream outputStream = resp.getOutputStream();
// outputStream.write("你好 World!".getBytes());
}
}
package com.hc.servlet;
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 java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/servletDemo02")
public class ServletDemo02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("do ServletDemo02");
System.out.println("======================");
String password = req.getParameter("password");
System.out.println(password);
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.write("<h1>你好 世界!</h1>");
}
}
结果如下:
注:地址栏变化了,request
对象不能共享数据了。