目录
ServletContext
Tomcat中的项目也称为应用程序,Java思想中,万物皆对象,那web应用程序也可以看为一个对象,在Java Web开发中,提供了一个对象“ServletContext”来表示应用程序。
说明:
1.
在⼀个
WEB
应⽤程序中,只能存在⼀个
ServletContext
对象。
2.
每个应⽤程序
,
都有它⾃⼰所对应的⼀个
ServletContext
对象。
3.
在服务器启动的时候,为每个
web
应⽤程序创建⼀个单独的
ServletContext
对象。
4. ServletContext
是接⼝
,
此接⼝的实现类是
tomcat
引擎提供
ServletContext对象的作⽤
- 获取WEB应⽤程序的初始化参数
- 获取WEB应⽤程序下任意资源的绝对路径
- ServletContext作为域对象,存储数据
如何获取ServletContext对象
⼀个
web
应⽤程序只有⼀个
ServletContext
对象
⽅式
1
:通过
ServletConfig
接⼝获取,它提供了⽅法
getServletContext()
public interface ServletConfig {
ServletContext getServletContext();
}
⽅式
2
:通过继承的
HttpServlet
类的⽗类
GenericServlet
获取,
GenericServlet类 提供了⽅法getServletContext()
,下⾯是源码分析
class Context1Servlet extends HttpServlet {}
↓
↓
public abstract class HttpServlet extends GenericServlet{}
↓
↓
public abstract class GenericServlet implements Servlet,ServletConfig,Serializable {
//⽤transient关键字标记的成员变量不参与序列化过程。
private transient ServletConfig config;
public ServletConfig getServletConfig() {
return this.config;
}
public ServletContext getServletContext() {
return this.getServletConfig().getServletContext();
}
//省略其他
}
代码演示
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//⽅式1:通过ServletConfig对象获取,它提供了⽅法 getServletContext()
ServletConfig config = super.getServletConfig();
ServletContext servletContext = config.getServletContext();
System.out.println("servletContext = "+servletContext);
//⽅式2:通过继承的HttpServlet类的⽗类GenericServlet获取, GenericServlet类提供了⽅法 getServletContext()
ServletContext servletContext2 = super.getServletContext();
System.out.println("servletContext2 = "+servletContext2);
}
作用一:获取WEB应用程序的初始化参数
因为⼀个
web
应⽤程序只有⼀个
ServletContext对象,所以这个对象对整个应⽤程序的相关内容都是了解的
web.xml
配置
WEB
应⽤程序的初始化参数
<!-- 配置的是WEB程序的初始化参数-->
<context-param>
<param-name>name</param-name>
<param-value>wensong</param-value>
</context-param>
API
操作
⽅法
|
返回值
|
描述
|
getInitParameter(String name)
|
String
|
获取
web
应⽤程序的初始化参数
|
代码演示:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* 获取ServletContext对象
* ⽗类的⽅法
* org.apache.catalina.core.ApplicationContextFacade实现接⼝ServletContext
*/
ServletContext context = getServletContext();
System.out.println(context);
//context对象,获取配置⽂件中的初始化参数
String value = context.getInitParameter("name");
System.out.println(value);
}
作用二:获取WEB应用程序下任意资源的绝对路径
⽅法
|
返回值
|
描述
|
getRealPath(String path)
|
String
|
接收⼀个资源的相对路径,返回真实路径
(
完整路径)的字符串
|
- 获取web⽬录下的a.txt绝对路径
- 获取web⽬下WEB-INF⽬录下的b.txt绝对路径
- 获取src⽬录下的c.txt绝对路径
- 获取web02下的d.txt绝对路径,获取不到
代码演示:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取servletContext对象
ServletContext servletContext = super.getServletContext();
//获取web⽬录下的a.txt绝对路径
String apath = servletContext.getRealPath("a.txt");
System.out.println("apath = " + apath);
//获取web⽬录下WEB-INF⽬录下的b.txt绝对路径
String bpath = servletContext.getRealPath("WEB-INF/b.txt");
System.out.println("bpath = " + bpath);
//获取src⽬录下的c.txt绝对路径
String cpath = servletContext.getRealPath("WEB-INF/classes/c.txt");
System.out.println("cpath = " + cpath);
//获取web02下的d.txt绝对路径
//tomcat的webapps中 没有部署该⽂件, ⽆法获取到
}
作用三:ServletContext作为域对象
JavaWeb
四⼤域对象
根据有作⽤范围由⼩到⼤:
- page(jsp有效)------》page域指 在当前⻚⾯中有效, ⼀旦⻚⾯超出该⻚⾯就不能访问了.
- request(⼀次请求)---》request域指 在本次请求与响应过程中, 都能够访问.
- session(⼀次会话)---》session域指 在本次打开浏览器访问服务器开始, 到本次浏览器关闭之间访问有效
- application(当前web应⽤)---》application域指 在当前web项⽬中均可访问. 由ServletContext代表application域
之所以他们是域对象,原因是他们都内置了
map
集合,都有
setAttribute
和
getAttribute
⽅法。
ServletContext
作为域对象
ServletContext
是在服务器启动的时候为每个
web
项⽬ 单独创建⼀个
ServletContext
对象。当
web项⽬从服务器中移除,或者是关闭服务器的时候
ServletContext
对象会被销毁。向
ServletContext中保存的数据⼀直存在(当服务器关闭的时
ServletContext
对象被销毁,然后⾥⾯数据才会失效)。
- ServletContext作⽤范围:整个web应⽤
⽅法
|
返回值
|
描述
|
setAttribute(String key, Object value)
|
void
|
向域对象存储数据
|
getAttribute(String key)
|
Object
|
取出域对象数据
|
removeAttribute(String key)
|
void
|
移除域对象数据
|
代码演示
:
第⼀个
Servlet:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取servletcontext对象
ServletContext context = super.getServletContext();
//存储数据 type=java
context.setAttribute("type","java");
//读取数据 type的值
Object value = context.getAttribute("type");
System.out.println("Context4Servlet type = " + value);
}
第⼆个
Servlet:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取ServletContext域对象
ServletContext context = super.getServletContext();
//获取域对象中 指定键的值
Object value = context.getAttribute("type");
System.out.println("Context5Servlet type = " + value);
}
案例 统计访问的次数
⽬的
: 练习域对象ServletContext
的使⽤
分析
:
- 第⼀次访问Servlet的时候,数据 1 存储到域对象中
- 第⼆次访问Servlet的时候,从域中取出数据++,再存储回去
@Override
public void init() throws ServletException {
//第⼀次访问Servlet的时候,数据 1 存储到域对象中
ServletContext context = super.getServletContext();
context.setAttribute("count",1);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//第⼆次访问Servlet的时候,从域中取出数据++,再存储回去
ServletContext context = super.getServletContext();
Integer count = (Integer) context.getAttribute("count");
System.out.println("welcome " + count);
count++;
context.setAttribute("count",count);
}
Servlet
注解开发取代web.xml
Servlet版本 | JavaEE版本 |
4.0 | 8.0 |
3.1 | 8.0 |
3.1 | 7.0 |
3.0 | 6.0 |
2.5 | 5.0 |
Servlet 2.5
版本时
- Servlet、过滤器(Filter)和监听器(Listener)的声明都必须使⽤web.xml
Servlet 3.0
版本时
- 新增的注解⽀持:该版本新增了若⼲注解,⽤于简化 Servlet、过滤器(Filter)和监听器 (Listener)的声明,这使得 web.xml ⽂件从该版本开始不再是必选的了
注解servlet配置
@WebServlet,
注解添加到⾃⼰定义的
Servlet
中的类声明上即可
,
注解的属性
urlPatterns,属性值就是浏览器的访问地址
//创建servlet,在@WebServlet注解中添加urlPatterns= "/test",作为请求路径
@WebServlet(urlPatterns = "/test")
注解
servlet
模板修改
- doPost⽅法内, 添加 doGet(request, response);
- 修改注解⼩括号内容为 ("/${Entity_Name}")
Response对象
负责对浏览器进⾏响应的对象
什么是响应
: 服务器接收到请求需要进⾏处理,将处理以后的结果显示回浏览器端(将这个过程称为是响应
Response
)。
- 查看Servlet中的service⽅ 法得知, Servlet中使⽤的ServletResponse接⼝, ⽽我们使⽤的是
- ServletResponse的⼦接⼝HttpServletResponse, 它继承⾃ServletResponse, 是与HTTP协议相关的Response响应对象
- 我们使⽤的⼦接⼝HttpServletResponse, 此接⼝对象由Tomcat引擎提供
- 可以实现对客户端的响应, 响应内容包括: 响应⾏,响应头,响应体
Response设置响应行
方法 | 返回值 | 描述 |
setStatus(int sc) | void | 设置响应的状态码 |
设置响应的状态码
- 200 正确
- 302 重定向
- 304 查找本地缓存
- 404 请求资源不存在
- 500 服务器内部错误
response.setStatus(500);
Response设置响应头
HTTP
协议的响应头
,
数据格式键值对
k:v
包含指导性信息
,
指导客户端
⽅法
|
返回值
|
描述
|
addHeader(String key,String value)
|
void
|
添加响应头,值为
String
类型
|
addIntHeader(String key,int value)
|
void
|
添加响应头,值为
int
类型
|
addDateHeader(String key,long l)
|
void
|
添加响应头,值为⽇期类型
|
setHeader(String key,String value)
|
void
|
更改响应头,值为
String
类型
|
setIntHeader(String key,int value)
|
void
|
更改响应头,值为
int
类型
|
setDateHeader(String key,long l)
|
void
|
更改响应头,值为⽇期类型
|
add
开头的⽅法:针对⼀个
key
对应多个
value
的情况。
⽐如已有⼀个响应头 hello:java
然后执⾏ addHeader(“hello”,”java2222”);
最终得到响应头的结果:hello:java,java2222
set
开头的⽅法:针对⼀个
key
对应⼀个
value
的情况。
⽐如已有⼀个响应头 hello:java
然后执⾏ addHeader(“hello”,”java2222”);
最终得到响应头的结果:hello:java2222
演示:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* response对象设置响应头
*/
response.addHeader("hello","java");
response.addDateHeader("date",System.currentTimeMillis());
/*
* addHeader() 添加,实现⼀个键对应多个值
* setHeader() 设置,原来的键覆盖
*/
response.setHeader("hello","java2222");
}
Response设置响应体
HTTP的响应体,就是⻚⾯的正⽂部分
字符流向浏览器写数据
⽅法
|
返回值
|
描述
|
write()
|
PrintWriter
|
使⽤字符串数据
,
没有差别
,
输出是整数
,
查询编码表
|
print()
|
PrintWriter
|
⽆论是什么
,
原样打印
|
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* response对象⽅法getWriter()
* 打印流的响应体
* write() 使⽤字符串数据,没有差别, 输出是整数,查询编码表
* print() ⽆论是什么,原样打印
*/
PrintWriter pw = response.getWriter();
pw.write(100);
pw.print(100);
}
字符流的中⽂乱码问题
产⽣乱码原因
:
编码和解码不⼀致
提问:在
Servlet
中编写以下代码,向⻚⾯输出中⽂是否会产⽣乱码?
response.getWriter().print("中⽂");
会乱码:
- 原因:
- 字符流是有缓冲区的,response获得字符流,response设计默认的缓冲区编码是ISO-8859-1。这个字符集不⽀持中⽂的。
- 解决:
- 设置response获得字符流缓冲区的编码 与 设置浏览器默认打开时候采⽤的字符集⼀致即可。
⽅法
| 返回值 | 描述 |
setHeader("Content-Type", "text/html;charset=UTF-8")
| void |
设置浏览器打开⻚⾯时采⽤的字符集
|
setContextType(String type)
| void |
设置浏览器打开⻚⾯时采⽤的字符集
|
// 设置浏览器默认打开的时候采⽤的字符集:
// response.setHeader("Content-Type", "text/html;charset=UTF-8");
// 简化代码
response.setContentType("text/html;charset=UTF-8");
// 输出中⽂汉字
response.getWriter().println("中⽂");
字节流向浏览器写数据
方法 | 返回值 | 描述 |
getOutputStream()
|
OutputStream
|
返回字节输出流
OutputStream
,响应⾮⽂本类型的数据
|
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* response对象的⽅法getOutputStream,响应⾮⽂本类型的数据
*/
//获取图⽚的绝对路径
String aFile = getServletContext().getRealPath("download/a.jpg");
FileInputStream fis = new FileInputStream(aFile);
OutputStream out = response.getOutputStream();
int len = 0;
byte[] bytes = new byte[1024];
while ((len = fis.read(bytes))!=-1){
out.write(bytes,0,len);
}
fis.close();
}
重定向
重定向的写法
浏览器进⾏重新的定向
:
- 设置302状态码: setStatus(302)
- 设置重定向资源的地址: setHeader("location","资源")
通过
HttpServletResponse
对象中的以下⽅法实现重定向
方法 | 返回值 | 描述 |
sendRedirect(String location)
| void | 重定向 |
重定向的代码实现
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* 浏览器进⾏重新的定向:
* 设置302状态码: setStatus(302)
* 设置重定向资源的地址: setHeader("location","/web02/servlet2")
*/
//response.setStatus(302);
//response.setHeader("location","/web02/servlet2");
// web02是发布设置的虚拟地址
response.sendRedirect("/web02/servlet2");
}
案例 文件下载
需求
:
- 客户端浏览器从服务器下载⽂件
分析
- 超链接地址 ,连接的地址是服务器端⽂件的路径
- 当点击下载⽂件时:
- 若浏览器识别该⽂件类型, 并不会下载⽂件, ⽽是直接打开运⾏
- 若浏览器不识别该⽂件类型, 直接下载⽂件
实现
:
html
代码
<body>
<a href="/web02/download?fileName=cat.png" name="">下载猫咪图⽚</a>
</body>
编写服务器端代码
,
告诉浏览器下载
,
不要打开
@WebServlet(urlPatterns = "/download")
public class DownloadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String filename= request.getParameter("fileName");
String filePath = filename;
String agent = request.getHeader("User-Agent");
if (agent.contains("MSIE")) {
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// ⽕狐浏览器
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?"+base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
//通知浏览器请你下载,不要打开
response.setHeader("Content-Disposition","attachment;filename=" +filename);
System.out.println("filePath = " + filePath);
String path = getServletContext().getRealPath("download/"+filePath);
FileInputStream fis = new FileInputStream(path);
OutputStream out = response.getOutputStream();
int len = 0;
byte[] bytes = new byte[8*1024];
while ((len = fis.read(bytes))!=-1){
out.write(bytes,0,len);
}
fis.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
案例 验证码
验证码的本质是个图⽚,图⽚⾥⾯是个随机⽣成字符串
随机字符串的思想:
String str ="abcdefABCDE1234567890";
Random.nextInt( str.length() )产⽣整数随机数
str.charAt(索引)
html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>验证码</title>
<script type="text/javascript">
function fnChange() {
/*
* ⻚⾯打开的时候,请求服务器资源/web02/code
* 点击图⽚js函数中,发了请求/web02/code
*
* 请求的资源没有变化,服务器端程序也没有变化
* 浏览器拿缓存吧, 放回状态码304
*
* 为了每次请求得到不⼀样的验证码,添加参数保证请求路径不同即可
*/
//点击图⽚,修改src的属性值
var code = document.getElementById("code");
var date = new Date().getTime();
code.src="/web02/code?t="+date;
}
</script>
</head>
<body>
<img src="/web02/code" onclick="fnChange()" id="code">
</body>
</html>
数据库端二维码产生:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 创建画布
int width = 120;
int height = 40;
BufferedImage bufferedImage = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
// 获得画笔
Graphics g = bufferedImage.getGraphics();
// 填充背景颜⾊
g.setColor(Color.white);
g.fillRect(0, 0, width, height);
// 绘制边框
g.setColor(Color.red);
g.drawRect(0, 0, width - 1, height - 1);
// ⽣成随机字符
// 准备数据
String data ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
// 准备随机对象
Random r = new Random();
// 声明⼀个变量 保存验证码
String code = "";
// 书写4个随机字符
for (int i = 0; i < 4; i++) {
// 设置字体
g.setFont(new Font("宋体", Font.BOLD, 28));
// 设置随机颜⾊
g.setColor(new Color(r.nextInt(255), r.nextInt(255),r.nextInt(255)));
String str = data.charAt(r.nextInt(data.length())) + "";
g.drawString(str, 10 + i * 28, 30);
// 将新的字符 保存到验证码中
code = code + str;
}
// 绘制⼲扰线
for (int i = 0; i < 6; i++) {
// 设置随机颜⾊
g.setColor(new Color(r.nextInt(255), r.nextInt(255),r.nextInt(255)));
g.drawLine(r.nextInt(width), r.nextInt(height), r.nextInt(width),r.nextInt(height));
}
// 将验证码 打印到控制台
System.out.println(code);
// 将验证码放到session中
//request.getSession().setAttribute("code_session", code);
// 将画布显示在浏览器中
ImageIO.write(bufferedImage, "jpg", response.getOutputStream());
}