1.概述
客户端基于HTTP协议访问服务器时,服务器创建代表请求的Request对象和代表响应的Response对象
Request对象包含了很多操作请求相关数据的方法
Response对象包含了很多操作响应数据的方法
2.继承结构
ServletResponse - 代表响应对象的接口
|
|-HttpServletResponse - 在父接口的基础上增加了很多和HTTP协议相关的方法
3.常用方法
(1)设置状态码
setStatus(int sc)
setStatus(int sc, String sm)
案例:手动配置状态码为404 或 500 通过浏览器访问 查看效果
(2)设置响应头
void addHeader(String name, String value)
void addIntHeader(String name, int value)
void addDateHeader(String name, long date)
void setHeader(String name, String value)
void setIntHeader(String name, int value)
void setDateHeader(String name, long date)
(3)获取输出流 向客户端发送数据
getOutputStream - 字节流
getWriter - 字符流
response.setCharacterEncoding() 指定服务器发送字符数据时 解码采用的编码集
response.setContentType() 指定客户端解析数据时 采用的字符集 在响应头中 增加ContentType头 通知浏览器解析数据时 采用的字符集
两码一致 解决乱码
**注意:response中获取的流 不需要手动关闭 服务器自动帮你关
**注意:同一个response对象中 只能获取一种流 要么字节流 要么字符流 如果先后获取两种不同流 则会在获取第二种流的时候报错
(4)设置响应头应用 - 实现重定向
HTTP协议中规定了 302状态码代表要实现重定向 Location代表重定向的地址
重定向:
当客户端发送http请求访问服务器中某一个资源时 如果服务器发先当前资源的位置发生了变化 可以 发送302+Location 命令浏览器重定向去访问另一个新的地址 浏览器了收到302后会立即自动访问Location指定的地址 从而访问到资源的新的位置 这个过程称之为发生了 重定向操作。
方法1:
response.setStatus(302);
response.setHeader("Location", "/Day10/index.jsp");
方法2:
response.sendRedirect("/Day10/index.jsp");
(5)设置响应头应用 - 定时刷新
HTTP协议中提供了refresh响应头 可以指定一个时间 和地址 浏览器收到这个头会经过指定时间自动访问指定地址
response.setHeader("Refresh", "3;url=/Day10/index.jsp");
(6)设置响应头应用 - 禁止缓存
HTTP协议提供了和缓存控制相关的头 因为历史原因 不同浏览器支持的头不同
Expires
Cache-Control
Pragma
浏览器为了减少对服务器的访问,会在第一次访问到资源后进行缓存,之后再访问,就直接用缓存中的数据,减少对服务器的访问。
这种缓存机制,提高了浏览器的响应数度,减少了服务器的访问量,在大多数情况下是好的,但是在某些场景下可能有问题。
比如:验证码 实时的数据 – 火车票余量 股票价格。。。
此时可以通过HTTP协议中缓存相关头 控制浏览器缓存机制:
response.setDateHeader("Expires", 0);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
============================================================
资源的三种条转方式:
请求转发:
在服务器内发生的资源跳转
一次请求一次响应,一个request对象,可以用request域在转发时传递数据
浏览器不知道请求转发的存在 地址栏仍然是原来的地址 刷新操作时仍然访问的是第一个资源的地址
地址是内部使用地址 不需要写应用名称
request.getRequestDispatcher("/index.jsp").forward(request,response);
请求重定向:
在服务器外发生的资源跳转
两次请求两次响应,两个request对象,不可以用request域在重定向时传递数据
浏览器知道请求重定向的存在 地址栏发生变化 刷新操作时访问的是最后一个资源地址
地址是外部使用地址 一定要写应用名称
response.sendRedirect("/Day10/index.jsp")
定时刷新:
在服务器外发生的资源跳转
两次请求两次响应,两个request对象,不可以用request域在重定向时传递数据
浏览器知道定时刷新的存在 地址栏发生变化 刷新操作时访问的是最后一个资源地址
地址是外部使用地址 一定要写应用名称
response.setHeader("refresh","3;url=/Day10/index.jsp")
三种跳转方式的选择:
如果需要利用request域传递数据 --> 必须请求转发
如果想要在资源跳转时改变地址栏 或 刷新操作 --> 请求重定向 或 定时刷新
如果想在资源跳转时,向用户提示一些信息 --> 定时刷新
如果以上三种需求都没有,三种方案都可以时,优先使用 请求转发,因为请求转发访问服务器的次数少,减轻服务器压力。
============================================================
二、ServletConfig
1.概述
每个Servlet都有一个ServletConfig对象 代表当前Servlet在web.xml中的配置信息
2.获取ServletConfig
Servlet在初始化时候 init方法中传入了ServletConfig对象
我们通常写的Servlet继承自HttpServlet - GenericServlet - Servlet
在GenericServlet中 实现了 Servlet接口定义的init方法 在其中将ServletConfig对象保存到了类的内部 比提供了 getServletConfig方法 返回该对象
所以作为GenericServlet的子孙类 可以直接调用getServletConfig方法来获取次对象
3.ServletConfig的功能
(1)获取Servlet名称
String getServletName()
(2)获取初始化参数
在Servlet在web.xml中的M<servlet>配置中 可以通过配置<init-param>来 配置当前Servlet的初始化参数 再在程序中通过ServletConfig对象来获取 从而 将一些不想写死在Servlet中的配置信息 提取到web.xml的当前Servlet的配置中
String getInitParameter(String name) 获取当前Servlet指定名称的初始化参数的值
Enumeration getInitParameterNames() 获取当前Servlet所有初始化参数的名字的枚举
三、ServletContext
1.概述
代表当前web应用。
2.ServletContext生命周期
当服务器启动时,服务器在启动时会依次加载web应用,每一个web应用加载完成后都会创建一个ServletContext对象唯一代表该web应用,这个对象一直存活,直到web应用移除出容器或服务器关闭时,随着应用销毁,ServletContext对象跟着销毁。
ServletContext一个应用只有一个,唯一的代表当前web应用,生命周期和web应用一样,web应用创建它就创建 web应用销毁它就跟着被销毁。
3.获取ServletContext
方法1:
ServletConfig.getServletContext();
方法2:
this.getServletContext();
4.ServletContext功能01 - 读取web应用初始化参数
可以在web.xml的根目录下 通过<context-param>来为整个web应用配置初始化参数
这些初始化参数可以通过ServletContext对象来获取
String getInitParameter(String name) 获取当前web应用指定名称的初始化参数的值
Enumeration getInitParameterNames() 获取当前web应用所有初始化参数的名字的枚举
5.ServletContext功能02 - 作为域对象来使用
生命周期
和web应用命一样长
作用范围
整个web应用
主要功能
在整个web应用范围内 整个web应用生命周期范围内 共享数据
setAttribute(String name,Object obj);//向域中设置数据
Object getAttribute(String name);//从域中获取数据
removeAttribute(String name);//从域中移除数据
6.ServletContext功能03 - 加载web资源
(1)路径难题
在web开发时,如果需要读取资源文件,需要写文件路径
如果写相对路径 - 则到当前程序的启动目录 tomcat/bin 下找文件 找不到 报错
如果写绝对路径 - 则到当前程序的启动目录 的根目录 下找文件 找不到 报错
如果写盘符开始的绝对路径 - 则从盘符开始找 能够找到这个文件 但是这种方法 将路径写死 一旦换一个发布环境 就无法使用了 所以不推荐
那么路径到底应该如何写呢?
(2)ServletContext解决路径难题
servletContext.getRealPath("xxxx")
此方法将会 在传入的路径前 拼接当前web应用的 盘符开始的绝对路径 从而得到 指定资源的盘符开始的绝对路径 从而访问到资源
因为应用的路劲格式getRealPath方法动态获取拼接的 传入的仅仅是资源相对于web应用根目录的路径 所以 并没有将路径写死 所以即使换了发布环境 路径也仍然正确
(3)利用类加载器加载资源
在web开发时,有时无法拿到ServletContext对象,此时如何解决路径难题呢?
此时可以使用类加载器加载资源文件
类加载器时虚拟机用来加载类的类
此类额外提供了方法 getResource 来获取资源的真实路径
需要注意的是,这个方法传入的路径必须是资源相对于类加载器默认加载类的路径
*********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
1. Response
1.1. Response概述
1.1.1. Response概述
Servlet中应该如何向用户输出数据呢?在doGet和doPost方法的参数中,HttpServletRequest代表的是http请求,而HttServletResponse代表的是http响应。想要获取请求中的信息时使用HttpServletRequest对象,而有数据需要发送给客户端时,就要用到HttpServletResponse对象了。
1.2. Response继承结构
1.2.1. Response继承结构
虽然我们经常简称为response,实际上是ServletResponse接口,其中定义了很多和响应对象相关的方法,HttpServletResponse是ServletResponse接口的子接口,在ServletResponse的基础上增加了很多和http协议相关的方法。如图-40所示:
图-40
1.3. Response常用方法
1.3.1. 设置状态码
setStatus(int sc)
1.3.2. 设置响应头
setIntHeader(String name, int value)
setHeader(String name, String value)
setDateHeader(String name, long date)
1.3.3. 获取输出流
PrintWriter getWriter()
ServletOutputStream getOutputStream();
1.4. Response输出信息到客户端
1.4.1. 输出信息到客户端api
查询api,在Response向外输出数据的方法有如下两个:
PrintWriter getWriter()
ServletOutputStream getOutputStream();
其中getWriter获取的是字符流,可以输出字符数据到客户端。
getOutputStream获取的是字节流,可以输出字节数据到客户端。
我们现在要将字符数据发送给客户端,可以调用getWriter方法向其中输出数据
经测试可以正确的输出。如图-41所示:
图-41
接着我们测试中文。发现输出时产生了乱码。如图-42所示:
图-42
1.4.2. 响应乱码处理
这个乱码是如何产生的呢?乱码的产生大多是由于编码和解码时的码表不同产生的。
那么服务器是以什么码表来发送数据呢?我们发现乱码是以“?”的形式出现的。根据我们的经验,这种问题多半是由ISO8859-1编码导致的。
确实是的,如果不指定,服务器默认将用iso8859-1进行编码发送数据。浏览器用什么码表打开呢?一般来说如果不指定,浏览器默认会用所在的操作系统的平台码,我们当前的中文系统中,默认就是使用GB2312作为解码码表的。
首先iso8859-1中没有中文,对于无法表示的字符,iso8859-1会用“?”来替代,所以真正发送给浏览器的数据其实是“?”,世界上所有的码表都默认兼容iso8859-1,所以gb2312认识,显示为了“?”。如图-43所示:
图-43
在解决这个问题时,可以通过设置response.setCharacterEncoding(“gbk”)来指定服务器发送数据时使用的码表。同时要注意,此行代码必须出现在任何输出数据的代码之前,如果在这行代码之前已经有任何数据写入给了response,则此行代码无效。
设置过后再重新测试。发现仍然是乱码,但不再是“??”而是变成了“涓浗”。如图-44所示:
图-44
这种类型的乱码是怎么发生的呢?我们接着分析。服务器用utf-8发送数据给浏览器,而浏览器用平台码(当前为gbk)gbk打开自然产生了乱码。如图-45所示:
图-45
这种乱码的产生是由于浏览器没有使用正确的编码打开造成的,那么我们该如何控制浏览器用指定码表打开数据呢?
在http协议中有一个响应头叫做Content-Type可以用来通知浏览器当前服务器发送的数据的格式,如果是字符格式的数据还可以指定解析时使用的码表。所以我们可以通过如下方法通知浏览器用指定码表打开发送的数据,代码如下,经测试没有乱码。
我们通过response.setHeader("Content-Type", "text/html;charset=utf-8");通知服务器发送数据时的码表。
通过response.setCharacterEncoding("utf-8");通知浏览器解析时使用的码表。
两码相同就不会有乱码了。
如图-46所示:
图-46
另外response提供了setContentType()快捷方法,在它的底层,会同时做上面两件事,所以可以一行代码解决response产生的乱码问题。如图-47所示:
图-47
1.4.3. Response输出数据时的细节
(1)getOutputStream和getWriter这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。 (2)Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎从response里面获取,Servlet引擎将这些数据当作响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。 (3)Serlvet的service方法结束后,Servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,如果没有,Servlet引擎tomcat将调用close方法关闭该输出流对象。
1.5. Response实现重定向
在HTTP协议中提供了302状态码和Location响应头,通知浏览器收到响应后立即自动访问Location中指定的地址,从而跳转访问另一个资源。
response.setState(302);
response.setHeader(“Location”,“。。。”);
经测试可以实现跳转效果。
同时我们打开HTTPWatch,发现在整个重定向的过程中发生了两次请求响应。
1.6. Response实现定时刷新
在HTTP协议中,提供了refresh响应头,可以命令浏览器经过多少秒刷新页面到哪个地址。
我们经常见到这样的效果,当注册成功后提示“恭喜您注册成功。。3秒后回到主页。。”,紧接着经过3秒回到主页。这就是定时刷新的效果。
通过如下代码实现。
1.7. Response实现禁止缓存
浏览器为了减少对服务器的访问,会在第一次访问到资源后进行缓存,之后再访问,就直接用缓存中的数据,减少对服务器的访问。
这种缓存机制,提高了浏览器的响应数度,减少了服务器的访问量,在大多数情况下是好的,但是在某些场景下可能有问题。
比如:验证码 实时的数据 – 火车票余量 股票价格。。。
response.setDateHeader("Expires", 0);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
2. ServletConfig
2.1. ServletConfig概述
2.1.1. ServletConfig是什么
ServletConfig代表当前Servlet在web.xml中的配置信息。
2.2. 获取ServletConfig
2.2.1. 获取ServletConfig
在Servlet接口中init方法参数就是ServletConfig,在Servlet创建出来时,init方法立即被容器调用,由容器传入ServletConfig对象。
在GenericServlet中,实现了这个方法,将ServletConfig设置成了类的成员变量,并提供了getServletConfig方法,获取该对象。
我们的Servlet都间接继承子GenericServlet,所以可以在自己的Servlet中直接调用getServletConfig方法获取这个对象。
2.3. ServletConfig获取初始化参数
2.3.1. 配置初始化参数
在web.xml中的<servlet>标签中可以配置零个或多个<init-param>标签,用来为当前Servlet配置一些自定义的参数--称为Serlvet的初始化参数。如图-34所示:
图-34
2.3.2. 通过ServletConfig获取初始化参数
在ServletConfig身上提供了如下方法获取该Servlet上配置的初始化参数。如图-35所示:
图-35
通过实验发现可以获取Servlet中配置的初始化参数。
如图-36所示:
图-36
3. ServletContext对象
3.1. ServletContext对象概述
ServletContext对象代表当前web应用。当服务器启动时,服务器在启动时会依次加载web应用,每一个web应用加载完成后都会创建一个ServletContext对象唯一代表该web应用,这个对象一直存活,直到web应用移除出容器或服务器关闭时,随着应用销毁,ServletContext对象跟着销毁。
3.2. 获取ServletContext对象
在ServletConfig对象身上提供了getServletContext()方法可以用来获取ServletContext对象。
而在GenericServlet中提供了便捷的getServletContext(),我们编写的servlet继承自GenericServlet,所以可以直接调用此方法,便捷的获取ServletContext。
3.3. ServletContext对象读取web应用初始化参数
我们可以在web.xml中利用<context-param>标签为整个web应用配置初始化信息
<context-param>
<param-name>encode</param-name>
<param-value>utf-8</param-value>
</context-param>
可以利用ServletContext对象的如下方法获取初始化信息:
ServletContext.getInitParameter();
ServletContext.getInitParameterNames();
之前我们在学习Servlet时,知道Servlet的web.xml配置中可以配置初始化参数,但是这种初始化参数只能在该Servlet内部获取。而为整个web应用配置的初始化参数,通过ServletContext可在整个web应用中使用。
一般来说,整个站点的编码应该是一致的,所以我们可以修改之前的代码,将配给servlet的编码集改为整个站点配置,通过ServletContext获取。
3.3.1. ServletContext对象作为域对象使用
域:一个对象具有了可以被看见的范围,利用这个对象身上的Map,在这个范围内共享数据,这样的对象叫做域对象
javaweb开发中一共有4大域对象
ServletContext域:
作用范围:
整个web应用
生命周期:
当服务器启动web应用加载后立即创建代表这个web应用的ServletContext对象,一直驻留在内存中,唯一的代表这个web应用,直到服务器关闭或web应用被移除出容器时,随着web应用的销毁,ServletContext对象销毁
操作域的方法:
setAttribute(String name,Object obj);//向域中设置数据
Object getAttribute(String name);//从域中获取数据
removeAttribute(String name);//从域中移除数据
3.4. ServletContext对象读取web资源
3.4.1. 路径难题
在web环境中读取资源时,如果写的是相对路径,则在tomcat/bin目录下寻找资源。如果写的是绝对路径,则在tomcat所在目录的根目录下寻找资源。如果写的是盘符开始的绝对路径可以正确找到资源,但是一旦改变了发布环境路径很可能是错的。我们发现无论怎么写路径都有问题。
3.4.2. ServletContext读取资源
面对路径难题,ServletContext为我们提供管理解决方法。
servletContext.getRealPath("xxxx");--这个方法会在传入的路径前拼接当前web应用的硬盘路径,从而拼接出资源的硬盘路径,从而可以找到资源。
这种方式没有将web应用的硬盘路径写死,所以即使换了一个发布环境,路径也是正确的
3.4.3. 类加载器读取资源
如果当前程序中没有ServletContext可以用,此时可以拜托类加载器帮我们加载资源:
类加载器可以加载.class文件,同样也可以加载其他类型的资源文件
在ClassLoader上提供了getResource("相对于类加载目录的路径")可以加载资源,但是要求这个路径给一个相对于类加载器加载类的目录的路径(WEB-INF/classes)
4. 实现资源跳转
4.1. 三种资源跳转方式
Request对象请求转发
Response对象请求重定向
Response对象定时刷新
4.2. 三种资源跳转方式的比较
4.2.1. 不同特点
转发是服务器内部资源跳转,重定向是通过302+Location实现浏览器跳转访问。
转发一次请求一次响应,重定向两次请求两次响应
转发地址栏不发生变化,重定向地址栏会发生变化
转发之前和转发之后request是一个,重定向之前和之后不是一个request
4.2.2. 应用场景
转发和重定向都能实现资源的跳转,那什么时候用重定向什么时候用转发呢?
如果希望资源跳转的过程中使用request域传输数据,必须用转发
如果希望资源跳转时想要改变地址栏的地址,此时必须用重定向
如果某些应用场景下,既不需要用request域传输据也不需要改变地址栏,此时两种都可以,但是推荐使用请求转发,可以减少对服务器的访问次数,从而减轻服务器的压力
如果在资源跳转之前想要向客户端输出些信息用定时刷新。