[理论知识]
我们在学习JavaEE企业级开发的初期,经常会遇到有关于Servlet生命周期的问题,这方面的理论知识网上有详尽的介绍,但是光理解理论概念以及Servlet生命周期的执行顺序是不够的。
[步骤解读一]Servlet生命周期
小博老师先为大家演示一下基本Servlet的生命周期,核心代码如下:
@WebServlet("/BWF01")
public class BWF01Servlet extends HttpServlet {
public BWF01Servlet(){
System.out.println("BWF01 -> constructor() has been called!");
}
public void init() throws ServletException {
System.out.println("BWF01 -> init() has been called!");
super.init();
}
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
System.out.println("BWF01 -> service() has been called!");
super.service(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("BWF01 -> doGet() has been called!");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("BWF01 -> doPost() has been called!");
}
public void destroy() {
System.out.println("BWF01 -> destroy() has been called!");
super.destroy();
}
}
开启服务器,第一次访问该Servlet后的执行顺序如下:
BWF01 -> constructor() has been called!
BWF01 -> init() has been called!
BWF01 -> service() has been called!
BWF01 -> doGet() has been called!
但是当我们后续再访问该Servlet时,执行顺序则只剩下:
BWF01 -> service() has been called!
BWF01 -> doGet() has been called!
由此可见,Servlet是单例的,只有在第一次被访问时会实例化对象,并且调用init()初始化参数方法,访问后对象没有被销毁(不调用destroy()方法)。只有当服务器关闭时,会调用destroy()方法销毁Servlet对象。
[步骤解读二]Servlet的实现是单例、多线程
接下来我们修改Servlet中的doGet()方法,核心代码如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("BWF01 -> doGet() has been called!");
System.out.println("BWF01 -> threadId = " + Thread.currentThread().getId());
}
通过浏览器多次访问该Servlet,发现每一次的threadId都是不相同的。这说明服务器对于多次客户端请求同一个Servlet,会创建一个子线程来处理,这些子线程共享同一个Servlet对象。
[步骤解读三]Servlet线程安全
我们再次修改此Servlet中的doGet()方法,核心代码如下:
private int times = 0; // 累加访问次数
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("BWF01 -> doGet() has been called!");
System.out.println("BWF01 -> times = " + times);
}
我们打开多个浏览器,多次访问该Servlet,发现不同的客户端浏览器访问Servlet时,成员属性times的值会累加。这是因为Servlet是单例的,虽然多个客户端每次访问,会在不同的线程中处理,但是Servlet的对象只有一份,因此成员属性也只有一份。由此可见对于Servlet来说,成员属性时线程不安全的,所以我们想要实现线程安全的数据存储,建议采用HttpServletRequest对象、HttpSession对象等。