Servlet入门3
Lifecycle 方法
3.1 重编Init 初始化方法
在初始化过程中, servlet应当准备好它要安排的一些资源, 以便这个servlet 能够接收请求,做到这些可以不用考虑多线程, 因为在servlet初始化是只能是单进程的。 一旦初始化方法完成, servlet就能接收客户端的请求。 当然如果初始化不能成功,这个方法会扔出throw UnavailableException解释的.
初始化方法使用ServletConfig 对象作为参数. 这个方法应该保存这个对象, 以便它能有方法getServletConfig返回. 最简单的办法是,搞出一个新类,他的初始化方法数调用super.init. 如果确实这样做, 你就应当自己保存ServletConfig 对象, 并且自己重编getServletConfig 方法以便它能从新的位置得到对象.
下面是个初始化方法的例子. 它是来自Survey Servlet的初始化方法, 从一个表单接收输入然后存储到文件中,为了存储survey信息, 它需要一个目录. 它以初始化参数接收这个目录.
public void init(ServletConfig config)
throws ServletException
{
super.init(config);
//获取目录
resultsDir = getInitParameter("resultsDir");
//如果没有目录, 不处理客户端
if (resultsDir == null) {
throw new UnavailableException (this,
"Not given a directory to write survey results!");
}
}
这里的初始化方法调用super.init 方法来管理安排ServletConfig对象. 这个初始化方法也设置了一个字段: resultsDir, 作为初始化参数提供的目录名. 如果没有目录名被提供, 这个 servlet扔出一个不适用的解释. 如果初始化 方法成功完成,servlet将能处理客户端请求
初始化参数
初始化参数的规定是一个服务器方面的规定。如果初始化参数被规定, 都可以用同样的方法得到: 用 getInitParameter方法. 这个方法将参数名作为自己的参数项.
3.2 重编Destroy 方法
当服务器卸载一个servlet, 它将调用servlet的destroy方法. 这个 destroy方法是与初始化方法相反,同时从内存中释放servlet.
并不是所有的调用了初始化init方法是也必须调用destroy方法.
对于大多数的servlets, 一些初始化的工作必须反做的. 如, 假设有一个servlet,它在初始化时打开一个数据库连接,他的destroy 方法如下显示:需要关闭这个连接的
/**
* 关闭数据库连接
*/
public void destroy() {
try {
con.close();
} catch (SQLException e) {
while(e != null) {
log("SQLException: " + e.getSQLState() + ‘\t‘ +
e.getMessage() + ‘\t‘ +
e.getErrorCode() + ‘\t‘);
e = e.getNextException();
}
} catch (Exception e) {
e.printStackTrace();
}
}
关于一个Servlet中断涉及的多线程
但一个服务器卸载一个servlet, 它会在所有的service已经完成后调用 destroy. 如果你的操作运行需要很长时间, 但destroy 被调用时还有线程在运行. 这个servlet编写者有责任确保所有的线程都已经完成;
长时间运行响应客户端请求的那些servlet应当保留当前有多少方法在运行的记录. 他的 long-running 方法应当周期性地轮询以确保他们能够继续运行下去. 如果servlet被destroy方法调用, 那么这个long-running 方法如果必要必须停止工作或清除.
举例, 变量serviceCounter用来统计有多少service方法在运行, 变量 shuttingDown显示这个servlet是否被destory. 每个变量有它自己的获取方法:
public ShutdownExample extends HttpServlet {
private int serviceCounter = 0;
private Boolean shuttingDown;
...
// serviceCounter
protected synchronized void enteringServiceMethod() {
serviceCounter++;
}
protected synchronized void leavingServiceMethod() {
serviceCounter--;
}
protected synchronized int numServices() {
return serviceCounter;
}
//shuttingDown
protected setShuttingDown(Boolean flag) {
shuttingDown = flag;
}
protected Boolean isShuttingDown() {
return shuttingDown;
}
}
这个service方法每次在它进入时要增加,而在它返回退出时要减少:
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
enteringServiceMethod();
try {
super.service(req, resp);
} finally {
leavingServiceMethod();
}
}
destroy方法应当检查serviceCounter, 如果存在长时间方式运行的话, 设置变量shuttingDown . 这个变量将会让那个正在处理请求的线程知道:该结束了,关闭吧! destroy 方法应当等待这几个service 方法完成, 这样就是一个清楚的关闭过程了.
public void destroy() {
/* 检查是否有线程在运行,如果存在,告诉他们stop. */
if (numServices() > 0) {
setShuttingDown(true);
}
/* 等待他们stop. */
while(numService() > 0) {
try {
thisThread.sleep(interval);
} catch (InterruptedException e) {
}
}
}
long-running 方法如必要应当检查这个变量,并且解释他们的工作:
public void doPost(...) {
...
for(i = 0; ((i < lotsOfStuffToDo) && !isShuttingDown()); i++) {
try {
partOfLongRunningOperation(i);
} catch (InterruptedException e) {
}
}
}
3.3 提供关于Servlet的信息
/**
* This is a simple example of an HTTP Servlet. It responds to the GET
* and HEAD methods of the HTTP protocol.
*/
public class SimpleServlet extends HttpServlet {
...
public String getServletInfo() {
return "A simple servlet";
}
}