ServletContext
# 70.ServletContext
ServletContext 代表整个 web 应用,可以和程序的容器(服务器)来通信
# 获取和使用 ServletContext
API 文档是这样描述 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.
获取 ServletContext 的方法:
- 通过
request
对象获取:request.getServletContext();
- 通过
HttpServlet
获取:this.getServletContext();
通过两种方式获取的是同一个对象。我们写代码来验证下:
package com.peterjxl.servletcontext;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/ServletContextDemo1")
public class ServletContextDemo1 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 演示获取ServletContext
// 1. 通过`request`对象获取
ServletContext context1 = req.getServletContext();
// 2. 通过`HttpServlet`获取
ServletContext context2 = this.getServletContext();
System.out.println("context1 == context2: " + (context1 == context2));
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
重启 Tomcat,访问 http://localhost: 8080/hello/ServletContextDemo1,可以看到控制台运行结果是 true
context1 == context2: true
# ServletContext 的功能
主要有如下功能:
- 获取 MIME 类型(最常用)
- 域对象:共享数据
- 获取文件的真实(服务器)路径
# 获取 MIME 类型
什么是 MIME 类型:在互联网通信过程中定义的一种文件数据类型,例如文本类型,图片类型。
这个 MIME 类型有什么用?在响应消息有有个响应头是 Content-type,就是告诉浏览器响应体是什么类型的数据,浏览器根据这个来展示数据
格式: 大类型/小类型,例如 text/html、image/jpeg
获取方法:String getMimeType(String file)
。为什么能这样获取?因为 MIME 类型都是存储在服务器里的,而 ServletContext 可以与服务器通信。
我们打开 Tomcat 的安装目录,然后打开 conf/web.xml 文件,这个文件是所有项目的 web.xml 的祖先,可以认为各个项目的 web.xml 文件都继承了它。
以 apache-tomcat-9.0.73\conf\web.xml 为例,从 649 行开始,定义了一大堆的 MIME 类型:
<mime-mapping>
<extension>123</extension>
<mime-type>application/vnd.lotus-1-2-3</mime-type>
</mime-mapping>
<mime-mapping>
<extension>3dml</extension>
<mime-type>text/vnd.in3d.3dml</mime-type>
</mime-mapping>
<mime-mapping>
<extension>3ds</extension>
<mime-type>image/x-3ds</mime-type>
</mime-mapping>
<mime-mapping>
<extension>3g2</extension>
<mime-type>video/3gpp2</mime-type>
</mime-mapping>
<mime-mapping>
<extension>3gp</extension>
<mime-type>video/3gpp</mime-type>
</mime-mapping>
<mime-mapping>
<extension>7z</extension>
<mime-type>application/x-7z-compressed</mime-type>
</mime-mapping>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
extension 标签是后缀名,然后 mime-type 是对应的 MIME 类型。我们写个代码来演示下:
package com.peterjxl.servletcontext;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/ServletContextDemo2")
public class ServletContextDemo2 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 演示获取MIME
ServletContext servletContext = this.getServletContext();
String filename = "a.jpg";
String mimeType = servletContext.getMimeType(filename);
System.out.println("mimeType: " + mimeType);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
重启 Tomcat,访问 http://localhost: 8080/hello/ServletContextDemo2,IDE 里的输出:
mimeType: image/jpeg
# 域对象:共享数据
域对象都有如下方法,用来共享数据:
setAttribute(String name,Object value)
getAttribute(String name)
removeAttribute(String name)
域对象 ServletContext 范围:所有用户的所有请求。而一个 request 域对象的范围只有在一次请求中。我们写个代码来演示下:
首先在 ServletContextDemo3
里设置一个数据:
package com.peterjxl.servletcontext;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/ServletContextDemo3")
public class ServletContextDemo3 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 演示共享数据
// 2. 通过HttpServlet获取servletContext
ServletContext servletContext = this.getServletContext();
servletContext.setAttribute("msg", "fukme");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
然后在 ServletContextDemo4
中获取数据:
package com.peterjxl.servletcontext;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/ServletContextDemo4")
public class ServletContextDemo4 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 演示获取MIME
// 2. 通过HttpServlet获取servletContext
ServletContext servletContext = this.getServletContext();
Object msg = servletContext.getAttribute("msg");
System.out.println(msg);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
重启 Tomcat,先访问 ServletContextDemo3
,然后访问 ServletContextDemo4
;虽然两个是独立的 Servlet,但是可以看到 ServletContextDemo4
能成功获取数据,这说明 ServletContext 能共享数据。
但是我们比较少用 ServletContext 共享,这是因为所有 Servlet 都能操作它,容易冲突;并且容易使得 ServletContext 对象变的臃肿。
# 获取文件的真实(服务器)路径
我们开发项目的时候,一般不会在服务器上面开发,而是在本地开发,开发完后部署到服务器上。也可以说是在本地工作空间中开发;
此时就有一个问题:如果要返回一个文件给用户,文件路径要怎么定义?因为部署到服务器上后,服务器的路径一开始是难以确定的;本地开发的话,文件路径也难以确定,即使确定了,后期一旦换个目录,又要改文件路径的话,非常麻烦。
此时,我们就可以将文件放到 web 目录下,然后通过 ServletContext 获取!
ServletContext 有一个获取文件路径的方法:String getRealPath(String path)
我们在 src 目录下新建一个 a.txt 文件,web 目录下新建一个 b.txt 文件,在 WEB-INF 目录下新建一个 c.txt 文件,此时项目结构如下:
然后我们可以通过 getRealPath 方法获取文件的路径:
ServletContext servletContext = this.getServletContext();
String realPath = servletContext.getRealPath("/b.txt");
System.out.println(realPath);
2
3
最后,我们就可以 File 对象操作文件了:
File file = new File(realPath);
重启 Tomcat,我们可以看到工作空间目录是这个(每个人的工作空间目录可能不一样):
Using CATALINA_BASE: "C:\Users\peterjxl\AppData\Local\JetBrains\IntelliJIdea2022.3\tomcat\f48a6060-bf20-4710-adb8-4ae1308d09c7"
我们打开这个目录,并打开 conf/Catalina/localhost/hello.xml 文件:可以看到项目的文件是在这里:D:\Projects\LearnJavaWeb\out\artifacts\LearnJavaWeb_war_exploded。
<Context path="/hello" docBase="D:\Projects\LearnJavaWeb\out\artifacts\LearnJavaWeb_war_exploded" />
我们访问 http://localhost: 8080/hello/ServletContextDemo5,可以看到打印的路径为:
D:\Projects\LearnJavaWeb\out\artifacts\LearnJavaWeb_war_exploded\b.txt
同理,如果想获取 WEB-INF 目录下的路径:
String c = context.getRealPath("/WEB-INF/c.txt");//WEB-INF目录下的资源访问
System.out.println(c);
2
如果想要获取 src 目录下的文件路径:可以这样获取:
String a = context.getRealPath("/WEB-INF/classes/a.txt");//src目录下的资源访问
System.out.println(a);
2
这是因为 src 目录下的会被复制到/WEB-INF/classes 下
中文文件问题。解决思路:
- 获取客户端使用的浏览器版本信息
- 根据不同的版本信息,设置 filename 的编码方式不同