Java Servlet
什么是Java servlet
Java Servlet,称为小服务程序或服务连接器(简称Servlet),是一个实现了servlet接口的普通java类,在于交互式地浏览和修改数据,生成动态Web内容。
Servlet的主要作用是
- 接受请求
- 处理请求
- 生成动态的web内容。
狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
最早支持Servlet标准的是JavaSoft的Java Web Server,此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。
Servlet的工作模式
- 客户端发送请求至服务器
- 服务器启动并调用 Servlet,Servlet 根据客户端请求生成响应内容并将其传给服务器
- 服务器将响应返回客户端
Servlet的API
- void init(ServletConfig config):初始化方法
- void service(ServletRequest request,ServletResponse response):服务方法
- void destroy():销毁方法
- ServletConfig getServletConfig():获取当前servlet的配置对象
- String getServletInfo():获取当前servlet的信息
Servlet的生命周期
Servlet接口中void init(ServletConfig config),void service(ServletRequest request,ServletResponse response),void destroy()三个方法其实就是Servlet的生命周期函数,分别代表“出生”,“工作”,“死亡”。
我们用下面代码来看看Servlet的生命周期:
public class Demo1Servlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("Servlet正在初始化");
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
//专门向客服端提供响应的方法
System.out.println("Servlet正在完成业务逻辑");
}
@Override
public void destroy() {
System.out.println("Servlet正在销毁");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
我们配置好xml中映射关系,打开服务器,第一次访问该Servlet时,控制台打印出了下面的信息:
在我们刷新4遍浏览器,控制台信息变成了这样:
当我们关闭服务器时,控制台打印出了如下信息:
Servlet这时终于销毁了,这就是一个Servlet的完整生命周期。
用一句话说明Servlet的生命周期也就是:默认第一次请求来的时候,服务器创建servlet的对象,且调用init方法实现初始化操作,且调调用一次service方法,每当请求来的时候,服务器获取一个线程,调用service方法,完成具体的业务逻辑(编写的代码)当servlet被移除的时候或者服务器正常关闭的时候,服务器调用destroy方法实现销毁操作。
GenericServlet 抽象类
我们前面讲了Servlet接口,但当我们每次使用时那么面有些不方便:不仅有一堆方法需要实现,还需要自己手动的维护ServletConfig这个对象的引用,那么有什么办法可以解决除了这些问题呢?
这时候就要介绍一个新的类了,他就是GenericServlet抽象类,他除了service没有实现,其他的方法都实现了。
下面我们来看看GenericServlet抽象类的原码:
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.LocalStrings");
private transient ServletConfig config;
public GenericServlet() {
}
public void destroy() {
}
public String getInitParameter(String name) {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getInitParameter(name);
}
}
public Enumeration<String> getInitParameterNames() {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getInitParameterNames();
}
}
public ServletConfig getServletConfig() {
return this.config;
}
public ServletContext getServletContext() {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getServletContext();
}
}
public String getServletInfo() {
return "";
}
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
public void log(String msg) {
this.getServletContext().log(this.getServletName() + ": " + msg);
}
public void log(String message, Throwable t) {
this.getServletContext().log(this.getServletName() + ": " + message, t);
}
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
public String getServletName() {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getServletName();
}
}
}
由于它也不是我们的重点,所以我们不再过多讲解。
HttpServlet 抽象类
有人会想GenericServlet抽象类已经很方便了,那为什么不用呢?
锵锵锵,那是因为我们的主角,HttpServlet抽象类的功能更为强大,它到底强大在哪些方面呢,下面我将一一列出。
首先我们看一下HttpServlet的声明:
public abstract class HttpServlet extends GenericServlet implements Serializable
很明显,它继承了GenericServlet抽象类,并实现了Serializable接口。自然也就继承了GenericServlet所拥有的优点。
HttpServlet比GenericServlet强大的地方主要有以下两点:
- 强转了两个参数HttpServletRequest和HttpServletResponse,调用了重载的service()方法
- 获取请求方式,根据请求方式的不同调用不同的doXxx()方法
要理解第二条,首先我们来看一下HttpServlet如何实现service() 方法的:
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest)req;
response = (HttpServletResponse)res;
} catch (ClassCastException var6) {
throw new ServletException("non-HTTP request or response");
}
this.service(request, response);
}
可以看到,HttpServlet抽象类中,接收到ServletRequest 和ServletResponse对象时,将其强转为HttpServletRequest和HttpServletResponse类型,之所以能够这样强制的转换,是因为在调用Servlet的Service方法时,Servlet容器总会传入一个HttpServletRequest对象和HttpServletResponse对象,预备使用HTTP。
之后有又调用参数为HttpServletRequest和HttpServletResponse类型的service()方法,那么这个方法又是如何实现的呢?
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);
}
}
可以看出在service方法中还是没有实现任何的服务逻辑,但是却依据HttpServletRequest中的方法参数,调用以下方法之一:doGet(),doPost(),doHead(),doPut(),doTrace(),doOptions()和doDelete()。这7种方法中,每一种方法都表示一个Http方法。doGet()和doPost()是最常用的。所以,如果我们需要实现具体的服务逻辑,不再需要覆盖service()方法了,只需要覆盖doGet()或者doPost()就好了。