javaee2020-教程直播-19详解ServletContext组件

0 业务问题引入

0.1 提两个问题

1.大家在访问某个网站的时候,往往都会看到网站的首页面显示您是第几位浏览者(网站计数器),这是怎么实现的?
2.我们在访问某个bbs网站的时候,往往会显示有多少人在线,这是怎么实现的?

可能我们会想到的常规实现思路:数据库或者文件。这种做法比较简单,但是却会对数据库或者文件访问过于频繁,开销比较大。

解决之道是用ServletContext

0.2 要理解ServletContext就必须和Cookie、Session做一个对比

在这里插入图片描述

  • 可以把它想象成一个公用的空间,可以被所有的客户访问,也就是说A客户端可以访问D,B客户端可以访问D,C客户端也可以访问D。
  • WEB容器在启动时,它会为每个Web应用程序都创建一个对应的ServletContext,它代表当前Web应用。并且它被所有客户端共享。
  • ServletContext对象可以通过ServletConfig.getServletContext()方法获得对ServletContext对象的引用,也可以通过this.getServletContext()方法获得其对象的引用
  • 由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。公共聊天室就会用到它。
  • 当web应用关闭、Tomcat关闭或者Web应用reload的时候,ServletContext对象会被销毁

1 ServletContext接口

该接口提供了一个全局的储存信息的空间,服务器开始,其就存在,服务器关闭,其才释放。

  • 对一个用户而言,一个用户只有一个ServletContext对象;【全部应用程序范围,最大】
  • 对一个用户而言,一个用户只有一个session对象;【该用户的会话范围,适中】
  • 对一个用户而言,一个用户可以拥有多个request对象;【用户一次会话中的一个请求的范围,最小】
  • 所以,为了节省空间,提高效率,ServletContext中,要放必须的、重要的、所有用户需要共享的线程又是安全的一些信息。

2 理解ServletContext对象

  • 换一种方式说吧,运行在JAVA虚拟机中的每一个Web应用程序都有一个与之相关的Servlet上下文
  • 站在服务器端的角度来看,是对应的一个应用程序,也就是初学者眼中的一个web项目。
  • ServletContext对象是Web服务器中的一个已知路径的根,Servlet上下文被定位于
  • http://localhost:8080/项目名.
  • 以 /项目名 请求路径(称为上下文路径)开始的所有请求被发送到与此ServletContext关联的Web应用程序。
  • 一个ServletContext对象表示了一个Web应用程序的上下文。

3 Servlet上下文

  • Servlet上下文提供对应用程序中所有Servlet所共有的各种资源和功能的访问。
  • Servlet上下文API用于设置应用程序中所有Servlet共有的信息。
  • Servlet可能需要共享他们之间的共有信息
    运行于同一服务器的Servlet有时会共享资源,如JSP页面、文件和其他Servlet。

4 业务举例

比如,做一个购物类的网站,要从数据库中提取物品信息,如果用session保存这些物品信息,每个用户都访问一便数据库,效率就太低了;所以要用来Servlet上下文来保存,在服务器开始时,就访问数据库,将物品信息存入Servlet上下文中,这样,每个用户只用从上下文中读入物品信息就行了。这肯定比从缓存或是库中提取对应的数据效率要高一些。

5 ServletContext接口详解

5.1 ServletContext接口简介

ServletContext接口定义了运行servlet的web应用的servlet视图。容器供应商负责提供servlet容器内ServletContext接口的实现。使用ServletContext对象,servlet可以记录事件日志,获取资源的URL地址,并且设置和保存上下文内可以访问的其他servlet的属性。

ServletContext以web的已知路径为根路径。比如,假定一个servlet上下文位于http://www.mycorp.com/catalog。以/catalog请求路径开头的所有请求,已知为上下文路径,被路由到和该ServletContext关联的web应用。

5.2 ServletContext接口作用域

容器中部署的每一个web应用都有一个ServletContext接口的实例对象与之关联。如果容器被分布在多个虚拟机上,一个web应用将在每一个VM中有一个ServletContext实例。

不作为web应用一部分部署的容器中的servlet默认是“默认”web应用的一部分,有一个默认的ServletContext。在分布式容器中。默认ServletContext是非分布式的,并且必须只存在于一个VM中。

5.3 初始化参数

ServletContext接口的初始化参数允许servlet访问与web应用相关的上下文初始化参数,这些由应用开发人员在部署描述符中指定:

getInitParameter

getInitParameterNames

应用开发人员利用初始化参数传送配置信息。典型的例子是web管理员的e-mail地址或者一个持有关键数据的系统名称。

5.4 上下文属性

