Servlet容器在启动一个Web应用时,会为它创建唯一的ServletContext对象。当Servlet容器终止一个Web应用时,就会销毁它的ServletContext对象。由此可见ServletContext对象与Web应用具有同样的生命周期。
ServletContext接口提供了一组在Web应用范围内存取共享数据的方法。“范围”在这里有两层含义:
1、表示一个特定时间段
2、表示在特定时间段内可以共享数据的所有Web组件的集合
Web应用范围具有以下两层含义:
1、表示由Web应用的生命周期构成的时间段
2、表示在Web应用的生命周期内的所有的Web组件的集合
存放在Web应用范围内的共享数据具有以下特点
1、共享数据的生命周期位于Web应用的生命周期中的一个时间片段内
2、共享数据可以被Web应用中的所有组件共享
由于ServletContext对象与Web应用具有同样长的生命周期,而ServletContext对象可以被Web应用中的所有Web组件共享,因此可以利用ServletContext对象来获取Web应用范围内的共享数据,基本思想如下:
1、面向对象编程的一个基本思想就是万物皆对象,因此,共享数据也理所当然地用java.lang.Object类型的任意Java对象来表示。
2、只要把代表共享数据的Java对象与ServletContext对象关联,该Java对象的生命周期就依附于ServletContext对象的生命周期,并且Web组件就可以通过ServletContext对象来访问它。实际上,该Java对象被存到Web应用范围内。
3、在Web应用范围内可以存放各种类型的共享数据。为了方便地存取特定的共享数据,可以把代表共享数据的Java对象作为ServletContext的属性来存到。每个属性包含一对属性名和属性值,属性值代表共享数据,属性名则用于标识共享数据。
在ServletContext接口中常用的用于存取共享数据的方法包括:
1、setArrribute(String name,Object object):向Web应用范围内存入共享数据。参数name指定属性名,参数Object指定共享数据
2、removeAttribute(String name):根据参数指定的属性名在Web应用范围内删除匹配的共享数据。
3、getAttribute(String name):根据参数给定的属性名,返回Web应用范围内匹配的共享数据。
在Web应用范围内存放共享数据的示例
许多网站都能统计特定网页被客户端访问的次数,JavaWeb应用就可以利用ServletContext来实现这一功能。
首先设计一个用于累计访问次数的计数器,用Counter类来实现。Counter类是一个普通的基于JavaBean风格的Java类。
public class Counter {
private int count;//计数值
public Counter(){
this(0);
}
public Counter(int count){
this.count=count;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public void add(int step){
count+=step;
}
}
接下来把计数器存放在Web应用范围内,每当网页被客户端访问一次,计数器就会递增一次。CounterServlet类实现了向应用范围内存取计数器的功能。
public class CounterServlet extends HttpServlet{
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获得ServletContext的引用
ServletContext context=getServletContext();
//从ServletContext中读取counter属性
Counter counter=(Counter) context.getAttribute("counter");
//如果ServletContext中没有counter属性,就创建counter属性
if(counter==null){
counter=new Counter(1);
context.setAttribute("counter", counter);
}
response.setContentType("text/html;charset=GB2312");
PrintWriter out=response.getWriter();
out.println("<html><head><title>CounterServlet</title></head>");
out.println("<body>");
//输出当前的counter属性
out.println("<h1>欢迎光临本站,您是第"+counter.getCount()+"位访问者。</h1>");
out.println("</body></html>");
counter.add(1);
out.close();
}
}
CounterClearServlet类负责将Web应用范围内的计数器删除。这个Servlet类没有什么实际意义,仅仅是为了更好的理解ServletContext的作用。
public class CounterClearServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获得ServletContext的引用
ServletContext context=getServletContext();
context.removeAttribute("counter");//删除counter属性
PrintWriter out=response.getWriter();
out.println("The counter is remove.");
out.close();
}
}
在web.xml文件中为CounterServlet和CounterClearServlet映射的URL分别为“/counter”和“/clear”。运行示例。
(1) 启动Tomcat,在浏览器中通过http://localhost:8080/helloapp/counter访问CounterServlet。在第一次访问该Servlet时,浏览器端显示计数器的值为1.
(2) 刷新上述访问CounterServlet的页面,会看到每刷新一次,计数器的值增加1,假定最后一次刷新后的计数器的值为5
(3) 另外再打开一个新的浏览器,也访问CounterServlet,此时计数器的值为6.
(4) 在浏览器中通过http://localhost:8080/helloapp/clear访问CounterClearServlet,然后再访问CounterServlet,浏览器端显示计数器的值为1
(5) 多次刷新上述访问CounterServlet的页面,会看到每刷新一次,计数器的值会增加1,假定最后一次刷新后的计数器的值为5
(6) 终止helloapp应用,在重新启动,然后再通过浏览器访问CounterServlet,计数器的值又会被初始化为1
(7) 在<CATALINA_HOME>/webapps目录下复制helloapp应用,改名为helloapp1,并在Tomcat中发布helloapp跟helloapp1这两个应用。在两个浏览器中分别多次通过以下URL访问helloapp应用及helloapp1应用中的CounterServlet。然后再通过浏览器
两个浏览器中的计数器的数值将各自独立地递增,由此可见,这两个Web应用拥有各自独立的计数器。
通过上述实验可以看出,在ServletContext中设置的属性,在Web应用运行期间一直存在,除非通过ServletContext的removeAttribute()方法将其删除。当Web应用被终止时,Servlet容器会销毁ServletContext对象,存储在ServletContext对象中的属性自然也不复存在。不同Web应用的ServletContext对象各自独立。
使用ServletContextListener监听器
在Servlet API中有一个ServletContextListener接口,它能够监听ServletContext对象的生命周期,实际上就是监听Web应用的生命周期。
当Servlet容器启动或终止Web应用时,会触发ServletContextEvent事件,该事件由ServletContextListener来处理。在ServletContextListener接口中定义了处理ServletContextEvent事件的两个方法。
1、contextInitialized(ServletContextEvent sce):当Servlet容器启动Web应用时调用该方法。在调用完该方法之后,容器再对Filter初始化,并且对那些在Web应用启动时就需要被初始化的Servlet进行初始化。
2、contextDestroy(ServletContextEvent sce):当Servlet容器终止Web应用时调用该方法。在调用该方法之前,容器会先销毁所有的Servlet和Filter过滤器。
public class MyServletContextListener implements ServletContextListener{
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("helloapp application is Destroyed");
//获得ServletContext对象
ServletContext context=sce.getServletContext();
//从web应用范围获得计数器对象
Counter counter=(Counter) context.getAttribute("counter");
if(counter!=null){
try{
//把计数器的数值写到count.txt文件中
String filepath=context.getRealPath("/count");
filepath=filepath+"/count.txt";
PrintWriter out=new PrintWriter(filepath);
out.println(counter.getCount());
out.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
public void contextInitialized(ServletContextEvent sce) {
System.out.println("helloapp application is Initialized.");
//获得ServletContext对象
ServletContext context=sce.getServletContext();
try{
BufferedReader reader=new BufferedReader(new InputStreamReader(context.getResourceAsStream("/count/count.txt")));
int count=Integer.parseInt(reader.readLine());
reader.close();
//创建计数器对象
Counter counter=new Counter(count);
//把计数器对象保存到web应用范围
context.setAttribute("counter", counter);
}catch(IOException e){
e.printStackTrace();
}
}
}