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的编码方式不同