servlet可以通过名称将对象属性绑定到上下文。任何绑定到上下文的属性可以被同一个web应用的其他servlet使用。ServletContext接口的下列方法允许访问这种功能:

setAttribute

getAttribute

getAttributeNames

removeAttribute

5.4.1 分布式容器中的上下文属性

上下文属性对于创建它们的VM来说是本地的。这防止ServletContext属性存储于分布式容器的共享内存中。当信息需要在运行于分布式环境中的servlet之间共享时,信息被放入会话中(参见第7章“会话”),存储于数据库中,或者存储于EJB组件中。

5.5 资源

ServletContext接口通过下列方法提供对web应用组成的静态内容文档层级的直接访问,包括HTML,GIF和JPEG文件:

getResource

getResourceAsStream

getResource和getResourceAsStream方法以“/”开头的字符串为参数,它指定上下文根路径的资源相对路径。文档的层级可能存在于服务器的文件系统,war文件,远程服务器或者在一些其它位置中。

这些方法不用来获取动态内容。比如,在一个支持JSP规范1的容器中,getResource("/index.jsp")这种形式的方法调用将返回JSP源代码,而不是处理后的输出。关于访问动态内容的更多信息参见第8章“转发请求”。

Web应用资源的完整列表可以使用getResourcePaths(String path)方法访问。该方法语义的完整信息可以在本规范的API文档中找到。

5.6 多个主机和ServletContext

Web服务器可能支持一个服务器上多个逻辑主机共享一个IP地址。这功能有时被称为“虚拟主机”。这种情况下,每一个逻辑主机必须有它自己的servlet上下文或者servlet上下文组。Servlet上下文不可以被多个虚拟主机共享。

5.7 重载考虑

尽管容器供应商因为对于易于开发而实现的类加载不做要求,但是任何那样的实现必须确保所有它们可能使用2的所有servlet和类,被加载在单个类加载器作用域内。必须保证应用应该如开发人员预想的那样运转。作为开发辅助,绑定监听器的会话通知的完整语义应当由容器支持,在类加载上会话终止的监听上使用。

上一代的容器创建新的类加载器以加载servlet,这和用来加载servlet上下文中使用的其他servlet或者类的类加载器不同。这可能造成servlet上下文内的对象引用指向一个意想不到的类或对象,造成意想不到的行为。需要阻止由新一代类加载器所引发的问题。

5.7.1 临时工作目录

每一个servlet上下文都需要一个临时存储目录。Servlet容器必须为每一个servlet上下文提供一个私有的临时目录,并且使它可以通过javax.servlet.context.tempdir上下文属性可用。这些属性关联的对象必须是java.io.File类型。

这项需求认可了很多servlet引擎实现中提供的常见便利。容器不需要在servlet重启时维持临时目录的内容,但是需要确保一个servlet上下文的临时目录的内容对于该servlet容器上运行的其他web应用的servlet上下文不可见。

6 使用ServletContext

6.1 怎么使用ServletContext

使用ServletContext:
(1) 如何得到ServletContext对象

this.getServletContext();
this.getServletConfig().getServletContext();

(2) 你可以把它想象成一张表,这个和Session非常相似:每一行就是一个属性

keyNameString:valueObject
  • 添加属性:setAttribute(String name, Object obj);
  • 得到值:getAttribute(String name),这个方法返回Object
  • 删除属性:removeAttribute(String name)
    (3) 生命周期
    ServletContext中的属性的生命周期从创建开始,到服务器关闭结束。

6.2 一个具体的实例

6.2.1 创建Servlet1和Servlet2

Servlet1用于在ServletContext中创建属性,Servlet2用于从ServletContext读取属性:
Servlet1的doGet方法为:

public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    response.setContentType("text/html;charset=utf-8");
    PrintWriter out = response.getWriter();
    // 获取ServletContext对象的引用
    // 第一种方法
    ServletContext servletContext = this.getServletContext();
    // 第二种方法
    // ServletContext servletContext2 = this.getServletConfig().getServletContext();
    servletContext.setAttribute("name", "小明");
    out.println("将 name=小明  写入了ServletContext");
}

Servlet2的doGet方法为:

public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    response.setContentType("text/html;charset=utf-8");
    PrintWriter out = response.getWriter();
    // 取出ServletContext的某个属性
    //1.首先获取到ServletContext
    ServletContext servletContext = this.getServletContext();
    //2.取出属性
    String name = (String)servletContext.getAttribute("name");
    out.println("name="+name);
}

6.2.2 先后访问这两个servlet

