1. 什么是 ServletContext

首先看看官方对其定义:

public interface ServletContext Defines a set of methods that a servlet uses to communicate with its servlet container, for example, to get the MIME type of a file, dispatch requests, or write to a log file.

由此可知,首先它是一个interface,它定义了一系列的方法用来 servlet 和其所在的容器进行通信。context这个词本身的含义为上下文、环境,servlet 容器为 servlet 的运行提供了环境。

当 servlet 容器启动了一个 webapp 后,会为其创建一个 ServletContext 对象,并且这个对象是唯一的。它就像一个 webapp 的管家一样,管理着这个 web 应用内的所有 servlet 对象,与此同时,servlet 对象也可以通过这个“管家”来访问容器内的各种资源。

2. 它提供了哪些方法

该接口定义了大量的方法,对方法的作用进行分类可以分为以下几类:

2.1 webapp范围内共享数据

因为一个 webapp 内只有一个 ServletContext 对象,所以它就可以作为一个“中间人”用来做为数据共享的场所。与此相关的方法包括:

  • setAttribute(String name, Object object) - 该方法接受两个参数,类似于 k-v 形式,其中 name 为属性名,object 将一个 java 对象作为共享数据(属性值)存储在 ServletContext 对象中
  • getAttribute(String name) - 根据属性名获取属性值,返回一个 Object 对象
  • getAttributeNames() - 返回一个 Enumeration 对象,该对象包含了所有的属性名
  • removeAttribute(String name) - 从 ServletContext 对象中删除指定的属性

2.2 访问当前 webapp 的资源

  • getContextPath() - 返回当前 web 应用的 URL 入口
  • getInitParameter(String name) - 根据给定的参数名,返回web应用范围内匹配的初始化参数
  • getInitParameterNames() - 返回一个 Enumeration 对象,包括了 web 应用范围内所有初始化参数名
  • getServletContextName() - 返回 web 应用的名字
  • getRequestDispatcher(String path) - 返回一个用于向其他 web 组件转发请求的 RequestDispatcher 对象

2.3 访问 Servlet 容器中其他 web 应用

  • getContext(String uripath) - 根据指定的 uri,返回当前容器内其他 web 应用的 ServletContext 对象

2.4 访问 Servlet 容器的相关信息

  • getMajorVersion() - 返回 Servlet 容器支持的 Java Servlet API 的主版本号
  • getMinorVersion() - 返回 Servlet 容器支持的 Java Servlet API 的次版本号
  • getServerInfo() - 返回 Servlet 容器的名字和版本号

2.5 访问服务器端的文件系统资源

  • getRealPath(String path) - 根据参数指定的虚拟路径,返回一个文件系统中的真实路径
  • getResource(String path) - 返回一个映射到参数指定路径的 URL
  • getResourceAsStream(String path) - 返回一个用于读取参数指定的文件的输入流
  • getMimeType(String file) - 返回参数指定文件的 MIME 类型

2.6 输出日志

  • log(String msg) - 向 Servlet 的日志文件中写日志
  • log(String msg, Throwable throwable) - 向 Servlet 的日志文件中写错误日志,以及异常的堆栈信息

3. 如何获得 ServletContext 对象

通过 getServletContext() 方法就可以获得当前 web 应用的 ServletContext 对象。该方法在ServletConfig 接口中声明,继承关系如下图所示。

4. ServletContext 应用实例

4.1 数据共享

  1. - 在一个servlet中设置一个 ServletContext 对象的属性值,然后通过另一个servlet去访问该属性
  2. HelloServlet.java
  3. public class HelloServlet extends HttpServlet {
  4. @Override
  5. protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  6. throws ServletException, IOException {
  7. ServletContext context = this.getServletContext();
  8. context.setAttribute("username", "tianyichen");
  9. }
  10. @Override
  11. protected void doPost(HttpServletRequest req, HttpServletResponse resp)
  12. throws ServletException, IOException {
  13. doGet(req, resp);
  14. }
  15. }
  16. GetServlet.java
  17. public class GetServlet extends HttpServlet {
  18. @Override
  19. protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  20. throws ServletException, IOException {
  21. ServletContext context = this.getServletContext();
  22. String username = (String) context.getAttribute("username");
  23. resp.setContentType("text/html");
  24. resp.setCharacterEncoding("utf-8");
  25. resp.getWriter().print("username=" + username);
  26. }
  27. @Override
  28. protected void doPost(HttpServletRequest req, HttpServletResponse resp)
  29. throws ServletException, IOException {
  30. doGet(req, resp);
  31. }
  32. }

4.2 读取web的初始化参数

  1. public class ReadInitParam extends HttpServlet {
  2. @Override
  3. protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  4. throws ServletException, IOException {
  5. ServletContext context = this.getServletContext();
  6. String paramValue = context.getInitParameter("url");
  7. resp.getWriter().print(paramValue);
  8. }
  9. @Override
  10. protected void doPost(HttpServletRequest req, HttpServletResponse resp)
  11. throws ServletException, IOException {
  12. doGet(req, resp);
  13. }
  14. }

其中初始化参数存放在 web.xml 的 context-param 元素中。

4.3 请求转发

什么是请求转发?用下图可以很好地解释。客户端请求了 a 这个url,a 将请求转发给了 b,b 将资源传给 a,然后 a 再把资源响应给客户端。虽然图中 a 和 b 用了服务器的图标,但是实际代表的是两个 servlet,通常情况下,一个 servlet 对应一个 url-pattern。
未命名文件.jpg
使用这种技术就实现了访问一个指定的 url,却拿到了另一个 url 资源的功能。

  1. Employer - 转发请求到 Employee
  2. --------------------------------
  3. public class EmployeeServlet extends HttpServlet {
  4. @Override
  5. protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  6. throws ServletException, IOException {
  7. resp.setCharacterEncoding("utf-8");
  8. resp.setContentType("text/html");
  9. resp.getWriter().print("你的请求被转发了!");
  10. }
  11. @Override
  12. protected void doPost(HttpServletRequest req, HttpServletResponse resp)
  13. throws ServletException, IOException {
  14. doGet(req, resp);
  15. }
  16. }
  17. public class EmployerServlet extends HttpServlet {
  18. @Override
  19. protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  20. throws ServletException, IOException {
  21. ServletContext context = this.getServletContext();
  22. context.getRequestDispatcher("/untouch").forward(req, resp);
  23. }
  24. @Override
  25. protected void doPost(HttpServletRequest req, HttpServletResponse resp)
  26. throws ServletException, IOException {
  27. doGet(req, resp);
  28. }
  29. }

4.4 读取配置文件

  1. public class ReadProp extends HttpServlet {
  2. @Override
  3. protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  4. throws ServletException, IOException {
  5. ServletContext context = this.getServletContext();
  6. InputStream is = context.getResourceAsStream("/WEB-INF/classes/mysql.properties");
  7. Properties prop = new Properties();
  8. prop.load(is);
  9. String username = prop.getProperty("username");
  10. String pwd = prop.getProperty("password");
  11. resp.getWriter().print("username=" + username + ", pwd=" + pwd);
  12. }
  13. @Override
  14. protected void doPost(HttpServletRequest req, HttpServletResponse resp)
  15. throws ServletException, IOException {
  16. doGet(req, resp);
  17. }
  18. }