Servlet总结
目录
一、主要内容(XMind图)
二、HTTP简介
1、概述
- HTTP协议(Hypertext Tansfer Protocol,超文本传输协议),是一 个客户端请求和响应的标准协议,这个协议详细规定了浏览器和万维网服务器之间互相通信的规则。用户输入地址和端口号之后就可以从服务器上取得所需要的网页信息。
- 通信规则规定了客户端发送给服务器的内容格式,也规定了服务器发送给客户端的内容格式。客户端发送给服务器的格式叫"请求协议";服务器发送给客户端的格式叫”响应协议"。
2、格式与流程
- 服务器端资源需要通过浏览器进行,此时由浏览器将我们给出的请求解析为满足HTTP协议的格式并发出。我们发出的请求格式需要按照浏览器规定的格式来书写。当浏览器获取到信息以后,按照特定格式解析并发送即可。接收到服务器端给出的响应时,也按照HTTP协议进行解析获取到各个数据,最后按照特定格式展示给用户。在浏览器中书写格式如下:
3、HTTP协议的特点
- 支持客户/服务器模式。
- 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、POST. 每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
- 灵活: HTTP允许传输任意类型的数据对象。传输的类型由Content Type加以标记。
- 无连接:无连接是表示每次连接只处理一个请求。 服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
HTTP1.1版本后支持可持续连接。通过这种连接,就有可能在建立-个TCP连接后,发送请求并得到回应,然后发送更多的请求并得到更多的回应通过把建立和释放TCP连接的开销分摊到多个请求上,则对于每个请求而言,由于TCP而造成的相对开销被大大地降低了。而且,还可以发送流水线请求,也就是说在发送请求 1之后的回应到来之前就可以发送请求2.也可以认为,一次连接发送多个请求,由客户机确认是否关闭连接,而服务器会认为这些请求分别来自不同的客户端。 - 无状态: HTTP 协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
4、HTTP的URL
- HTTP (超文本传输协议)是一个基于请求与响应模式的、应用层的协议,常基于TCP的连接方式,绝大多数的Web开发,都是构建在HTTP协议之上的Web应用。
- HTTP URL (URL是-种特殊类型的URI,包含了用于查找某个资源的足够的信息)的格式如下:
http://host[:port]/[abc_path]
http://IP(主机名/域名) :端口/访问的资源路径
- http 表示要通过HTTP协议来定位网络资源;
- host 表示合法的Internet主机域名或者IP地址;
- port指定一 个端口号,为空则使用缺省端口80;
- abc_ path指定请求资源的URI;如果URL中没有给出abc_ path,那么当它作为请求URI时,必须以"/"的形式给出,通常这个工作浏览器自动帮我们完成。
5、HTTP请求
- HTTP请求由三部分组成,分别是:请求行、请求头、请求正文。
- 通过chrome浏览器,F12 -> Network查看。
5.1、Get请求(没有请求体)
5.2、Post请求
- 请求行以一个方法符号开头,以空格分开,后面跟着请求的URI和协议的版本。格式如下: Method Request-URI HTTP-Version CRLF
- Method表示请求方法;
- Request-URI是一个统一 资源标识符;
- HTTP-Version表示请求的HTTP协议版本;
- CRLF表示回车和换行;
6、HTTP响应
- 在接收和解释请求消息后,服务器返回一个HTTP响应消息。HTTP响应也是由三个部分组成,分别是:状态行、消息报头、响应正文。
7、消息头
- HTTP消息由客户端到服务器的请求和服务器到客户端的响应组成。请求消息和响应消息都是由开始行(对于请求消息,开始行就是请求行,对于响应消息,开始行就是状态行),消息报头(可选),空行(只有CRLF的行),消息正文(可选)组成。
- 每一个报头域都是由名字 +" : "+空格+值组成,消息报头域的名字是不区分大小写的。
7.1、请求头
- 请求报头允许客户端向服务器端传递请求的附加信息以及客户端自身的信息。
- Referer: 该请求头指明请求从哪里来。
- 如果是地址栏中输入地址访问的都没有该请求头地址栏输入地址,通过请求可以看到,此时多了一个Referer的请求头,并且后面的值为该请求从哪里发出。比如:百度竞价,只能从百度来的才有效果,否则不算;通常用来做统计工作、防盗链。
7.2、响应头
- 响应报头允许服务器传递不能放在状态行中的附加响应信息,以及关于服务器的信息和对Request- URI所标识的资源进行下一步访问的信息。
- Location: Location响应报头 域用于重定向接受者到一个新的位置。
- Location响应报头域,常用在更换域名的时候。
response.sendRedirect("http://www.baidu.com");
- Refresh: 自动跳转(单位是秒), 可以在页面通过met标签实现,也可在后台实现。
<meta http-equiv="refresh" content="3;ur1=http://www.baidu.com">
二、Servlet的实现
-
Servlet是Server与Applet的缩写,是服务端小程序的意思。使用Java语言编写的服务器端程序,可以像生成动态的WEB页,Servlet 主要运行在服务器端,并由服务器调用执行,是-种按照Servlet标准来开发的类。是SUN公司提供的一门用于开发动态Web资源的技术。(言外之意: 要实现web开发,需要实现Servlet标准)
-
Servlet本质上也是lava类,但要遵循Servlet规范进行编写,没有main0方法,它的创建、使用、销毁都由Servlet容器进行管理(如Tomcat)。(言外之意: 写自己的类,不用写main方法,别人自动调用)
-
Servlet是和HTTP协议是紧密联系的,其可以处理HTTP协议相关的所有内容。这也是Servlet应用广泛的原因之一。
-
提供了Servlet 功能的服务器,叫做Servlet容器,其常见容器有很多,如Tomcat, Jetty, WebLogic,Server, WebSphere, JBoss等等。
1、Servlet的实现
1.1、新建类
package com.xxxx.servlet;
public class Servlet01 {
}
1.2、实现Servlet规范
package com.xxxx.servlet;
import javax.servlet.http.HttpServlet;
public class Servlet01 extends HttpServlet {
}
1.3、重写service方法
- 满足Servlet规范只是让我们的类能够满足接收请求的要求,接收到请求后需要对请求进行分析,以及进行业务逻辑处理,计算出结果,则需要添加代码,在规范中有一个叫做service的方法,专门用来做请求处理的操作,业务代码则可以写在该方法中。
package com.xxxx.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("Hello Servlet!");
resp.getWriter().write("Hello World");
}
}
1.4、设置注解
- 在完成好了一切代码的编写后,还需要向服务器说明,特定请求对应特定资源。
- 开发servlet项目,使用@WebServlet将一个继承于javax.servlet.http.HttpServlet 的类定义为Servlet组件。在Servlet3.0中 ,可以使用@WebServlet注解将一个继承 于javax.servlet.http.HttpServlet的类标注为可以处理用户请求的Servlet。
package com.xxxx.servlet;
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("/ser01")
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("Hello Servlet!");
resp.getWriter().write("Hello World");
}
}
- 注解配置Servlet
@WebServlet(name="Servlet01",value="/ser01")
@WebServlet(name="Servlet01",urlPatterns = "/ser01")
//配置多个访问路径
@WebServlet(name="Servlet01",value={"/ser01",'/ser001'})
@WebServlet(name="Servlet01",urlPatterns={"/ser01",'/ser001'})
1.5、发布项目并启动服务
- 到此,需要编写和配置的地方已经完成,项目已经完整了,但是如果需要外界能够访问,还需要将项目发布到服务器.上并运行服务器。
1.6、访问并查看结果
-
在项目正确发布到服务器上之后,用户即可通过浏览器访问该项目中的资源。注意url的格式正确,tomcat的端口为8080。
-
浏览器访问地址: http://localhost:8080/s01/ser01
2、Servlet的工作流程
- 通过请求头获知浏览器访问的是哪个主机
- 再通过请求行获取访问的是哪个- -个web应用
- 再通过请求行中的请求路径获知访问的是哪个资源
- 通过获取的资源路径在配置中匹配到真实的路径,
- 服务器会创建servlet对象,(如果是第一 次访问时, 创建servlet实例, 并调用init方法进行初始化操作)
- 调用service (request, response) 方法来处理请求和响应的操作
- 调用service完毕后返回服务器由服务器讲response缓冲区的数据取出,以http响应的格式发送给浏览器.
3、Servlet生命周期
-
Servlet没有main()方法,不能独立运行,它的运行完全由Servlet引擎来控制和调度。所谓生命周期,指的是servlet容器何时创建servlet实例、何时调用其方法进行请求的处理、何时进行销毁其实例的整个过程。
- 实例和初始化时机
- 当请求到达容器时,容器查找该servlet对象是否存在,如果不存在,则会创建实例并进行初始化。
- 就绪/调用/服务阶段
- 有请求到达容器,容器调用servlet 对象的service()方法处理请求的方法在整个生命周期中可以被多次调用; HttpServlet 的service()方法,会依据请求方式来调用doGet()或者doPost()方法。但是,这两个do方法默认情况下,会抛出异常,需要子类去override。
- 销毁时机
- 当容器关闭时(应用程序停止时),会将程序中的Servlet实例进行销毁。
- 上述的生命周期可以通过Servlet中的生命周期方法来观察。在Servlet中有三个生命周期方法,不由用户手动调用,而是在特定的时机有容器自动调用,观察这三个生命周期方法即可观察到Servlet的生命周期。
- 实例和初始化时机
-
init方法,在Servlet实例创建之后执行(证明该Servlet有实例创建了)
- 若重写init()方法,则需要在第一行调用父类的init方法,不然会报错,具体原因见 ServletConfig类 。
public void init(ServletConfig config) throws Serv1etException {
System.out.print1n("实例创建了...");
}
- service方法,每次有请求到达某个Servlet 方法时执行,用来处理请求(证明该Servlet 进行服务了)
protected void service (HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException {
System.out.println("服务调用了...");
}
- destroy方法,Servlet 实例销毁时执行(证明该Servlet的实例被销毁了)
public void destroy() {
System.out.println("实例销毁了...);
}
- Servlet的生命周期,简单的概括这就分为四步: servlet 类加载–>实例化–>服务–>销毁。
- 下面我们描述一下 Tomcat与Servlet 是如何工作的,看看下面的时序图:
- Web Client向Servlet容器(Tomcat) 发出Http请求
- Servlet容器接收Web Client的请求
- Servlet容器创建一个HttpServletRequest对象,将Web Client请求的信息封装到这个对象中
- Servlet容器创建一个HttpServletResponse对象
- Servlet容器调HttpServlet对象service方法,把Request与Response作为参数,传给HttpServlet
- HttpServlet调用HttpServletRequest对象的有关方法,获取Http请求信息
- HttpServlet调用HttpServletResponse对象的有关方法,生成响应数据
- Servlet容器把HttpServlet的响应结果传给Web Client
4、请求分发处理
4.1、继承Servlet类,重写server()方法
@WebServlet("/ser02")
public class Servlet02 implements Servlet {
@Override
protected void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("server");
// 类型转换(因为它有 getMethod() 方法)
//只有Servlet的子类HttpServlet才有getMethod()方法
HttpServletRequest req = (HttpServletRequest) servletRequest;
//获取请求方式
String method = req.getMethod();
if ("GET".equals(method)) {
doGet();
} else if ("POST".equals(method)) {
doPost();
}
}
/**
* 做 get 请求的操作
*/
public void doGet(){
System.out.println("get 请求");
}
/**
* 做 post 请求的操作
*/
public void doPost(){
System.out.println("post 请求");
}
}
4.2、继承HttpServlet类,重写doGet()和doPost()方法,使用注解
@WebServlet("/ser03")
public class Servlet03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get请求");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post请求");
}
}
- 若使用配置,则web.xml中:
<servlet>
<servlet-name>Servlet03</servlet-name>
<servlet-class>com.itlazy.servlet.Servlet03</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet03</servlet-name>
<url-pattern>/ser03</url-pattern>
</servlet-mapping>
5、Servlet类的继承体系
三、ServletConfig 类
- ServletConfig 类从类名上来看,就知道是 Servlet 程序的配置信息类。
- Servlet 程序和 ServletConfig 对象都是由 Tomcat 负责创建,我们负责使用。
- Servlet 程序默认是第一次访问的时候创建,ServletConfig 是每个 Servlet 程序创建时,就创建一个对应的 ServletConfig 对象
1、ServletConfig 的三大作用
- 可以获取 Servlet 程序的别名 servlet-name 的值
- 获取初始化参数 init-param
- 获取 ServletContext 对象
<!-- servlet 标签给 Tomcat 配置 Servlet 程序 -->
<servlet>
<!--servlet-name 标签 Servlet 程序起一个别名(一般是类名) -->
<servlet-name>HelloServlet</servlet-name>
<!--servlet-class 是 Servlet 程序的全类名 -->
<servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
<!--init-param 是初始化参数 -->
<init-param>
<!-- 是参数名 -->
<param-name>username</param-name>
<!-- 是参数值 -->
<param-value>root</param-value>
</init-param>
<!--init-param 是初始化参数 -->
<init-param>
<!-- 是参数名 -->
<param-name>url</param-name>
<!-- 是参数值 -->
<param-value>jdbc:mysql://localhost:3306/test</param-value>
</init-param>
</servlet>
<!--servlet-mapping 标签给 servlet 程序配置访问地址 -->
<servlet-mapping>
<!--servlet-name 标签的作用是告诉服务器,我当前配置的地址给哪个 Servlet 程序使用 -->
<servlet-name>HelloServlet</servlet-name>
<!--
url-pattern 标签配置访问地址 <br/>
/ 斜杠在服务器解析的时候,表示地址为: http://ip:port/ 工程路径 <br/>
/hello 表示地址为: http://ip:port/ 工程路径 /hello <br/>
-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
- servlet代码:
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("2 init 初始化方法");
// 1 、可以获取 Servlet 程序的别名 servlet-name 的值
System.out.println("HelloServlet 程序的别名是:" + servletConfig.getServletName());
// 2 、获取初始化参数 init-param
System.out.println(" 初始化参数 username 的值是;" + servletConfig.getInitParameter("username"));
System.out.println(" 初始化参数 url 的值是;" + servletConfig.getInitParameter("url"));
// 3 、获取 ServletContext 对象
System.out.println(servletConfig.getServletContext());
}
- 注意:若重写init()方法,必须得调用父类的init方法,并传入config。因为父类方法有初始化config操作,若不调用,则GenericServlet类中的属性servletConfig就为空。
四、ServletContext类
- 每一个web应用都有且仅有一个ServletContext对象,又称Application对象,从名称中可知,该对象是与应用程序相关的。在WEB容器启动的时候,会为每一个WEB应用程序创建一个对应的ServletContext对象。
- 该对象有两大作用,第一:作为域对象用来共享数据,此时数据在整个应用程序中共享;第二:该对象中保存了当前应用程序相关信息。例如可以通过getServerlnfo() 方法获取当前服务器信息,getRealPath(String path)获取资源的真实路径等。
1、什么是 ServletContext
- ServletContext 是一个接口,它表示 Servlet 上下文对象
- 一个 web 工程,只有一个 ServletContext 对象实例。
- ServletContext 对象是一个域对象。
- ServletContext 是在 web 工程部署启动的时候创建。在 web 工程停止的时候销毁。
2、ServletContext获取
//通过request对象获取
ServletContext servletContext = request.getServletContext();
//通过session对象获取
ServletContext servletContext = request.getSession().getServletContext();
//通过 servletConfig 对象获取,在 Servlet 标准中提供了 ServletConfig 方法
ServletConfig servletConfig = getServletConfig();
ServletContext servletContext = servletConfig.getServletContext();
//直接获取,Servlet 类中提供了直接获取 ServletContext 对象的方法
ServletContext servletContext = getServletContext();
3、ServletContext类的四个作用
- 获取 web.xml 中配置的上下文参数 context-param
- 获取当前的工程路径,格式: /工程路径
- 获取工程部署后在服务器硬盘上的绝对路径
- 获取当前服务器的版本信息
- 像 Map 一样存取数据
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
// 1 、获取web.xml中配置的上下文参数 context-param
ServletContext context = getServletConfig().getServletContext();
String username = context.getInitParameter("username");
System.out.println("context-param 参数 username 的值是:" + username);
System.out.println("context-param 参数 password 的值是:" + context.getInitParameter("password"));
// 2 、获取当前的工程路径。结果: /工程路径
System.out.println( " 当前工程路径:" + context.getContextPath() );
// 3 、获取工程部署后在服务器硬盘上的绝对路径
//斜杠被服务器解析地址为 :http://ip:port/工程名/映射到 IDEA 代码的 web 目录
System.out.println(" 工程部署的路径是:" + context.getRealPath("/"));
System.out.println(" 工程下 css 目录的绝对路径是:" + context.getRealPath("/css"));
System.out.println(" 工程下imgs目录1.jpg的绝对路径是:" + context.getRealPath("/imgs/1.jpg"));
//4、获取当前服务器的版本信息
String serverInfo = request.getServletContext().getServerInfo();
}
- web.xml配置:
<!--context-param 是上下文参数 ( 它属于整个 web 工程 )-->
<context-param>
<param-name>username</param-name>
<param-value>context</param-value>
</context-param>
<!--context-param 是上下文参数 ( 它属于整个 web 工程 )-->
<context-param>
<param-name>password</param-name>
<param-value>root</param-value>
</context-param>
- 像 Map 一样存取数据:
//5、像Map一样存储数据
public class ContextServlet1 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取 ServletContext 对象
ServletContext context = getServletContext();
System.out.println(context);
System.out.println(" 保存之前: Context1 取 获取 key1 的值是:"+ context.getAttribute("key1"));
context.setAttribute("key1", "value1");
System.out.println("Context1 中获取域数据 key1 的值是:"+ context.getAttribute("key1"));
}
}
4、ServletContext域对象
- 什么是域对象?
- 域对象,是可以像 Map 一样存取数据的对象,叫域对象。
- 这里的域指的是存取数据的操作范围,整个 web 工程。
存数据 | 取数据 | 删除 数据 | |
---|---|---|---|
Map | put() | get() | remove() |
域对象 | setAttribute() | getAttribute() | removeAttribute() |
// 获取ServletContext对象
ServletContext servletContext = request.getServletContext();
// 设置域对象
servletContext.setAttribute("name","zhangsan");
// 获取域对象
String name = (String) servletContext.getAttribute("name");
// 移除域对象
servletContext.removeAttribute("name");
五、HttpServletRequest 类
- HttpServletRequest 对象:主要作用是用来接收客户端发送过来的请求信息,例如:请求的参数,发送的头信息等都属于客户端发来的信息,service()方法中形参接收的是 HttpServletRequest 接口的实例化对象,表示该对象主要应用在 HTTP 协议上,该对象是由 Tomcat 封装好传递过来。
1、常用方法
方法名 | 作用 |
---|---|
getRequestURI() | 获取请求的资源路径 |
getRequestURL() | 获取请求的统一资源定位符(绝对路径) |
getRemoteHost() | 获取客户端的 ip 地址 |
getHeader() | 获取请求头 |
getProtocol() | 获取Http版本号 |
getContextPath() | 获取webapp的名字 |
getMethod() | 获取请求的方式 GET 或 POST |
- 示例:
//请求 http://localhost:8080/servlet01/ser04?name=admin&pwd=123456
// 获取客户端请求的完整URL (从http开始,到?前⾯结束)
String url = request.getRequestURL().toString();
//http://localhost:8080/servlet01/ser04
System.out.println("获取客户端请求的完整URL:" + url);
// 获取客户端请求的部分URL (从站点名开始,到?前⾯结束)
String uri = request.getRequestURI();
// /servlet01/ser04
System.out.println("获取客户端请求的部分URL:" + uri);
// getRemoteHost() 获取客户端的 ip 地址
/**
* 在 IDEA 中,使用 localhost 访问时,得到的客户端 ip 地址是 ===>>> 127.0.0.1
* 在 IDEA 中,使用 127.0.0.1 访问时,得到的客户端 ip 地址是 ===>>> 127.0.0.1
* 在 IDEA 中,使用 真实 ip 访问时,得到的客户端 ip 地址是 ===>>> 真实的客户端 ip 地址
*/
System.out.println("端 客户端 ip 址 地址 => " + req.getRemoteHost());
//getHeader() 获取请求头
System.out.println(" 请求头 User-Agent ==>> " + req.getHeader("User-Agent"));
// 获取HTTP版本号
String protocol = request.getProtocol();
//HTTP/1.1
System.out.println("获取HTTP版本号:" + protocol);
// 获取webapp名字 (站点名)
String webapp = request.getContextPath();
// /servlet01
System.out.println("获取webapp名字:" + webapp);
// 获取客户端的请求方式
String method = request.getMethod();
//GET
System.out.println("获取客户端的请求方式:" + method);
2、获取请求参数
方法名 | 作用 |
---|---|
getParameter() | 获取请求的参数 |
getParameterValues() | 获取请求的参数(多个值的时候使用) |
getQueryString() | 获取请求行中的参数部分 |
//请求 http://localhost:8080/servlet01/ser04?name=admin&pwd=123456
// 获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobby = req.getParameterValues("hobby");
System.out.println(" 用户名:" + username);
System.out.println(" 密码:" + password);
System.out.println(" 兴趣爱好:" + Arrays.asList(hobby));
// 获取请求行中的参数部分 ?后面的
String queryString = request.getQueryString();
//name=admin&pwd=123456
System.out.println("获取请求行中的参数部分:" + queryString);
3、请求乱码问题
- 由于现在的 request 属于接收客户端的参数,所以必然有其默认的语言编码,主要是由于在解析过程中默认使用的编码方式为 ISO-8859-1(此编码不支持中文),所以解析时一定会出现乱码。要想解决这种乱码问题,需要设置 request 中的编码方式,告诉服务器以何种方式来解析数据。或者在接收到乱码数据以后,再通过相应的编码格式还原。
3.1、Get请求解决乱码问题
// 获取请求参数
String username = req.getParameter("username");
//1 先以 iso8859-1 进行编码
//2 再以 utf-8 进行解码
//该方法对get和post都有效
username = new String(username.getBytes("iso-8859-1"), "UTF-8");
- 借助了String 对象的方法,该种方式对任何请求有效,是通用的。
- Tomcat8起,以后的GET方式请求是不会出现乱码的。
3.2、Post请求解决乱码问题
// 设置请求体的字符集为 UTF-8 ,从而解决 post 请求的中文乱码问题
req.setCharacterEncoding("UTF-8");
System.out.println("-------------doPost------------");
// 获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobby = req.getParameterValues("hobby");
System.out.println(" 用户名:" + username);
System.out.println(" 密码:" + password);
System.out.println(" 兴趣爱好:" + Arrays.asList(hobby));
4、请求转发
- 请求转发,是一种服务器的行为,当客户端请求到达后,服务器进行转发,此时会将请求对象进行保存,地址栏中的 URL 地址不会改变,得到响应后,服务器端再将响应发送给客户端,从始至终只有一个请求发出。
方法名 | 作用 |
---|---|
getRequestDispatcher() | 获取请求转发对象 |
request.getRequestDispatcher(url).forward(request,response);
5、请求转发跳转问题(base)
- base标签的作用:
- 请求转发之后,跳转会以地址栏中的地址作为参照物,而请求转发时候地址栏不变,跳转时就会出错。
- 请求转发之后,跳转会以地址栏中的地址作为参照物,而请求转发时候地址栏不变,跳转时就会出错。
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--base 标签设置页面相对路径工作时参照的地址
href 属性就是参数的地址值
-->
<!--
..表示找该网页地址栏的上一层目录,若href最后不加/斜杠,则servlet会认为是个资源,
会多往上找一层目录,若找到8080/,还有../就不继续往上找了,直接为根路径。
即以上图的错误路径的找法。
-->
<base href="http://localhost:8080/07_servlet/a/b/">
</head>
<body>
这是 a 下的 b 下的 c.html 页面<br/>
<a href="../../index.html">跳回首页</a><br/>
</body>
</html>
6、request作用域
- 通过该对象可以在一个请求中传递数据,作用范围:在一次请求中有效,即服务器跳转有效。
方法名 | 作用 |
---|---|
setAttribute() | 设置域数据 |
getAttribute() | 获取域数据 |
// 设置域对象内容
request.setAttribute(String name, String value);
// 获取域对象内容 默认返回Object,若要返回String,则需要强转
request.getAttribute(String name);
// 删除域对象内容
request.removeAttribute(String name);
- request 域对象中的数据在一次请求中有效,则经过请求转发,request 域中的数据依然存在,则在请求转发的过程中可以通过 request 来传输/共享数据。
7、web中的相对路径与绝对路径
-
在 javaWeb 中,路径分为相对路径和绝对路径两种:
-
相对路径是:
- . 表示当前目录
- … 表示上一级目录
- 资源名 表示当前目录/资源名
-
绝对路径:
- http://ip:port/工程路径/资源路径
-
在实际开发中,路径都使用绝对路径,而不简单的使用相对路径。
- 绝对路径
- base+相对
8、web中/斜杠的不同意义
-
在 web 中 / 斜杠 是一种绝对路径。
-
/ 斜杠 如果被浏览器解析,得到的地址是:http://ip:port/
<a href="/">斜杠</a>
- / 斜杠 如果被服务器解析,得到的地址是:http://ip:port/工程路径
- /servlet1
- servletContext.getRealPath(“/”);
- request.getRequestDispatcher(“/”);
- 特殊情况: response.sendRediect(“/”); 把斜杠发送给浏览器解析。得到 http://ip:port/
六、HttpServletResponse 类
- Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的 request 对象和代表响应的 response 对象。
- request 和 response 对象代表请求和响应:获取客户端数据,需要通过 request 对象;向客户端输出数据,需要通过 response 对象。
- HttpServletResponse 的主要功能用于服务器对客户端的请求进行响应,将 Web 服务器处理后的结果返回给客户端。service()方法中形参接收的是 HttpServletResponse 接口的实例化对象,这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。
1、响应数据
-
接收到客户端请求后,可以通过 HttpServletResponse 对象直接进行响应,响应时需要获取输出流。
-
有两种形式:
- getWriter() 获取字符流(只能响应回字符)
- getOutputStream() 获取字节流(能响应一切数据)
-
响应回的数据到客户端被浏览器解析。
-
注意:两者不能同时使用
// 字符输出流
PrintWriter writer = response.getWriter();
writer.write("Hello");
writer.write("<h2>Hello</h2>");
// 字节输出流
ServletOutputStream out = response.getOutputStream();
out.write("Hello".getBytes());
out.write("<h2>Hello</h2>".getBytes());
// 设置响应MIME类型,
// 同时设置服务端与客户端的编码格式
// text/html:表示可以识别html标签
// charset:设置编码格式
response.setContentType("text/html;charset=utf-8");
2、响应乱码问题
-
两种响应数据方式都适用下面方法。
-
解决响应中文乱码方案一(不推荐使用):
// 设置服务器字符集为 UTF-8
resp.setCharacterEncoding("UTF-8");
// 通过响应头,设置浏览器也使用 UTF-8 字符集
resp.setHeader("Content-Type", "text/html; charset=UTF-8");
- 解决响应中文乱码方案二(推荐):
// 它会同时设置服务器和客户端都使用 UTF-8 字符集,还设置了响应头
// 此方法一定要在获取流对象之前调用才有效
resp.setContentType("text/html; charset=UTF-8");
- 两种响应方式的使用:
// 设置客户端的响应类型及编码
response.setHeader("content-type","text/html;charset=UTF-8");
// 得到字符输出流
PrintWriter writer = response.getWriter();
//getWriter()设置完编码格式后只需写入字符串即可,就不会乱码
writer.write("<h2>你好</h2>");
// 设置客户端的编码及响应类型
ServletOutputStream out = response.getOutputStream();
response.setHeader("content-type","text/html;charset=UTF-8");
//设置编码类型之后,getBytes也要设置一样的编码类型写出,不然会乱码
out.write("<h2>你好</h2>".getBytes("UTF-8"));
3、请求重定向
- 请求重定向,是指客户端给服务器发请求,然后服务器告诉客户端说。我给你一些地址。你去新地址访问。叫请求重定向(因为之前的地址可能已经被废弃)。
3.1、请求重定向的两种方法
-
请求重定向的第一种方案:
-
// 设置响应状态码 302 ,表示重定向,(已搬迁)
resp.setStatus(302);
-
// 设置响应头,说明 新的地址在哪里
resp.setHeader(“Location”, “http://localhost:8080”);
-
-
请求重定向的第二种方案(推荐使用):
- resp.sendRedirect(“http://localhost:8080”);
3.2、请求重定向与请求转发的区别
七、Cookie对象
-
Cookie是浏览器提供的一种技术,通过服务器的程序能将一些只须保存在客户端,或者在客户端进行处理的数据,放在本地的计算机上,不需要通过网络传输,因而提高网页处理的效率,并且能够减少服务器的负载,但是由于 Cookie 是服务器端保存在客户端的信息, 所以其安全性也是很差的。例如常见的记住密码则可以通过 Cookie 来实现。
-
有一个专门操作Cookie的类 javax.servlet.http.Cookie。随着服务器端的响应发送给客户端,保存在浏览器。当下次再访问服务器时把Cookie再带回服务器。
-
Cookie 的格式:键值对用“=”链接,多个键值对间通过“;”隔开。
-
浏览器允许每个域名所包含的cookie数:
- Microsoft指出InternetExplorer8增加cookie限制为每个域名50个,但IE7似乎也允许每个域名50个cookie。
- Firefox每个域名cookie限制为50个。
- Opera每个域名cookie限制为30个。
- Safari/WebKit貌似没有cookie限制。但是如果cookie很多,则会使header大小超过的处理的限制,会导致错误发生。
- 注:“每个域名cookie限制为20个”将不再正确!
1、Cookie的创建
- servlet中的代码:
protected void createCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
//1 创建 Cookie 对象
Cookie cookie = new Cookie("key4", "value4");
//2 通知客户端保存 Cookie
resp.addCookie(cookie);
//1 创建 Cookie 对象
Cookie cookie1 = new Cookie("key5", "value5");
//2 通知客户端保存 Cookie
resp.addCookie(cookie1);
resp.getWriter().write("Cookie 创建成功");
}
2、服务器获取cookie
- servlet代码:
// 获取Cookie数组
Cookie[] cookies = request.getCookies();
// 判断数组是否为空
if (cookies != null && cookies.length > 0) {
// 遍历Cookie数组
for (Cookie cookie : cookies){
System.out.println(cookie.getName());
System.out.println(cookie.getValue());
}
}
3、Cookie值的修改
// 方案一:
// 1 、先创建一个要修改的同名的 Cookie 对象
// 2 、在构造器,同时赋于新的 Cookie 值。
Cookie cookie = new Cookie("key1","newValue1");
// 3 、调用 response.addCookie( Cookie ); 通知 客户端 保存修改
resp.addCookie(cookie);
// 方案二:
// 1 、先查找到需要修改的 Cookie 对象
// 获取Cookie数组
Cookie[] cookies = request.getCookies();
Cookie cookie = null;
// 判断数组是否为空
if (cookies != null && cookies.length > 0) {
// 遍历Cookie数组
for (Cookie cook : cookies){
if(cook.getName().equals("key1")){
cookie = cook.getValue();
}
}
}
if (cookie != null) {
// 2 、调用 setValue() 方法赋于新的 Cookie 值。
cookie.setValue("newValue2");
// 3 、调用 response.addCookie() 通知客户端保存修改
resp.addCookie(cookie);
}
4、控制cookie的存活时间
- 除了 Cookie 的名称和内容外,我们还需要关⼼一个信息,到期时间,到期时间用来指定该 cookie 何时失效。默认为当前浏览器关闭即失效。我们可以手动设定 cookie 的有效时间(通过到期时间计算),通过 setMaxAge(int time);方法设定 cookie 的最⼤有效时间,以秒为单位。
到期时间的取值
- 负整数
若为负数,表示不存储该 cookie。
cookie 的 maxAge 属性的默认值就是-1,表示只在浏览器内存中存活,一旦关闭浏览器窗口,那么 cookie 就会消失。 - 正整数
若⼤于 0 的整数,表示存储的秒数。
表示 cookie 对象可存活指定的秒数。当⽣命⼤于 0 时,浏览器会把 Cookie 保存到硬盘上,就算关闭浏览器,就算重启客户端电脑,cookie 也会存活相应的时间。 - 零
若为 0,表示删除该 cookie。
cookie ⽣命等于 0 是一个特殊的值,它表示 cookie 被作废!也就是说,如果原来浏览器已经保存了这个 Cookie,那么可以通过 Cookie 的 setMaxAge(0)来删除这个 Cookie。 ⽆论是在浏览器内存中,还是在客户端硬盘上都会删除这个 Cookie。
设置cookie对象指定时间后失效
//创建Cookie对象
Cookie cookie = new Cookie("aa","AA");
//设置cookie失效时间
//cookie.setMaxAge(-1); // 关闭浏览器失效
//存活指定秒数 若该网页不刷新,即便在该网站的其他网页,cookie也存活
cookie.setMaxAge(20);
//响应cookie
resp.addCookie(cookie);
//设置七天失效
Cookie cookie1 = new Cookie("bb","BB");
cookie1.setMaxAge(7*24*60*60);
resp.addCookie(cookie1);
//设置即时删除,即maxAge值为零
Cookie cookie2 = new Cookie("cc","CC");
//设置为删除
cookie2.setMaxAge(0);
resp.addCookie(cookie2);
5、设置cookie的有效路径
-
Cookie 的 path 属性可以有效的过滤哪些 Cookie 可以发送给服务器。哪些不发。
-
path 属性是通过请求的地址来进行有效的过滤。
-
CookieA path=/工程路径
-
CookieB path=/工程路径/abc
-
请求地址如下:
- http://ip:port/工程路径/a.html
- CookieA 发送
- CookieB 不发送
- http://ip:port/工程路径/abc/a.html
- CookieA 发送
- CookieB 发送
- http://ip:port/工程路径/a.html
// 1. 在当前项目中的资源可以访问(默认)
Cookie cookie = new Cookie("aa","AA");
cookie.setPath("/servlet03");
resp.addCookie(cookie);
// 2. 在指定项目中的资源可以访问 (只能在站点名是/servlet04的项目下的资源能访问)
Cookie cookie1 = new Cookie("bb","BB");
cookie1.setPath("/servlet04");
resp.addCookie(cookie);
// 3. 在当前服务器中任意资源都可访问 (只要是放在同一服务器下的资源)
Cookie cookie2 = new Cookie("cc","CC");
cookie2.setPath("/");
resp.addCookie(cookie2);
// 4. 在指定项目下的指定资源(/servlet03/cook02)可以访问
Cookie cookie3 = new Cookie("dd","DD");
cookie3.setPath("/servlet03/cook02");
resp.addCookie(cookie3);
6、cookie注意点
6.1、Cookie保存在当前浏览器中。
- 在一般的站点中常常有记住用户名这样一个操作,该操作只是将信息保存 在本机上,换电脑以后这些信息就无效了。⽽且 cookie 还不能跨浏览器。
6.2、Cookie存中文问题
- Cookie 中不能出现中文,如果有中文则通过 URLEncoder.encode()来进行 编码,获取时通过URLDecoder.decode()来进行解码。
String name = "姓名";
String value = "张三";
// 通过 URLEncoder.encode()来进行编码
name = URLEncoder.encode(name);
value = URLEncoder.encode(value);
// 创建Cookie对象
Cookie cookie = new Cookie(name,value);
// 发送Cookie对象
response.addCookie(cookie);
// 获取时通过 URLDecoder.decode()来进行解码
URLDecoder.decode(cookie.getName());
URLDecoder.decode(cookie.getValue());
6.3、同名Cookie问题
- 如果服务器端发送重复的Cookie那么会覆盖原有的Cookie。
6.4、浏览器存放Cookie的数量
-
不同的浏览器对Cookie也有限定,Cookie的存储有是上限的。Cookie是存 储在客户端(浏览器)的,而且一般是由服务器端创建和设定。后期结合Session来实现回话跟踪。
-
总结:当访问的路径包含了cookie的路径时,则该请求将带上该cookie;如果访问路径不包含cookie路径,则该请求不会携带该cookie。
八、HttpSession类
1、概念
- Session 就一个接口(HttpSession)。
- Session 就是会话。它是用来维护一个客户端和服务器之间关联的一种技术。
- 每个客户端都有自己的一个 Session 会话。
- Session 会话中,我们经常用来保存用户登录之后的信息。
2、HttpSession的创建
- 如何创建和获取 Session。它们的 API 是一样的。
- request.getSession()
- 第一次调用是:创建 Session 会话
- 之后调用都是:获取前面创建好的 Session 会话对象。
- isNew(); 判断到底是不是刚创建出来的(新的)
- true 表示刚创建
- false 表示获取之前创建
- 每个会话都有一个身份证号。也就是 ID 值。而且这个 ID 是唯一的。
- getId() 得到 Session 的会话 id 值。
3、标识符 JSESSIONID解析
- Session 既然是为了标识一次会话,那么此次会话就应该有一个唯一的标志,这个标志就是sessionId。
- 每当一次请求到达服务器,如果开启了会话(访问了 session),服务器第一步会查看是否从客户端回传一个名为 JSESSIONID 的 cookie,如果没有则认为这是一次新的会话,会创建一个新的 session 对象,并用唯一的 sessionId 为此次会话做一个标志。如果有 JESSIONID 这 个cookie回传,服务器则会根据 JSESSIONID 这个值去查看是否含有id为JSESSION值的session 对象,如果没有则认为是一个新的会话,重新创建一个新的 session 对象,并标志此次会话; 如果找到了相应的 session 对象,则认为是之前标志过的一次会话,返回该 session 对象,数据达到共享。
- 这里提到一个叫做 JSESSIONID 的 cookie,这是一个⽐较特殊的 cookie,当用户请求服务器时,如果访问了 session,则服务器会创建一个名为 JSESSIONID,值为获取到的 session(无论是获取到的还是新创建的)的 sessionId 的 cookie 对象,并添加到 response 对象中,响应给客户端,有效时间为关闭浏览器。
- 所以 Session 的底层依赖 Cookie 来实现。
// 获取session对象 (获取或创建)
HttpSession session = req.getSession();
// 会话标识符
// 会话标识符:C77DEE4D344F8F11C465C5806026448B
System.out.println("会话标识符:" + session.getId());
//会话的创建时间: 1596958395620
System.out.println("会话的创建时间: " + session.getCreationTime());
//会话的最后一次访问:1596958395621
System.out.println("会话的最后一次访问:" + session.getLastAccessedTime());
//是否是新的会话:false,若首次访问直接访问该网页,则为:true
System.out.println("是否是新的会话: " + session.isNew());
4、Session 域数据的存取
- Session 用来表示一次会话,在一次会话中数据是可以共享的,这时 session 作为域对象存在,可以通过 setAttribute(name,value) 方法向域对象中添加数据,通过 getAttribute(name) 从域对象中获取数据,通过 removeAttribute(name) 从域对象中移除数据。
// 获取session对象
HttpSession session = request.getSession();
// 设置session域对象
session.setAttribute("uname","admin");
// 获取指定名称的session域对象
String uname = (String) request.getAttribute("uname");
// 移除指定名称的session域对象
session.removeAttribute("uname");
- 数据存储在 session 域对象中,当 session 对象不存在了,或者是两个不同的 session 对象时,数据也就不能共享了。这就不得不谈到 session 的⽣命周期。
5、Session对象的销毁(生命周期)
- public void setMaxInactiveInterval(int interval) 设置 Session 的超时时间(以秒为单位),超过指定的时长,Session就会被销毁。
- 值为正数的时候,设定 Session 的超时时长。
- 负数表示永不超时(极少使用)
- public int getMaxInactiveInterval()获取 Session 的超时时间
- public void invalidate() 让当前 Session 会话马上超时无效。
- Session 默认的超时时长是多少!
- Session 默认的超时时间长为 30 分钟。
- 因为在Tomcat服务器的配置文件web.xml中默认有以下的配置,它就表示配置了当前Tomcat服务器下所有的Session超时配置默认时长为:30 分钟。
<session-config>
<session-timeout>30</session-timeout>
</session-config>
- 如果说。你希望你的 web 工程,默认的 Session 的超时时长为其他时长。你可以在你自己的 web.xml 配置文件中做以上相同的配置。就可以修改你的 web 工程所有 Seession 的默认超时时长。
<!-- 表示当前 web 工程。创建出来 的所有 Session 默认是 20 分钟 超时时长 -->
<session-config>
<session-timeout>20</session-timeout>
</session-config>
- 在java代码中设置:
// 先获取 Session 对象
HttpSession session = req.getSession();
// 设置当前 Session 3秒后超时
session.setMaxInactiveInterval(3);
resp.getWriter().write(" 当前 Session 已经设置为 3 秒后超时");
// 让 Session 会话马上超时
session.invalidate();
resp.getWriter().write("Session 已经设置为超时(无效)");
- 关闭浏览器失效:从前面的 JESSIONID 可知道,session 的底层依赖 cookie 实现,并且该 cookie 的有效时间为关闭浏览器,从而session 在浏览器关闭时也相当于失效了(因为没有 JSESSION 再与之对应)。
- **关闭服务器失效:**当关闭服务器时,session 销毁。Session 失效则意味着此次会话结束,数据共享结束。
九、Servlet的三大作用域
/**
* Servlet的三大作用域
* setAttribute(name,value); 设置作用域
* getAttribute(name); 获取作用域
* removeAttribute(name); 移除作用域
*
* 1. request作用域
* 在一次请求中有效 (请求转发)
* 2. session作用域
* 在一次会话中有效,session失效则销毁 (无论请求转发还是重定向)
* 3. ServletContext作用域
* 在整个应用程序中 (服务器关闭失效)
*
*/