以此访问Servlet1,我们可以分别看到输出如下:
在这里插入图片描述
以此访问Servlet2,我们可以分别看到输出如下:
在这里插入图片描述

6.2.3 访问结果读解

  • 运行结果似乎和Session,Cookie的应用没什么不同。
  • 其实看似相同,实则完全不一样
  • 只要我们不关闭Tomcat或者reload该应用,这时候我们关闭当前的浏览器,或者是换一个浏览器,假设我们从Chrome换到IE再次访问Servlet2,依然可以看到结果!
  • 这就是它们最大的不同了,因为ServletContext是存在于服务器内存中的一个公共空间,它可以供所有的用户客户端访问。

7 Servlet应用详解

7.1 多个Servlet通过ServletContext对象实现数据共享

这个很好理解,类似于Session,我们也可以通过ServletContext对象来共享数据,但要注意的是,Session只能在一个客户端共享数据,它独占一个客户端。而ServletContext中的数据是可以供所有客户端共享的。

7.2 实现Servlet的请求转发

之前我们学过的请求转发是通过request对象的:
request.getRequestDispatcher("/url").forward(request, response);

这里要说明的是,ServletContext也可以实现请求转发:
this.getServletContext().getRequestDispatcher("/url").forward(request, response);
这两个转发效果是一样的。

7.3 获取Web应用的初始化参数

在【Servlet——开发细节+ServletConfig对象】中,我们介绍过在Servlet部署的时候,我们可以使用一个或多个标签为servlet配置一些初始化参数,然后我们通过ServletConfig对象获取这些参数,假如有如下的MyServlet,它的配置为:

<servlet>  
    <servlet-name>MyServlet</servlet-name>  
    <servlet-class>com.gavin.servlet.MyServlet</servlet-class>  
    <init-param>  
        <param-name>encoding</param-name>  
        <param-value>utf-8</param-value>  
    </init-param>  
</servlet>  

可以看到它配置了一个初始化参数:encoding=utf-8,那么我们在MyServlet的源代码中需要这样去得到这个参数:

String encoding = this.getServletConfig().getInitParameter("encoding");

上述的参数配置方法只针对一个特定的Servlet有效,现在我们可以通过ServletContext来获取全局的、整个Web应用的初始化参数,全局的初始化参数是这样配置在web.xml文件中的:

<!-- 如果希望所有的Servlet都可以使用该配置,则必须这么做 -->
<context-param>
    <param-name>name</param-name>
    <param-value>gavin</param-value>
</context-param>

然后我们可以在任意一个Servlet中使用ServletContext获取这个参数:

String name = this.getServletContext().getInitParameter("name");

7.4 利用ServletContext对象读取资源文件(比如properties文件)

读取资源文件要根据资源文件所在的位置分为两种情况:

(1)文件在WebRoot文件夹下,即我们的Web应用的根目录下。这时候我们可以使用ServletContext来读取该资源文件。

假设我们Web根目录下有一个配置数据库信息的dbinfo.properties文件,里面配置了name和password属性,这时候可以通过ServletContext去读取这个文件:

// 这种方法的默认读取路径就是Web应用的根目录
InputStream stream = this.getServletContext().getResourceAsStream("dbinfo.properties");
// 创建属性对象
Properties properties = new Properties();
properties.load(stream);
String name = properties.getProperty("name");
String password = properties.getProperty("password");
out.println("name="+name+";password="+password);

(2)但是如果这个文件放在了src目录下,通过ServletContext是读不到的,必须要使用类加载器去读取。

// 类加载器的默认读取路径是src根目录
InputStream stream = MyServlet.class.getClassLoader().getResourceAsStream("dbinfo.properties")

但是如果这个文件此时还没有直接在src目录下,而是在src目录下的某个包下,比如在com.gavin包下,此时类加载器要加上包的路径,如下:

InputStream stream = MyServlet.class.getClassLoader().getResourceAsStream("cn/edu/cuit/dbinfo.properties")

另外,补充一点,ServletContext可以获取文件的全路径,当然这个也是在Web应用根目录下的文件。比如我们在WebRoot文件夹下有一个images文件夹,images文件夹下有一个Servlet.jpg图片,为了得到这个图片的全路径,如下:

// 如何读取到一个文件的全路径,这里会得到在Tomcat的全路径
String path = this.getServletContext().getRealPath("/images/Servlet.jpg");

在网站开发中,有很多功能要使用ServletContext,比如

  1. 网站计数器
  2. 网站的在线用户显示
  3. 简单的聊天系统

总之,如果是涉及到不同用户共享数据,而这些数据量不大,同时又不希望写入数据库中,我们就可以考虑使用ServletContext实现。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值