JavaWeb基础知识总结
0 从Servlet出发
Servlet是一个相当古老的技术了,可以说现在的任何一家公司都不再直接使用servlet来写项目,但是这并不能否认他的重要性,把目光放到现在的市场上,可以看到流行的spring全家桶也是基于Servlet开发出来的。各种框架追寻到底层,还是Servlet。
学习Servlet的最大好处就是可以为我们打下坚实的基础,进而在往后的框架学习过程中学得更加得心应手。
1 Tomcat
Tomcat 服务器是一个免费的开放源代码的运行JAVA的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试 jsp 程序的首选,它的底层是Socket的一个程序,同时它也是一个JSP和Serlvet的容器。
1.1 为什么我们需要用到Tomcat
如果你学过html,css,你会知道你写的页面只能自己访问,别人不能远程访问你写的页面,Tomcat就是提供能够让别人访问自己写的页面的一个程序
1.2 Tomcat的目录结构
1.2.1 bin目录
bin目录主要是用来存放tomcat的命令,主要有两大类,一类是以.sh结尾的(linux命令),另一类是以.bat结尾的(windows命令)。同时很多环境变量的设置也都在此处,例如可以设置JDK路径、tomcat路径 。
- startup 用来启动tomcat
- shutdown 用来关闭tomcat
- 修改catalina可以设置tomcat的内存
1.2.2 conf目录
conf目录主要是用来存放tomcat的一些配置文件。
- server.xml可以设置端口号、设置域名或IP、默认加载的项目、请求编码
- web.xml可以设置tomcat支持的文件类型
- context.xml可以用来配置数据源之类的
- tomcat-users.xml用来配置管理tomcat的用户与权限
- 在Catalina目录下可以设置默认加载的项目
1.2.3 lib目录
lib目录主要用来存放tomcat运行需要加载的jar包。
例如,像连接数据库的jdbc的包我们可以加入到lib目录中来。
1.2.4 logs目录
logs目录用来存放tomcat在运行过程中产生的日志文件,非常重要的是在控制台输出的日志。
在windows环境中,控制台的输出日志在catalina.xxxx-xx-xx.log文件中
在linux环境中,控制台的输出日志在catalina.out文件中
1.2.5 temp目录
temp目录用户存放tomcat在运行过程中产生的临时文件。(清空不会对tomcat运行带来影响)
1.2.6 webapp目录
webapps目录用来存放应用程序,当tomcat启动时会去加载webapps目录下的应用程序。可以以文件夹、war包、jar包的形式发布应用。
当然,你也可以把应用程序放置在磁盘的任意位置,只要在配置文件中映射好就行。
1.2.7 work目录
work目录用来存放tomcat在运行时的编译后文件,例如JSP编译后的文件。
清空work目录,然后重启tomcat,可以达到清除缓存的作用。
1.3 Tomcat的web应用实例
我们可以在webapps中建立了webtest目录,下面放置我们的html文件,jsp文件,图片等等,那么webtest就被当做一个web应用被管理起来。
举例:在webapps下创建一个web站点,在web站点下创建一个html文件,然后访问这个html文件。
以下是访问结果:
对于一个web站点,它内部的文件目录是有规范的。这个规范是约定熟成的。
为什么要这样设置web站点⽬录呢?
假如说我有一个需求:我有多个html⽂件,想把其中的⼀个html⽂件作为我web站点的⾸⻚。 如果没有WEB-INF⽬录下的web.xml⽂件⽀持,是⽆法解决我的这个需求的。
下面将webtest站点下的welcome.html页面作为我网站的首页,那么我们需要做的是:
- 在web站点下面创建一个WEB-INF目录
- 在WEB-INF目录中创建一个web.xml文件
web.xml我们不可能会写,所以可以在webapps⽬录下其他的站点中抄⼀份过来【复制ROOT/WEB-INF/web.xml的⽂件到⾃⼰的站点中】在web.xml中添加以下代码
1 | <welcome-file-list> |
最后web.xml文件中的内容为:
1 |
|
访问结果:
1.4 Tomcat的体系结构
1.5 Tomcat访问WEB资源的流程图
2 Servlet
2.1 Servlet定义
我们编写Java程序想要在⽹上实现聊天、发帖这样⼀些的交互性功能,普通的Java技术是⾮常难完成的。于是sun公司就提供了一种名为Servlet的技术供我们使⽤,Servlet其实就是⼀个遵循Servlet开发的java类,Servlet由服务器调⽤的,运⾏在服务器端。
2.2 HTTP协议
2.1.1 HTTP协议的定义
超⽂本传输协议(HTTP,HyperText Transfer Protocol)是互联⽹上应⽤最为⼴泛的⼀种⽹络协议。所有的WWW⽂件都必须遵守这个标准。它是TCP/IP协议的⼀个应⽤层协议。
简单来说,HTTP协议就是客户端和服务器交互的⼀种通迅的格式。
例子:在浏览器点击⼀个链接,浏览器就为我打开这个链接的网页。
原理:当在浏览器中点击这个链接的时候,浏览器会向服务器发送⼀段⽂本,告诉服务器请求打开的是哪⼀个网页。服务器收到请求后,就返回⼀段⽂本给浏览器,浏览器会将该⽂本解析,然后显示出来。
这段文本就是遵循HTTP协议规范的。
2.1.2 HTTP1.0和HTTP1.1的区别
- HTTP1.0协议中,客户端与web服务器建立连接后,只能获得⼀个web资源【短连接,获取资源后就断开连接】
- HTTP1.1协议中,允许客户端与web服务器建立连接后,在⼀个连接上获取多个web资源【保持连接】
2.1.3 HTTP请求
浏览器向服务器请求某个web资源时,称之为浏览器向服务器发送了⼀个http请求。
⼀个完整http请求应该包含三个部分:
- 请求⾏【描述客户端的请求⽅式、请求的资源名称,以及使⽤的HTTP协议版本号】
- 多个消息头【描述客户端请求哪台主机,以及客户端的⼀些环境信息等】
- 一个空行
2.1.3.1 请求行
请求行:GET/java.html HTTP/1.1
请求行中的GET称之为请求方式,请求方式有:POST、GET、HEAD、OPTIONS、DELETE、TRACE、PUT。
常用的有:POST、GET
⼀般来说,当我们点击超链接,通过地址栏访问都是get请求方式。通过表单提交的数据⼀般是post方式。可以简单理解GET方式用来查询数据,POST方式用来提交数据,get的提交速度比post快
GET方式:在URL地址后附带的参数是有限制的,其数据容量通常不能超过1K。
POST方式:可以在请求的实体内容中向服务器发送数据,传送的数据量⽆限制。
2.1.1.2 请求头
- Accept:text/html,img/* 【浏览器告诉服务器,它支持的数据类型】
- Accept-Charset:ISO-8859-1 【浏览器告诉服务器,它支持哪种字符集】
- Accept-Encoding:gzip,compress 【浏览器告诉服务器,它支持的压缩格式】
- Accept-Language:en-us,zn-cn 【浏览器告诉服务器,它的语言环境】
- HOST:www.jegret.cn 【浏览器告诉服务器,它想访问哪台主机】
- If-Modified-Since:Tue,11 Jul 2000 20:32:55 GMT【浏览器告诉服务器,缓冲数据的时间】
- Referer:http://www.jegret.cn 【浏览器告诉服务器,客户机是从哪个页面来的,常用与做防盗链】
- User-Agent:Mozilla/4.0(compatible;MSIE 5.5;Windows NT 5.0) 【浏览器告诉服务器,浏览器的内核是什么】
- Cookie:浏览器告诉服务器,带来的Cookie是什么
- Connection:close/Keep-Alive 【浏览器告诉服务器,请求完断开连接还是保持连接】
- Date:Tue,11 Jul 2000 20:32:55 GMT 【浏览器告诉服务器,请求的时间】
2.1.4 HTTP响应
一个HTTP响应代表着服务器向浏览器回送数据。
一个完整的HTTP响应应该包含四个部分:
- 一个状态行【用与描述服务器对请求的处理结果】
- 多个消息头【用与描述服务器的基本信息,以及数据的描述,服务器通过这些数据的描述信息,可以通知客户端如何处理等一会它回送的数据】
- 一个空行
- 实体内容【服务器向客户端回送数据】
2.1.4.1 状态行
格式:HTTP版本号 状态码 原因叙述
状态行:HTTP/1.1 200 OK
状态码用与表示服务器对请求的处理结果,它是一个三位的十进制数。响应状态码分为5类
状态码 | 含义 |
---|---|
100~199 | 表示成功接受请求,要求客户端继续提交下一次请求才能完成整个处理过程 |
200~299 | 表示成功接受请求并已完成整个处理过程,常用200 |
300~399 | 为完成请求,客户需进一步细化请求。例如,请求的资源已移动一个新地址,常用302、307和304 |
400~499 | 客户端的请求有错误,常用404 |
500~599 | 服务器端出现错误,常用500 |
2.1.4.2 响应头
- Location:http://www.jegret.cn/index.html【服务器告诉浏览器要跳转到哪个页面】
- Server:apache tomcat【服务器告诉浏览器,服务器的型号是什么】
- Content-Encoding:gzip 【服务器告诉浏览器数据压缩的格式】
- Content-Length:80 【服务器告诉浏览器回送数据的长度】
- Content-Language:zh-cn 【服务器告诉浏览器,服务器的语言环境】
- Content-Type:text/html; charset=GB2312 【服务器告诉浏览器,回送数据的类型】
- Last-Modified:Tue, 11 Jul 2000 18:23:51 GMT【服务器告诉浏览器该资源上次更新时间】
- Refresh:1;url=http://www.jegret.cn【服务器告诉浏览器要定时刷新】
- Content-Disposition:attachment; filename=aaa.zip【服务器告诉浏览器以下载方式打开数据】
- Transfer-Encoding:chunked 【服务器告诉浏览器数据以分块方式回送】
- Set-Cookie:SS=Q0=5Lb_nQ; path=/search【服务器告诉浏览器要保存Cookie】
- Expires:-1【服务器告诉浏览器不要设置缓存】
- Cache-Control:no-cache 【服务器告诉浏览器不要设置缓存】
- Pragma:no-cache 【服务器告诉浏览器不要设置缓存】
- Connection:close/Keep-Alive 【服务器告诉浏览器连接方式】
- Date:Tue,11 Jul 2000 20:32:55 GMT 【服务器告诉浏览器回送数据的时间】
2.3 一个简易的Servlet程序示例
2.3.1 编写逻辑代码
我们创建一个类实现Servlet接口,然后我们就可以发现我们一共有五个方法需要重写
- init:初始化
- getServletConfig:获取servlet配置
- service:服务
- getServletInfo:获取servlet信息
- destroy:销毁
service方法是我们写逻辑代码的地方, 这里我们调用ServletResponse对象的方法向浏览器输出一个“Hello World!!!”以作示例。
2.3.2 配置xml文件
光写servlet文件还不行,Tomcat还不知道如何访问这个servlet,这时我们就需要来编写xml文件来配置浏览器访问servlet的路径。
详细代码如下:
1 |
|
这个时候我们就可以通过Tomcat访问到我们需要的servlet。
2.4 Servlet生命周期
Servlet的生命周期可分为以下5个步骤:
- 加载Servlet。当Tomcat第⼀次访问Servlet的时候,Tomcat会负责创建Servlet的实例
- 初始化。当Servlet被实例化后,Tomcat会调用init()方法初始化这个对象
- 处理服务。当浏览器访问Servlet的时候,Servlet 会调用service()方法处理请求
- 销毁。当Tomcat关闭时或者检测到Servlet要从Tomcat删除的时候会自动调用destroy()方法,让该实例释放掉所占的资源。⼀个Servlet如果长时间不被使用的话,也会被Tomcat自动销毁
- 卸载。当Servlet调用完destroy()方法后,等待垃圾回收。如果有需要再次使⽤这个Servlet,会重新调⽤init()方法进行初始化操作。
简单总结:只要访问Servlet,service()就会被调用。init()只有第⼀次访问Servlet的时候才会被调用。 destroy()只有在Tomcat关闭的时候才会被调用。
2.5 通过继承HttpServlet编写Servlet程序
如果每次编写程序都实现Servlet接口,那么每次我们都需要去实现五个接口,这显然太麻烦了。所以为了方便,在实际开发中我们一般会通过继承HttpServlet来编写Servlet程序,HttpServlet类实现了Servlet接⼝的所有⽅法。编写Servlet时,只需要继承HttpServlet,重写我们需要的方法即可。
HttpServlet类在原有Servlet接口上添加了⼀些与HTTP协议处理⽅法,它比Servlet接口的功能更为强大。
⼀般我们开发的时候,都是重写doGet()和doPost()方法的。
2.6 Servlet调用图
2.7 Servlet的一些小细节
2.7.1 一个servlet可以被多次映射
1 | <servlet> |
⽆论我访问的是http://localhost:8080/MyServlet1还是http://localhost:8080/MyServlet2。我访问的都是MyServlet。
2.7.2 Servlet映射的URL可以使用通配符
简单来说:
- 以”/’开头和以”/*”结尾的是用来做路径映射的。
- /* 匹配所有
- 以前缀”*.”开头的是用来做扩展映射的。
- “/” 是用来定义default servlet映射的。会匹配到/login这样的路径型url,不会匹配到模式为*.jsp这样的后缀型url
- 剩下的都是用来定义详细映射的。比如: /aa/bb/cc.action
注意:为什么定义”/*.action”这样一个看起来很正常的匹配在启动tomcat时会报错?因为这个匹配即属于路径映射,也属于扩展映射,导致容器无法判断。
在Servlet映射到的URL中也可以使用通配符,但是只能有两种固定的格式:
- 一种格式是 *.扩展名
- 另一种格式是以正斜杠(/)开头并以“/*”结尾。
如果.扩展名和正斜杠(/)开头并以“/”结尾两种通配符同时出现,匹配的是哪一个呢?*
- 看谁的匹配度高,谁就被选择
- *.扩展名的优先级最低
示例代码如下:
1 | <servlet-mapping> |
2.8 Servlet是单例的
2.8.1 Servlet是单例的原因
浏览器多次对Servlet的请求,⼀般情况下,服务器只创建⼀个Servlet对象,也就是说,Servlet对象⼀旦创建了,就会驻留在内存中,为后续的请求做服务,直到服务器关闭。
2.8.2 每次访问请求对象和响应对象都是新的
对于每次访问请求,Servlet引擎都会创建⼀个新的HttpServletRequest请求对象和⼀个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调⽤的Servlet的service()方法,service⽅法再根据请求⽅式分别调⽤doXXX⽅法。
2.8.3 线程安全
当多个⽤户访问Servlet的时候,服务器会为每个⽤户创建⼀个线程。当多个⽤户并发访问Servlet共享资源的时候就会出现线程安全问题。
线程安全的原则:
- 如果⼀个变量需要多个⽤户共享,则应当在访问该变量的时候,加同步机制synchronized (对象){}
- 如果⼀个变量不需要共享,则直接在 doGet() 或者 doPost()定义.这样不会存在线程安全问题
2.9 在web访问任何资源都是在访问Servlet
⽆论在web中访问什么资源【包括JSP】,都是在访问Servlet。没有手工配置缺省Servlet的时候,你访问静态图⽚,静态网页,缺省Servlet会在你web站点中寻找该图片或网页,如果有就返回给浏览器,没有就报404错误
1 | <servlet-mapping> |
2.10 ServletConfig对象
通过ServletConfig对象我们可以方便地读取到web.xml配置的初始化参数。
web.xml文件:
1 | <servlet> |
MyServlet:
1 | public class MyServlet2 extends HttpServlet { |
访问结果:
2.11 ServletContext对象
当Tomcat启动的时候,就会创建⼀个ServletContext对象。它代表着当前web站点。
ServletContext对象的用处:
- ServletContext既然代表着当前web站点,那么所有Servlet都共享着⼀个ServletContext对象,所以Servlet之间可以通过ServletContext实现通讯。
- ServletConfig获取的是配置的是单个Servlet的参数信息,ServletContext可以获取的是配置整个web站点的参数信息
- 利⽤ServletContext读取web站点的资源⽂件
- 实现Servlet的转发【⽤ServletContext转发不多,主要⽤request转发】
2.11.1 实现Servlet之间的通信
ServletContext对象可以被称之为域对象。域对象可以简单理解成⼀个容器【类似于Map集合】
实现Servlet之间通讯就要用到ServletContext的setAttribute(String name,Object obj)方法
- 第⼀个参数是关键字
- 第⼆个参数是你要存储的对象
接下来是一个示例
MyServlet2代码:
1 | public class MyServlet2 extends HttpServlet { |
MyServlet3代码:
1 | public class MyServlet3 extends HttpServlet { |
注意:要先访问MyServlet2,把数据填充进去,再访问MyServlet3,才能把数据读取出来。
以下是访问结果:
访问MyServlet2
访问MyServlet3
读取数据并输出
2.11.2 获取web站点配置的信息
web.xml⽂件⽀持对整个站点进⾏配置参数信息【所有Servlet都可以取到该参数信息】
1 | <context-param> |
以下是MyServlet2代码:
1 | public class MyServlet2 extends HttpServlet { |
以下是MyServlet3代码:
1 | public class MyServlet3 extends HttpServlet { |
依次访问MyServlet2,MyServlet3,可见两者都能获取这个站点的配置信息。
3 Response对象
3.1 response、request对象
Tomcat收到客户端的http请求,会针对每⼀次请求,分别创建⼀个代表请求的request对象、和代表响应的response对象。
既然request对象代表http请求,那么我们获取浏览器提交过来的数据,找request对象即可。
response对象代表http响应,那么我们向浏览器输出数据,找response对象即可。
3.2 HttpServletResponse对象
http响应由状态行、实体内容、消息头、⼀个空行组成。HttpServletResponse对象就封装了http响应的信息。
3.2.1 调用getOutputStream()方法向浏览器输出数据
3.2.1.1 print()方法
1 | // 获取到OutputStream流 |
3.2.1.2 write()方法
1 | // 获取到OutputStream流 |
3.2.2 调用getWriter()方法向浏览器输出数据
对于getWriter()方法而言,是Writer的子类,那么只能向浏览器输出字符数据,不能输出⼆进制数据, 使用getWriter()方法输出中文数据,代码如下:
1 | //设置浏览器⽤UTF-8编码显示数据, |
getOutputStream()方法和getWriter()方法的一些问题:
- **getWriter()和getOutputStream()**两个方法不能同时调用。如果同时调用就会出现异常。
- Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎从response里面获取,Servlet引擎将这些数据当作响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。
- Servlet的serice()方法结束后【也就是doPost()或者doGet()结束后】,Servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,如果没有,Servlet引擎将调用close方法关闭该输出流对象。
有关问题的解决方法:https://juejin.cn/post/6844904128431259662
3.2.3 重定向
重定向(Redirect)就是通过各种方法将各种网络请求重新定个方向转到其他位置(如:网页重定向、域名重定向、路由选中的变化也是对数据报文经由路径的一种重定向)。
1 | // 重定向到index.jsp⻚⾯ |
出重定向是通过302状态码和跳转地址实现的。于是乎,我们设置http消息头就可以实现重定向跳转。
1 | // 设置状态码是302 |
其实**sendRedirect()方法就是对setStatus()和setHeader()**进行封装,原理就是setStatus()和setHeader()
4 Request对象
HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,开发⼈员通过这个对象的方法,可以获得客户这些信息。
简单来说,要得到浏览器信息,就找HttpServletRequest对象
4.1 HttpServletRequest常用方法
4.1.1 获得客户机【浏览器】信息
- getRequestURL方法返回客户端发出请求时的完整URL。
- getRequestURI方法返回请求行中的资源名部分。
- getQueryString 方法返回请求行中的参数部分。
- getPathInfo方法返回请求URL中的额外路径信息。额外路径信息是请求URL中的位于Servlet的路径之后和查询参数之前的内容,它以“/”开头。
- getRemoteAddr方法返回发出请求的客户机的IP地址
- getRemoteHost方法返回发出请求的客户机的完整主机名
- getRemotePort方法返回客户机所使用的网络端⼝号
- getLocalAddr方法返回WEB服务器的IP地址。
- getLocalName方法返回WEB服务器的主机名
4.1.2 获得客户机请求头
- getHeader方法
- getHeaders方法
- getHeaderNames方法
4.1.3 请求参数
- getParameter方法
- getParameterValues(String name)方法
- getParameterNames方法
- getParameterMap方法
- setAttribute(String name, Object o)方法
4.2 获取数据
4.2.1 POST方式
1 | <form action="/egret/MyServlet" method="post"> |
MyServlet:
1 | //设置request字符编码的格式 |
4.2.2 GET方式
常见的get方式提交数据有:使用超链接,sendRedirect()
1 | <!--超链接方式--> |
MyServlet:
1 | // 接收以username为参数名带过来的值 |
4.3 中文乱码问题
POST:当我们点击提交按钮的时候,数据封装进了Form Data中,http请求中把实体主体带过去了【传输的数据称之为实体主体】,既然request对象封装了 http请求,所以request对象可以解析到发送过来的数据,于是只要把编码设置成UTF-8就可以解决乱码问题了。
1 | request.setCharacterEncoding("utf-8"); |
GET:GET方式的数据是从消息行带过去的,没有封装到request对象里面,所以使用request设置编码是无效的。
要解决get方式乱码问题也不难,我们既然知道Tomcat默认的编码是ISO 8859-1,那么get方式由消息体带过去给浏览器的时候肯定是用ISO 8859-1编码了。
1 | // 此时得到的数据已经是被ISO 8859-1编码后的字符串了,这个是乱码 |
总结:
- post方式直接改request对象的编码
- get方式需要手工转换编码
- get方式也可以修改Tomcat服务器的编码,不推荐,因为会太依赖服务器了!
- 提交数据能用post就用post
4.4 转发
转发和重定向的功能一样,也是为了实现页面跳转。
1 | // 以username为关键字存egret值,转发可以保存这个设置的值 |
4.4.1 转发的时序图
4.4.2 转发的禁忌
如果在调⽤forward方法之前,在Servlet程序中写⼊的部分内容已经被真正地传送到了客户端, forward⽅法将抛出IllegalStateException异常。 也就是说:不要在转发之前写数据给浏览器。
如果在调⽤forward方法之前向Servlet引擎的缓冲区中写入了内容,只要写入到缓冲区中的内容还没有被真正输出到客户端,forward方法就可以被正常执行,原来写入到输出缓冲区中的内容将被清空,但是,已写⼊到HttpServletResponse对象中的响应头字段信息保持有效。
4.4.3 重定向和转发的区别
4.4.3.1 实际发生位置不同,地址栏不同
- 转发是由服务器进行跳转的,在转发的时候,浏览器的地址栏是没有发生变化的,在我访问MyServlet1的时候,即使跳转到了MyServlet3的页面,浏览器的地址还是MyServlet1的。也就是说浏览器是不知道该跳转的动作,转发是对浏览器透明的。通过上面的转发时序图我们也可以发现,实现转发只是⼀次的http请求,⼀次转发中request和response对象都是同⼀个。这也解释了,为什么可以使用request作为域对象进行Servlet之间的通讯。
- 重定向是由浏览器进行跳转的,进行重定向跳转的时候,浏览器的地址会发生变化的。曾经介绍过:实现重定向的原理是由response的状态码和Location头组合而实现的。这是由浏览器进行的页面跳转实现重定向会发出两个http请求,request域对象是无效的,因为它不是同⼀个request对象
4.4.3.2 用法不同
转发和重定向的时候,资源地址究竟怎么写。
有的时候要把应用名写上,有的时候不用把应用名写上。很容易把⼈搞晕。记住⼀个原则:给服务器用的直接从资源名开始写,给浏览器用的要把应用名写上
1 | // 转发时"/"代表的是本应用程序的根目录【JavaWebProject】 |
4.4.3.3 能够去往的URL的范围不⼀样
- 转发是服务器跳转只能去往当前web应用的资源
- 重定向是服务器跳转,可以去往任何的资源
4.4.3.4 传递数据的类型不同
- 转发的request对象可以传递各种类型的数据
- 包括对象重定向只能传递字符串
4.4.3.5 跳转的时间不同
- 转发:执行到跳转语句时就会立刻跳转
- 重定向:整个页面执行完之后才执行跳转
4.4.3.6 转发和重定向的使用场景
转发是带着转发前的请求的参数的。重定向是新的请求。
典型的应用场景:
- 转发:访问 Servlet 处理业务逻辑,然后forward到 jsp 显示处理结果,浏览器里URL不变
- 重定向:提交表单,处理成功后 redirect 到另⼀个 jsp,防止表单重复提交,浏览器里URL变了
5 Cookie和Session
5.1 会话技术
会话:指⽤户开⼀个浏览器,访问⼀个网站,只要不关闭该浏览器,不管该用户点击多少个超链接,访问多少资源,直到⽤户关闭浏览器,整个这个过程我们称为⼀次会话。
5.2 Cookie
网页之间的交互是通过HTTP协议传输数据的,而HTTP协议是⽆状态的协议。⽆状态的协议是什么意思呢?⼀旦数据提交完后,浏览器和服务器的连接就会关闭,再次交互的时候需要重新建立新的连接。
服务器⽆法确认用户的信息,于是乎,W3C就提出了:给每⼀个用户都发⼀个通行证,⽆论谁访问的时候都需要携带通行证,这样服务器就可以从通行证上确认用户的信息。通行证就是Cookie。
Cookie的流程:浏览器访问服务器,如果服务器需要记录该用户的状态,就使用response向浏览器发送⼀个Cookie,浏览器会把Cookie保存起来。当浏览器再次访问服务器的时候,浏览器会把请求的网址连同Cookie⼀同交给服务器。
Cookie具有不可跨域名性。浏览器判断⼀个网站是否能操作另⼀个网站的Cookie的依据是域名。所以⼀般来说,当我访问baidu的时候,浏览器只会把baidu颁发的Cookie带过去,⽽不会带上google的Cookie。
5.2.1 Cookie的API
- Cookie类用于创建⼀个Cookie对象
- response接口中定义了⼀个addCookie方法,它用于在其响应头中增加⼀个相应的Set-Cookie头字段
- request接口中定义了⼀个getCookies方法,它用于获取客户端提交的Cookie
常用接口:
- public Cookie(String name,String value)
- setValue与getValue方法
- setMaxAge与getMaxAge方法
- setPath与getPath方法
- setDomain与getDomain方法
- getName方法
5.2.2 Cookie的使用
发送一个Cookie:
1 | //设置response的编码 |
接收Cookie:
1 | Cookie[] cookies = request.getCookies(); |
5.2.3 Cookie的有效期
Cookie的有效期是通过**setMaxAge()**来设置的。
- 如果MaxAge为正数,浏览器会把Cookie写到硬盘中,只要还在MaxAge秒之前,登陆网站时该Cookie就有效【不论关闭了浏览器还是电脑】
- 如果MaxAge为负数,Cookie是临时性的,仅在本浏览器内有效,关闭浏览器Cookie就失效了,Cookie不会写到硬盘中。Cookie默认值就是-1。所以如果没设置Cookie的有效期,在硬盘中就找不到对应的⽂件。
- 如果MaxAge为0,则表示删除该Cookie。Cookie机制没有提供删除Cookie对应的⽅法,把MaxAge设置为0等同于删除Cookie。
5.3 Session
5.3.1 Session的定义
Session是另⼀种记录浏览器状态的机制。不同的是Cookie保存在浏览器中,Session保存在服务器中。用户使用浏览器访问服务器的时候,服务器把用户的信息以某种的形式记录在服务器,这种记录就是Session。
如果说Cookie是检查用户身上的”通行证“来确认用户的身份,那么Session就是通过检查服务器上的”客户明细表“来确认用户的身份的。
Session相当于在服务器中建立了⼀份“客户明细表”。
Session比Cookie使用更加方便,Session可以解决Cookie解决不了的事情:Session可以存储对象,而Cookie只能存储字符串。
5.3.2 Session的API
- long getCreationTime();【获取Session被创建时间】
- String getId();【获取Session的id】
- long getLastAccessedTime();【返回Session最后活跃的时间】
- ServletContext getServletContext();【获取ServletContext对象】
- void setMaxInactiveInterval(int var1);【设置Session超时时间】
- int getMaxInactiveInterval();【获取Session超时时间】
- Object getAttribute(String var1);【获取Session属性】
- Enumeration getAttributeNames();【获取Session所有的属性名】
- void setAttribute(String var1, Object var2);【设置Session属性】
- void removeAttribute(String var1);【移除Session属性】
- void invalidate();【销毁该Session】
- boolean isNew();【该Session是否为新的】
5.3.3 Session的应用
Session有着request和ServletContext类似的方法。其实Session也是⼀个域对象。Session作为⼀种记录浏览器状态的机制,只要Session对象没有被销毁,Servlet之间就可以通过Session对象实现通讯。
⼀般来讲,当我们要存进的是用户级别的数据就⽤Session,用户级别即是:只要浏览器不关闭,希望数据还在,就使用Session来保存。
MyServlet1:
1 | // 得到Session对象 |
MyServler2:
1 | //获取到从MyServlet1的Session存进去的值 |
5.3.4 Session的超时时间
Session在用户第⼀次访问服务器Servlet,jsp等动态资源就会被自动创建,Session对象保存在内存⾥,这也就为什么上⾯的例子可以直接使用request对象获取得到Session对象。如果访问HTML,IMAGE等静态资源Session不会被创建。
Session生成后,只要用户继续访问,服务器就会更新Session的最后访问时间,⽆论是否对Session进⾏读写,服务器都会认为Session活跃了⼀次。
由于会有越来越多的用户访问服务器,因此Session也会越来越多。为了防止内存溢出,服务器会把长时间没有活跃的Session从内存中删除,这个时间也就是Session的超时时间。
Session的超时时间默认是30分钟,有三种方式可以对Session的超时时间进行修改。
第一种方式:在tomcat/conf/web.xml文件中设置,时间值为20分钟(单位为分钟),所有的WEB应用都有效
1 | <session-config> |
第二种方式:在单个的web.xml文件中设置,对单个web应用有效,如果有冲突,以自己的web应用为准
1 | <session-config> |
第三种方式:通过setMaxInactiveInterval()方法设置,单位为秒
1 | // 设置Session最长超时时间为60秒,这里的单位是秒 |
5.3.5 Session的原理
Session之所以可以识别不同的用户,依靠的就是Cookie。
该Cookie是服务器自动颁发给浏览器的,不用我们手工创建的。该Cookie的maxAge值默认是**-1,也就是说仅当前浏览器使用,不会将该Cookie存在硬盘中**。
当我们访问一个Servlet时,服务器就会创建⼀个Session对象,然后执行我们的程序代码,并自动颁发个Cookie给用户浏览器。然后当我们用同⼀个浏览器访问Servlet时,浏览器会把Cookie的值通过http协议带过去给服务器,这个时候服务器就知道用哪⼀个Session。而当我们使用新会话的浏览器访问Servlet的时候,该新浏览器并没有Cookie,服务器⽆法辨认使用哪⼀个Session,所以这时就不知道该用哪个Session来执行程序。
5.3.6 URL重写
当前浏览器把Cookie禁用之后,浏览器在发请求的时候,就不会把cookie带到服务器端了(其中最重要的也包括JSESSIONID),因为禁用Cookie之后浏览器拒绝一切站点向浏览器写入cookie的(注意再禁用之前是否已经有一些已经存在的cookie了),这样的话,多个请求就不能在服务器端拿到同一个session对象了(因为发送请求的时候没有把JSESSIONID的值传给服务器)。
HttpServletResponse类提供了两个URL地址重写的⽅法:
- encodeURL(String url)
- encodeRedirectURL(String url)
注意:这两个方法会自动判断该浏览器是否⽀持Cookie,如果支持Cookie的话,重写后的URL 地址就不会带有JSESSIONID了。
1 | String url = "/egret/MyServlet"; |
URL地址重写的原理:将Session的id信息重写到URL地址中。服务器解析重写后URL,获取Session的id。这样⼀来,即使浏览器禁用掉了Cookie,但Session的id通过服务器端传递,还是可以使用Session来记录用户的状态。
5.4 Cookie和Session的区别
从存储方式上比较
- Cookie只能存储字符串,如果要存储非ASCII字符串还要对其编码。
- Session可以存储任何类型的数据,可以把Session看成是⼀个容器
从隐私安全上比较
- Cookie存储在浏览器中,对客户端是可见的,信息容易泄露出去,如果使用Cookie,最好将Cookie加密。
- Session存储在服务器上,对客户端是透明的,不存在敏感信息泄露问题。
从有效期上比较
- Cookie保存在硬盘中,只需要设置maxAge属性为比较大的正整数,即使关闭浏览器,Cookie还是存在的。
- Session保存在服务器中,设置maxInactiveInterval属性值来确定Session的有效期,并且Session依赖于名为JSESSIONID的Cookie,该Cookie默认的maxAge属性为-1。如果关闭了浏览器,该Session虽然没有从服务器中消亡,但也就失效了。
从对服务器的负担比较
- Session是保存在服务器的,每个用户都会产生⼀个Session,如果是并发访问的用户非常多,是不能使用Session的,Session会消耗大量的内存。
- Cookie是保存在客户端的。不占用服务器的资源。像baidu、淘宝这样的大型网站,⼀般都是使用Cookie来进⾏会话跟踪。
从浏览器的支持上比较
- 如果浏览器禁用了Cookie,那么Cookie是⽆用的了!
- 如果浏览器禁用了Cookie,Session可以通过URL地址重写来进行会话跟踪。
从跨域名上比较
- Cookie可以设置domain属性来实现跨域名
- Session只在当前的域名内有效,不可跨域名