文章目录
-
- 关于Servlet对象的生命周期
- 创建和销毁Servlet对象的流程
- 测试先后顺序
- 在服务器启动时就创建实例
- tip: init和无参构造的作用差不多, 为什么定义的规范是init()
关于Servlet对象的生命周期
我们都知道, 我们开发一个Servlet
程序的时候, 每一个类都要实现Servlet
接口, 然后实现其中的方法, 那我们的Servlet对象的声明周期是什么呢, 是在服务启动的时候就创建这个类, 还是在获得到请求该类的资源的时候才去创建这个类的对象...?
上面的问题我们等会去进行测试, 但是有几点我们是确定的
- 就是关于这些对象的声明周期, 本质上是由
Tomcat
服务器进行管理的(自己创建的Servlet对象Tomcat服务器不会管理) - 实际上底层是一个HashMap, 来管理每个类对应的实例, 每个类的实例都是用
Servlet容器
(其实就是Tomcat服务器)进行管理, 并且每一个类的实例都只有一个 - 虽然每个类的实例都只有一个, 但是这个类却不符合单例模式, 因为这些类也可以拥有一个public修饰的构造方法(下面会模拟), 实际上是一种
假单例的策略
创建和销毁Servlet对象的流程
下面是Servlet
接口中的几个方法

我们前面关于模拟一个Servlet的时候就说过,
我们底层是通过类名采用的反射机制获得该类的Class对象, 然后拿到无参数
的构造方法, 转换为Servlet
接口, 然后调用的service()
方法, 所以实际上关于Servlet
对象的创建和上面描述的过程差不太多 , 唯一新增的就是构造对象之后的init()初始胡对象
, 还有使用完毕对象的destroy()
方法...
大致流程如下

我们的service()
方法, 工作在init()和destroy()方法中间的那一档, 这也是我们最常用的方法
下面我们进行测试...
测试先后顺序
java
package com.qnn.servlet;
import jakarta.servlet.*;
import java.io.IOException;
public class HelloServlet implements Servlet {
// 添加一个无参的构造方法用来进行测试
public HelloServlet() {
System.out.println("HelloServlet constructor...");
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("HelloServlet init");
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Hello Servlet");
}
@Override
public void destroy() {
System.out.println("HelloServlet destroy");
}
@Override
public String getServletInfo() {
return "";
}
@Override
public ServletConfig getServletConfig() {
return null;
}
}
上面是我们的测试代码
我们现在启动Tomcat服务器

测试Tomcat服务器没有输出任何的日志信息, 可见我们启动Tomcat服务器, 默认情况下不会直接创建对象, 这也是合理的, 属于是节约资源的一种方法...
我们现在在浏览器中输入URL路径http://127.0.0.1:8080/myproject/world
(上节课的内容)
访问这个服务器, 结果如下
我们发现, Tomcat成功的输出了日志信息,
首先是调用了无参构造器的信息,
然后是调用初始化init方法的信息,
最后是调用service提供服务的信息
此时我们再次输入URL获取服务

会发现此时并没有重新创建对象, 而是由老对象提供服务, 其实也很好理解, 这其实也是一种节约资源的手段...
最后我们关闭服务器, 查看日志输出情况...

可以很明显看到, 服务器结束之前调用了destroy()
方法进行对象的销毁...
总结, 关于Servlet对象的默认声明周期如下
客户端请求服务->对象创建->持续提供服务->服务器结束对象销毁
其实, 这也是懒汉模式的另一种体现(比较的节约资源, 请求需要时只创建一次, 以一个实例持续提供服务)
在服务器启动时就创建实例
那我假如偏要在服务器启动的时候就创建对象呢?
此时我们需要在web.xml
文件中的servlet
标签下添加一个内容
xml
<load-on-startup>1</load-on-startup>
中间写的是一个整数(不考虑负数)
整数越小, 创建对象的优先级越高
添加后的web.xml
文件如下
xml
<?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>hello</servlet-name>
<servlet-class>com.qnn.servlet.HelloServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/world</url-pattern>
</servlet-mapping>
</web-app>
此时我们再次启动Tomcat服务器...

会发现, 这次在Tomcat服务器启动的时候就进行了对象的构造以及init()方法的调用
tip: init和无参构造的作用差不多, 为什么定义的规范是init()
思考, 假设我们定义一个有参数的构造方法会怎样呢?

启动服务器, 进行测试
虽然服务器可以正常的启动, 但是我们获取资源的时候, 会报出下面的错误提示

服务器端错误
也就是说, 不允许有带参数的构造方法
所以官方不建议我们手动的指定无参数的构造方法(因为你指不定哪天就写成带参数的构造方法了)
所以我们不建议明确的指定构造方法...也就是使用init进行初始化对象