Servlet生命周期

第 14 章 生命周期

注意

讲一下servlet的生命周期与运行时的线程模型,对了解servlet的运行原理有所帮助,这样才能避免一些有冲突的设计。

如果你不满足以下任一条件,请继续阅读,否则请跳过此后的部分,进入下一章:第 15 章 分页

  1. 了解servlet的生命周期。

  2. 了解servlet运行时的线程模型,及设计程序时需要注意的部分。

14.1. 生命周期

我们之前使用的都是javax.servlet.http.HttpServlet,这个类实现了javax.servlet.Servlet接口,而这个接口中定义的三个方法是所有servlet都必须实现的。

package
 javax.servlet;

public interface Servlet {

void init(ServletConfig config);

void service(ServletRequest request, ServletResponse response);

void destroy();

}

如图所示,tomcat之类的服务器首先根据web.xml中的定义实例化servlet,然后调用它的init()方法进行初始 化,init()方法的ServletConfig参数是服务器传递进servlet的,其中包含web.xml配置的初始化信息和 ServletContext对象等共享内容。

初始化后的servlet实例便进入等待请求的状态,当有与servlet-mapping匹配的请求进入时,服务器会调用servlet实例的service方法,传入ServletRequest与ServletResponse两个参数等待servlet处理完毕。

注意一点,对于每个web应用,内存中只存在一个servlet实例,所有请求都是调用这个servlet实例,所以我们说 servlet不是线程安全的,所有操作都要限制在service()方法中进行,不要在servlet中定义类变量。(doGet()和 doPost()是HttpServlet覆盖service()方法后分支出来的辅助方法,实际上服务器调用的还是service()。)

当web应用卸载时,服务器会调用每个已经初始化的servlet的destroy(),然后销毁这些servlet实例,如果你需要在servlet销毁时释放什么资源的话,可以写在destory()方法中。

那么servlet是在什么时候进行初始化的呢?我们可以通过web.xml中的load-on-startup标签。

<servlet
>
<servlet-name >TestServlet</servlet-name >
<servlet-class >anni.TestServlet</servlet-class >
<load-on-startup >1</load-on-startup >
</servlet >

load-on-startup的值是一个整数,当它大于等于零的时候服务器会在web发布的时候初始化servlet。当它小于零或者我们没有设置load-on-startup的时候,服务器会在用户第一次访问servlet的时候才去初始化servlet。

或许你对load-on-startup为什么是一个整数存有疑问,为什么不是true和false呢?这是因为如果我们在 web.xml中设置了多个servlet的时候,可以使用load-on-startup来指定servlet的加载顺序,服务器会根据load- on-startup的大小依次对servlet进行初始化。不过即使我们将load-on-startup设置重复也不会出现异常,服务器会自己决定初 始化顺序。

回头看看javax.servlet.Filter中也有init()和destroy()方法,它的声明周期与servlet基 本一致,服务器使用init()对Filter初始化,销毁Filter的时候调用destroy()方法,只是过滤器就不在有load-on- startup设置了,它总是会在服务器启动的时候进行初始化,然后按照web.xml定义的顺序依次执行。

14.2. 线程模型

我们做一个试验,以此来证明某些编写servlet的方法是绝对错误的。

第一步,我们打开浏览器,浏览14-02的index.jsp页面,输入“叮咚”。

第二步,我们再打开一个14-02/index.jsp页面,输入“lingirl”。

第三步,点击第一个页面的提交按钮,然后在10秒之内点击另一个页面的提交按钮,等两个页面都提交成功后,我们会看到如下页面。

url上有乱码这个就是提交“叮咚”的页面,会惊讶吧?本来这时应该显示“叮咚”的。

这个页面对应提交“lingirl”的页面,它似乎是显示正常的。

到底是哪里出错了,为什么第一个页面提交了数据,却得到第二个页面提交的结果,首先让我们看一下TestServlet的代码。

package
 anni;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TestServlet extends HttpServlet {

private String username;

public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
this .username = request.getParameter("username" );

try {
Thread.sleep(10000);
} catch (InterruptedException ex) {
}

response.getWriter().write(this .username);
}

}

doGet()方法中从request中获得username参数,然后赋给this.username,这是一个类变量。然后暂 停10秒,这10秒我们假设正在进行一些很费时间的计算,这样我们就有十秒钟去点两个页面的提交按钮了。最后将this.username写入 response。

你也许在想:“这没有问题啊,第一个页面提交了数据,等待10秒返回,第二个页面再提交数据,等待10秒返回,两者并不冲突啊。”可实际上在多线程模型中不会有这种队列让请求一个一个执行,所有请求都是蜂拥而至。

在这个例子里,第一个请求过来将“叮咚”赋值给this.username后进行等待,10秒之内我们的第二个请求又调用了 doGet()方法,并把this.username修改为“lingirl”,等到10秒后第一个请求结束等待后,获得的this.username已 经是“lingirl”了。

this.username这种写法在servlet中是绝对禁用的,如果有什么信息需要保存,可以考虑放到session或ServletContext中。

14.3. 在jsp中定义类变量

写在<%%>之间的代码,在转换成servlet之后都会service()方法内运行,所以我们不必担心出现上边this.username的问题。

但是我们可以用<%!%>(注意多出来的感叹号)定义类变量或类方法,把上一个罪大恶极的servlet改造成jsp的话,就像这样。

<%@ page contentType="text/html; charset=gb2312"
%>
<%!
String username;
%>
<%
this .username = request.getParameter("username" );
try {
Thread.sleep(10000);
} catch (InterruptedException ex) {
}
out.write(this .username);
%>

注意

使用14-03下的例子可以测试jsp出错的效果,记得要在10秒之内点击两次。

<%!%>似乎是一个巨大的陷阱,如果我们使用它定义类变量就一定会出现多线程错误。

不过凡事都有正反两面,当我们需要在jsp中定义一个通用方法时,就需要借助<%!%>的力量了,假设我们需要一个方 法,根据用户的性别显示不同的html内容,如果sex = 0就输出红色的“男”,如果sex = 1就输出绿色的“女”。为实现这个功能,我们可以定义一个sexRenderer()方法。

14-04/index.jsp页面显示效果如下:

index.jsp中的代码分两部分。

第一部分定义sexRenderer()方法和

<%!
public String sexRenderer(int sex) {
if (sex == 0) {
return "<span style='color:red;'>男</span>" ;
} else if (sex == 1) {
return "<span style='color:green;'>女</span>" ;
} else {
return "" ;
}
}
%>

第二部分循环显示保存了性别信息的数组,显示的时候将会调用sexRenderer()方法。

<%
int [] people = {0, 1, 1, 0};
for (int i = 0; i < this .people.length; i++) {
%>
<tr>
<td><%=this .sexRenderer(this .people[i])%></td>
</tr>
<%
}
%>

好的,现在我们知道可以在<%!%>中定义方法和变量了。但是同时也要了解的是<%!%>已经脱离了 service()方法,这就导致不能在它里边使用request,response这些默认变量了,如果想要调用request只能写成void doSomething(HttpServletRequest request)的形式了,稍微注意一下即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值