4.3.3 init和destroy方法
init和destroy方法分别在Servlet容器建立Servlet对象和销毁Servlet对象时调用。而且这两个方法只在Servlet的生命周期里调用一次。在Servlet接口中定义了这两个方法,在GenericServlet类中提供了这两个方法的默认实现。init方法有一个ServletConfig类型的参数,可以通过这个参数获得配置信息(也就是在web.xml文件中配置的内容),关于ServletConfig接口的内容将在4.4节详细介绍。destroy方法一般用于在Servlet对象被销毁时释放一些全局的资源,如数据库连接、网络资源等。
init方法和destroy方法的定义如下:
public void init(ServletConfig config) throws ServletException;
public void destroy();
有很多开发人员在编写Servlet类时往往直接覆盖了init方法来完成初始化工作。这么做一般没什么问题。但却将GenericServlet中init方法的默认实现覆盖了。先看看GenericServlet类中相关方法的实现代码:
public abstract class GenericServlet
implements Servlet, ServletConfig, java.io.Serializable
{
private transient ServletConfig config;
// 获得ServletConfig对象
public ServletConfig getServletConfig()
{
return config;
}
// 实现Servlet接口中的init方法
public void init(ServletConfig config) throws ServletException
{
this.config = config;
this.init();
}
// GenericServlet类中提供了空参数的init方法
public void init() throws ServletException
{
}
}
从上面的代码可以看出,带参数的init方法的第一行将config参数值赋给了类变量config。并且getServletConfig方法是通过config变量来返回ServletConfig对象的。如果开发人员覆盖了带参数的init方法,而又未调用super.init(config)语句,那么通过getServletConfig方法就无法再获得ServletConfig对象了,虽然开发人员也可以在Servlet类的init方法中将ServletConfig对象保存起来,但这实在是多此一举。当然,如果即不调用super.init(config)语句,也不保存ServletConfig对象,那么这个ServletConfig对象将会丢失,也就是说,在service、doXxx等方法中将无法获得并使用ServletConfig对象了。为了避免这个尴尬,在GenericServlet类中提供了一个不带参数的init方法,并且在带参数的init方法中调用该init方法,如上面的代码所示。
如果开发人员在Servlet类中覆盖这个不带参数的init方法,那么就仍然可以通过getServletConfig方法来获得ServletConfig对象。因此,我建议尽量在Servlet类中覆盖不带参数的init方法来初始化Servlet,如果要在init方法中使用ServletConfig对象,可以使用getServletConfig方法来获得ServletConfig对象。
下面的例子演示了init和destroy方法在Servlet的生命周期中的调用情况。
1. 实例说明
在本例中的Servlet类中覆盖了init和destroy方法。在第一次访问Servlet时,将调用init方法。然后通过重新发布Servlet的方式使Servlet引擎调用该Servlet类的destroy方法。然后再次访问这个Servlet,Servlet引擎再次调用init方法。最后通过停止Tomcat的方式使Servlet引擎再次调用destroy方法。也就是说,在本例中,Servlet引擎会调用两次init方法和两次destroy方法。
2. 编写Servlet类
本例中Servlet类的实现代码如下:
public class InitDestroyServlet extends HttpServlet
{
// 覆盖无参数的init方法
public void init() throws ServletException
{
System.out.println("init方法被调用!");
}
// 覆盖destroy方法
public void destroy()
{
System.out.println("destroy方法被调用");
}
// 处理客户端的HTTP请求
protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException
{
response.setContentType("text/html;charset=UTF-8");
response.getWriter().println("测试init和destroy方法");
}
}
下面的InitDestroyServlet类的配置代码:
<servlet>
<servlet-name>InitDestroyServlet</servlet-name>
<servlet-class>chapter4.InitDestroyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>InitDestroyServlet</servlet-name>
<url-pattern>/InitDestroyServlet</url-pattern>
</servlet-mapping>
3. 通过测试来观察init和destroy方法的调用情况
在IE地址栏中输入如下的URL:
http://localhost:8080/demo/InitDestroyServlet
这时在控制台中将输出“init方法被调用!”的信息。这时在Eclipse中稍微修改一下InitDestroyServlet类(可以加一个空格,并保存),这时Eclipse会自动重新发布Web应用,在控制台中就会输出“destroy方法被调用”的信息。在等待Web应用重新发布成功后,再次访问上面的URL,并且在Eclipse中停止Tomcat,这时,Servlet引擎会再次调用Servlet类的destroy方法。以上测试过程输出的信息如图4.4所示。
图4.4 init和destroy方法的调用情况
4. 程序总结
从本例可以看出,Servlet类的destroy方法会在Web应用重新发布时,或Web服务端(如Tomcat)停止时调用。