1.Servlet的生命周期
当我们说 servlet 具有生命周期时,只是指在调用 servlet 时,Servlet对象的创建及其中各个方法的调用,是按一定的规则被容器执行的。换言之,在任何 servlet 上创建的方法总是按相同的次序被调用的。servlet 生命周期由一系列事件组成,这些事件定义了 servlet 是如何加载和实例化、初始化,它如何处理来自客户机的请求,以及它是如何从服务器中删除的。
加载和实例化
对于 Web 应用程序的部署描述符中定义的每个 servlet,servlet 容器查找并加载 servlet 类型的类。当启动 servlet 引挚本身时,这可能发生,或者后来当实际把客户机请求委托给 servlet 时也可能发生这种情况。之后,它实例化一个或多个 servlet 类的对象实例,用于服务客户机请求。
初始化
在实例化后,在容器准备处理客户机请求前,它初始化一个 servlet。容器是通过下面方式初始化 servlet:调用它的 init() 方法,并传递实现 ServletConfig 接口的对象。在init()方法中,servlet 可以从部署描述符中读取配置参数,或者执行任何其他一次性活动,因此 servlet 容器有且仅有一次调用init()方法。
请求处理
在 servlet 被初始化后,容器就可以准备处理客户机请求。当客户机请求到来时,就通过 service() 方法,并把请求和响应对象作为参数传递,从而把请求委托给 servlet。在 HTTP 请求中,请求和响应对象分别是 HttpServletRequest 和 HttpServletResponse 的实现。在 HttpServlet 类中,service() 方法针对每种类型的 HTTP 请求,调用不同的处理程序方法:doGet() 方法用于 GET 请求,doPost() 方法用于 POST 请求,等等。
从服务中删除
出于各种原因(比如要节省内存资源),servlet 容器可能决定从服务中删除 servlet。为此,servlet 容器对 servlet 调用 destroy() 方法。调用 destroy() 方法后,servlet 不可能再服务其他的客户机请求了。现在 servlet 实例成为了垃圾收集对象。
2.Servlet的多线程模型
servlet 开发人员必须注意存储在不同作用域中的变量和属性上的多个线程的影响。
局部变量
局部变量始终是线程安全的,因为每个 servlet 对这些变量有自己的副本,因此它们不能用于在线程之间共享数据,因为它们的作用域被限制到声明它们的方法中。
实例变量
在多线程 servlet 模型中,实例变量不是线程安全的。在实现 SingleThreadModel 的 servlet 中,实例变量只由一个线程一次访问。
静态变量
静态变量从来都不是线程安全的。这些变量在类一级,因此它们在所有实例之间共享。因此,这些变量不是线程安全的,尽管 servlet 实现了 SingleThreadModel 接口。这就是为什么它们通常只用于存储常量/只读数据。
上下文作用域
ServletContext 对象由 Web 应用程序的所有 servlet 共享,因此,多个线程可以同时从该对象中设置和获取属性。在这种情形中,实现 SingleThreadModel 接口没有任何区别。因此上下文属性不是线程安全的。
会话作用域
HttpSession 对象由服务属于同一个会话的请求的多个线程共享,所以会话属性也不是线程安全的。就像上下文属性的情形,线程模型对这种行为没有影响。
请求作用域
ServletRequest 对象是线程安全的,因为它可以在 service() 方法中,以本地的方式访问,因此请求属性是安全的,与使用的线程模型无关。
当我们说 servlet 具有生命周期时,只是指在调用 servlet 时,Servlet对象的创建及其中各个方法的调用,是按一定的规则被容器执行的。换言之,在任何 servlet 上创建的方法总是按相同的次序被调用的。servlet 生命周期由一系列事件组成,这些事件定义了 servlet 是如何加载和实例化、初始化,它如何处理来自客户机的请求,以及它是如何从服务器中删除的。
加载和实例化
对于 Web 应用程序的部署描述符中定义的每个 servlet,servlet 容器查找并加载 servlet 类型的类。当启动 servlet 引挚本身时,这可能发生,或者后来当实际把客户机请求委托给 servlet 时也可能发生这种情况。之后,它实例化一个或多个 servlet 类的对象实例,用于服务客户机请求。
初始化
在实例化后,在容器准备处理客户机请求前,它初始化一个 servlet。容器是通过下面方式初始化 servlet:调用它的 init() 方法,并传递实现 ServletConfig 接口的对象。在init()方法中,servlet 可以从部署描述符中读取配置参数,或者执行任何其他一次性活动,因此 servlet 容器有且仅有一次调用init()方法。
请求处理
在 servlet 被初始化后,容器就可以准备处理客户机请求。当客户机请求到来时,就通过 service() 方法,并把请求和响应对象作为参数传递,从而把请求委托给 servlet。在 HTTP 请求中,请求和响应对象分别是 HttpServletRequest 和 HttpServletResponse 的实现。在 HttpServlet 类中,service() 方法针对每种类型的 HTTP 请求,调用不同的处理程序方法:doGet() 方法用于 GET 请求,doPost() 方法用于 POST 请求,等等。
从服务中删除
出于各种原因(比如要节省内存资源),servlet 容器可能决定从服务中删除 servlet。为此,servlet 容器对 servlet 调用 destroy() 方法。调用 destroy() 方法后,servlet 不可能再服务其他的客户机请求了。现在 servlet 实例成为了垃圾收集对象。
2.Servlet的多线程模型
servlet 开发人员必须注意存储在不同作用域中的变量和属性上的多个线程的影响。
局部变量
局部变量始终是线程安全的,因为每个 servlet 对这些变量有自己的副本,因此它们不能用于在线程之间共享数据,因为它们的作用域被限制到声明它们的方法中。
实例变量
在多线程 servlet 模型中,实例变量不是线程安全的。在实现 SingleThreadModel 的 servlet 中,实例变量只由一个线程一次访问。
静态变量
静态变量从来都不是线程安全的。这些变量在类一级,因此它们在所有实例之间共享。因此,这些变量不是线程安全的,尽管 servlet 实现了 SingleThreadModel 接口。这就是为什么它们通常只用于存储常量/只读数据。
上下文作用域
ServletContext 对象由 Web 应用程序的所有 servlet 共享,因此,多个线程可以同时从该对象中设置和获取属性。在这种情形中,实现 SingleThreadModel 接口没有任何区别。因此上下文属性不是线程安全的。
会话作用域
HttpSession 对象由服务属于同一个会话的请求的多个线程共享,所以会话属性也不是线程安全的。就像上下文属性的情形,线程模型对这种行为没有影响。
请求作用域
ServletRequest 对象是线程安全的,因为它可以在 service() 方法中,以本地的方式访问,因此请求属性是安全的,与使用的线程模型无关。