一、了解Servlet
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性, 是运行在 Web 服务器或应用服务器上的程序。和JDBC一样,Servlet同样是作为JavaEE规范的一项技术、接口、规范,他不能直接和客户端进行交互而是部署在一个容器中,像我们熟知的Tomcat容器,由这个容器来实现Servlet的接口,从而能和客户端进行交互。它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。主要功能在于收集来自网页表单的用户输入,交互式地浏览和生成数据,还可以生成动态Web内容。
二、Servlet的生命周期
在通过一个URL路径发起对一个Servlet请求的过程中,其本质是在调用执行Servlet实例的doXXX()方法。该Servlet实例创建和使用的过程,被称为Servlet的生命周期。整个生命周期包括:实例化、初始化、服务、销毁。
1. 加载和实例化
从JavaSE的角度出发,我们如果想要获取一个对象的实例,有2种方法,一种是构造方法,另一种是反射,但是反射的本质还是调用构造方法,所以我们想要获取一个Servlet的实例对象,主要是通过他的构造方法。那么我们再回到Servlet,Servlet容器负责加载和实例化Servlet。当Servlet容器启动时,或者在容器检测到需要这个Servlet来响应第一个请求时,它必须要知道所需的Servlet类在什么位置,通过类加载器加载Servlet类,成功加载后,容器通过反射的形式,调用无参构造方法来创建Servlet的实例。
2.初始化
在Servlet实例化之后,容器将调用Servlet的init()方法初始化这个对象。比如说,我们想要连接数据库,那么必然要与数据库建立连接,如果每调用一次Servlet就需要建立一次数据库连接的话,这对服务器端的压力是非常大的,所以我们只需要在创建该Servlet对象后,只建立一次与数据库的连接,而初始化的目的就是为了让Servlet对象在处理客户端请求前完成一些初始化的工作,而对于每一个Servlet实例,init()方法只被调用一次。
3.服务
在完成Servlet对象的创建以及初始化后,就该到了我们想要使用Servlet去干什么的阶段了,而这个阶段主要是调用service()方法来处理来自客户端的请求,目前经常使用的请求主要是GET和POST,当然也有其他的,比如HEAD、PUT等,那么在service方法中主要就是处理这些请求,比如doGet()、doPost()等,而这些方法的实现在HttpServlet父类中,都是返回给一个405状态码的响应,表示服务器不支持该方式的请求。
如果想要访问我们自定义的Servlet,到服务阶段:
如果子类没有重写service()方法,则调用HttpServlet父类的service()方法,在父类的该方法中进行请求方式的判断,如果是GET请求,则调用doGet()方法;如果是POST请求,则调用doPost()方法; 如果子类重写doXXX()方法,则调用子类重写后的doXXX()方法;如果子类没有重写doXXX()方法,则调用父类的doXXX()方法,在父类的方法实现中,返回一个405状态码的错误页面。
如果子类重写了service()方法,那么只执行子类的service()方法,doXXX()方法也不再执行。
4.销毁
当服务器停止或者重启时,也就代表一个Servlet的生命周期即将结束,调用实例的destroy()方法实现销毁,自此,Servlet会作为垃圾等待回收。
Servlet生命周期代码实现
package com.fulian.web.servlet;
import java.io.IOException;
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("/home.do")
public class HomeServlet extends HttpServlet {
// 1.实例化
// 构造方法
public HomeServlet() {
System.out.println("1.HomeServlet实例被创建");
}
// 2.初始化
// 重写HttpServlet父类的init()方法
// 通过实例自动调用init()方法
@Override
public void init() throws ServletException {
System.out.println("2.HomeServlet实例初始化");
}
// 3.服务
// 通过实例调用HttpServlet父类的service()方法
// service()方法中,会根据请求方式(get或post)的不同
// 调用子类重写的doGet()或doPost()
// 如果子类没用重写,则响应405
// 根据请求方式的不同,需要调用不同的请求方法
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet被单机了!" + this.hashCode());
String method = req.getMethod();
System.out.println("请求方式:" + method);
System.out.println(req.getRemoteAddr());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost被单机了!");
}
// 4.销毁
@Override
public void destroy() {
System.out.println("4.HomeServlet实例被销毁");
}
}
当发起多次对home.do的请求后,可以看到:
当服务器启动,发起对home.do的请求时,其生命周期开始,完成实例化、初始化和doGet()方法的调用,当第二次访问时,不再进行实例化和初始化,从hashcode可以看出,两次访问都是同一个HomeServlet实例对象,当服务器重启时,完成对HomeServlet的销毁。再次对home.do发起请求,创建并实例化一个新的HomeServlet实例对象,开始新的生命周期。