个人java学习路线-HttpServletRequest接口,乱码,线程安全问题,转发和重定向,Cookie和Session
HttpServletRequest接口
简单介绍
HttpServletRequest是Servlet规范中的重要接口之一
HttpServletRequest 继承了 ServletRequest()
HttpServletRequest接口的实现类是WEB容器负责实现的,Tomcat服务器有自己的实现。
我们无需关心具体的实现,我们只需要面向HttpServletRequest接口调用方法即可。
HttpServletRequest这个对象封装了哪些信息呢?
封装了HTTP请求协议的全部内容:
- 请求方式
- URI
- 协议版本号
- 表单提交的数据
....
HttpServletRequest一般变量名叫做:request,表示请求,HttpServletRequest对象代表一次请求,
一次请求对应一个request对象,100个请求对应100个request对象,所以request对象的生命周期是短暂的,什么是一次请求:
到目前为止我们可以这样理解一次请求:在网页上点击超链接,到网页停下来,这就是一次完整的请求。
HttpServletRequest中的方法
表单提交的数据,会自动被封装到request对象中,request对象中有Map集合存储这些数据:Map集合的key是name,value是一个一维字符串数组
- String getParameter(String name) //通过key获取value这个一维数组中的首元素(这个用的多,但不能获取类似多选框的多个值)
- String[] getParameterValues(String name) //通过Map集合的key获取value
- Map getParameterMap() //获取整个map集合
- Enumeration getParameterNames() //获取map集合的key
- String getContextPath() //获取上下文路径,webapp路径
- String getMethod() //获取浏览器的请求方式
- String getRequestURI() //获取URI
- StringBuffer getRequestURL() //获取URL
- String getServletPath() //获取Servlet PATH url-pattern
- String getRemoteaddr() //获取客户端IP地址
- void setAttribute(String name,Object o) //向Request范围中添加数据
- Object getAttribute(String name) //从Request范围中读取数据
- void removeAttribute(String name) //移除Request范围中的数据
- RequestDispatcher getRequestDispatcher(String path) //获取请求转发器,让转发器对象指向某个资源
- void setCharacterEncoding(String env)
- Cookie[] getCookies() //下面两个都是后面讲
- HttpSession getSession()
关于存数据范围对象的选择
ServletConetx 应用范围,可以跨用户传递数据
ServletRequest 请求范围,只能在同一个请求中传递数据【可以跨Servlet传递数据,但是这多个Servlet必须在同一个请求中】
优先选择request范围
乱码
1.乱码经常出现在什么位置上
* 数据"传递"过程中的乱码
* 数据"展示"过程中的乱码
* 数据"保存"过程中的乱码
2.数据保存过程中的乱码
- 最终保存到数据库中的术后,数据出现乱码、
- 导致数据保存过程中的乱码包括以下两种情况
第一种情况:在保存之前,数据本身就是乱码,保存到数据库中一定时乱码
第二种情况:保存之前,数据不是乱码,但是由于本身数据库不支持简体中文,保存之后出现乱码
3.数据展示过程中的乱码?
- 最终显示到网页上出现中文乱码
- 经过执行java程序之后,java程序负责向浏览器响应的时候,中文出现乱码,怎么解决:
* 设置响应的内容类型,以及对应的字符编码方式:
response.setContentType=("text/html;charset=UTF-8");
- 没有经过执行java程序,直接访问html页面,出现中文乱码,解决方法:
<meta content="text/html;charset=UTF-8">
4.数据传递过程中的乱码?
- 将数据从浏览器发送给服务器的时候,服务器接受到的数据时乱码
- 浏览器是这样发送数据给服务器的: dname=%E5%B8%82%E5%9C%BA%E9%83%A8
这个是“市场部”对应的ISO-8859-1的编码
- ISO-8859-1是国际标准码,不支持中文编码,兼容ASCII码,又被称为latin1编码
- 不管是哪个国家的文字,在浏览器发送给服务器的时候都会采用ISO-8859-1编码的方式发送。
- 发送给web服务器之后,web服务器不知道这些数据之前是什么类型的文字,所以web接受的数据时会乱码
- 解决数据传递过程中的乱码:
第一种方法:
万能方法,既能解决POST请求乱码,又能解决GET请求乱码
先将服务器中接受到的数据采用ISO-8859-1的方式解码,回到初始状态
再给一种支持简体中文的编码方式重写编码组装我【组装的编码方式需和浏览器的编码方式相同】
第二种解决方案:
request.setCharacterEncoding("UTF-8");
以上代码必须在从request中获取任何数据之前才能起效果
第三种方案:专门解决GET请求的乱码问题,因为种方式只对请求行编码
修改CATALINA_HOME/conf/server.xml文件
<Connector port="9527" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8" />//最后一行是加的
扩展: 上面的Connector标签中都可以编写哪些属性?
port 端口
maxThreads Tomcat服务器最高能支持多少个线程并发执行,默认200
URIEncoding 设置请求行上的编码方式,解决GET请求乱码
线程安全问题
1.Servlet是单实例多线程环境下运行的
2.什么时候程序存在线程安全问题?
- 多线程并发
- 有共享的数据
- 共享数据有修改操作
3.在jvm中,哪些数据可能会存在线程安全问题?
- 局部变量内存空间不共享,一个线程一个栈,局部变量在栈中储存,局部变量不会存在线程安全问题
- 常量不会被修改,所以常量不会存在线程安全问题。
- 所有线程共享一个堆:
*堆内存中new出来的对象在其中储存,对象内部有“实例变量”,所以“实例变量”的内存是多线程共享的
实例变量多线程共同访问,并且涉及到修改操作的时候就会存在线程安全问题
- 所有线程共享一个方法区
*方法区中有静态变量,静态变量的内存也是共享的,若涉及修改,静态变量也会出现线程安全问题。
4.数据库中也会出现线程安全问题,如多个线程共享同一章表,并且同时去修改表中的一些记录,那么这些记录就存在线程安全问题。
怎么解决?
- 第一种方案是:在java程序中使用synchronized关键字,线程都排队执行,自然不会在数据库中年并发,解决线程安全问题。
- 第二种方案是:行级锁【悲观锁】
- 第三种方案是:事务隔离级别,例如:串行化
- 第四种方案是:乐观锁......
5.怎么解决线程安全问题?
5.1 不使用实例变量,尽量使用局部变量
5.2 若必须使用实例变量,那么我们可以考虑把该对象变成多列对象,一个线程一个java对象,实例变量的内存也不会共享
5.3 若必须使用单例,那么只能使用synchronized线程同步机制,线程一但排队执行,则吞吐量降低,降低用户体验。
6. Servlet怎么解决线程安全问题?
6.1 不使用实例变量,尽量用局部变量
6.2用synchronized
转发和重定向
1.跳转包括两种方式:
- 转发- forward
- 重定向- redirect
2.代码
-转发:
request.getRequestDispatcher("/b").forward(request, response);
-重定向:
response.sendRedirect(request.getContextPath()+"/b");
3.相同的和不同点:
相同:都可以完成资源跳转
不同:
转发是request对象触发的
重定向是response对象触发的
转发是一次请求,浏览器地址栏不会变化【/a】
重定向是两次请求,浏览器地址栏上的地址发生变化【/a,/b】
重定向的路径需要加webapp的根路径
转发是在本项目内完成的内部资源跳转
重定向可以完成app跳转
4.跳转的下一个资源可以是web服务器中的任何一种资源:html servlet jsp...
5.若想完成app跳转,必须使用重定向
若想在下一个资源中调用上一个资源的request范围中的数据,用转发
重定向可以解决浏览器的刷新问题(表单页面转发刷新会重复提交表单)
6.重定向时,程序给了联络线一个新的路径,浏览器又自动给web服务器发送了一个新的请求
7.点击一个超链接,到网页最终停下来,可能时多次请求
Cookie
1.由曲奇饼干翻译过来的Cookie,可以保存会话状态到浏览器缓存上,或者客户端硬盘上。(保存数据)
2.Cookie机制是Http协议规定的,所有基于Http协议的项目,就有Cookie的存在
3.Cookie可以实现的功能:
- 保留购物车商品的状态到客户端
- 十天之内免登录
.......
4.java中Cookie被当作类来处理,
Cookie cookie=new Cookie(String cookieName,String cookieValue);
5.服务器一次可以向浏览器发送多个Cookie,默认情况下Cookie会保存在浏览器缓存中,浏览器关闭Cookie被清除。
6.Cookie和请求路径绑定,不同的请求路径会让浏览器发送不同的Cookie给服务器
7.如何绑定:*****
/prj-servlet-18/test/createAndSendCookieToBrowser 请求服务器,服务器生成Cookie,并将Cookie发送给浏览器客户端
这个浏览器中的Cookie会默认和"test/"这个路径绑定在一起。
也就是说只要发送"test/"请求,Cookie一定会提交给服务器。
/prj-servlet-18/a请求服务器时
浏览器中的Cookie会默认和"prj-servlet-18/"绑定在一起
******注意有无"/"
8.路径可以指定:在java程序中
cookie.setPath("/prj-servlet-18/king");
那么Cookie将和"/prj-servlet-18/king"路径绑定
9.因为默认Cookie保存在浏览器缓存中,浏览器关闭,Cookie消失,所以我们可以设置Cookie有效时长,从而把Cookie保存在硬盘中:
cookie1.setMaxAge();
单位秒,必须大于0(等于零自动直接销毁)
cookie1.setMaxAge(60*60);//1小时
10.服务器接受浏览器发送的Cookie:
Cookie[] cookies=request.getCookies();
if(cookies!=null) {
for (Cookie cookie : cookies) {
String cookieName=cookie.getName();
String cookieValue=cookie.getValue();
System.out.println(cookieName+"="+cookieValue);
}
}
添加的话直接用addCookie()方法就好
response.addCookie(cookie1);
Session
关于web编程中的Session:
1.Session不是java特有的,是web开发的机制
2.对应java中的类型: javax.serlvet.http.HttpSession 简称session/会话
3.Cookie可以将会话状态保存在客户端,HttpSession可以将会话状态保存在服务器端
4.HttpSession对象是一个会话级别的对象,一次会话对应一个HttpSession对象
5.什么是一次会话?
目前可以简单的认为:用户打开浏览器并发送多个请求,最后关闭浏览器,表示一次完整的会话
6.在会话过程中,web服务器一直为当前用户维护着一个会话对象/HttpSession
7.在WEB容器中,WEB容器维护了大量的HttpSession对象,也就是说,WEB容器中应该有一个"session列表"
为什么当前会话中的每一次请求可以获取到属于自己的会话对象?session的实现原理?
- 打开浏览器,在浏览器上发送首次请求
- 服务器会创建一个HttpSession对象,该对象代表一次会话
- 同时生成HttpSession对象对应的Cookie对象,并且Cookie对象的name是JSESSIONID,Cookie的value是32位长度的字符串
- 服务器将Cookie的value和HttpSession对象绑定到session列表中
- 服务器将Cookie完整发送给浏览器客户端
- 浏览器客户端将Cookie保存到缓存中
- 只要浏览器不关闭,Cookie不会消失
- 当再次发送请求的时候,会自动提交缓存当中的Cookie
- 服务器接受到Cookie,验证该Cookie的name的确是:JSESSIONID,然后获取该Cookie的value
- 通过Cookie的value去session列表中检索对应的HttpSession对象。
8.和HttpSession对象关联的这个Cookie是比较特殊的,在java中就叫做:jsessionid
9.浏览器禁用Cookie会出现什么问题?怎么解决?
- 浏览器禁用Cookie,则浏览器缓存中不在保存Cookie
- 导致在同一个会话中,无法获取到对应的会话对象
- 禁用Cookie之后,每一次获取的会话对象都是新的
浏览器禁用Cookie后,若还想拿到对应的Session对象,必须使用URL重写机制,怎么重写URL:
http://localhost/prj-servlet-21/user/accessMySelfSession;jessionid=D3E9985SADAS331SAD..
(手动添加Cookie)
10.浏览器关闭之后,服务端对应的session对象会被销毁吗?为什么?
- 浏览器关闭之后,服务器不会销毁session
- 因为B/S架构的系统基于HTTP协议,而Http协议是一种无连接/无状态的协议
- 什么是无连接/无状态?
* 请求的瞬间浏览器和服务器之间的通道是打开的,请求响应结束之后,通道关闭
* 这样做的目的是为了降低服务器的压力
11.session对象什么时候会被销毁?
- web系统中引入了session超时的概念
- 当很长一段时间(可以自己设置)没有用户再访问该session对象,此时session对象超时,web服务器自动回收session对象
- 可以配置:web.sml文件中,默认30分钟
<session-config>
<session-timeout>120</session-timeout>
</session-config>
12.什么是一次会话?
- 本质上说是从session对象的创建到超时销毁是一次完整对话
13.关于javax.servlet.http.HttpSession接口中常用方法:
- void setAttribute(String name,Object value)
- Object getAttribute(String name)
- void removeAttribute(String name)
- void invalidate()
-
补充HttpServletRequest中的方法:
//获取session对象,若没有获取到,则创建session对象
HttpSession session=request.getSession();
//获取session对象,若没有获取到,则创建session对象
HttpSession session=request.getSession(true);
//获取session对象,若没有获取到,则返回null
HttpSession session=request.getSession(false);
ServletContext,HttpSession,HttpServletRequest接口的对比:
1。以上都是范围对象:
ServletContext application; 是应用范围
HttpSession session; 是会话对象
HttpServletRequest request; 是请求范围
2.三个范围的排序:
application>session>request
3.
application完成跨会话共享数据,
session完成跨请求共享数据,但是这些请求必须在用一个会话中
request完成跨Servlet共享数据,但是这些Servlet必须在同一个请求中【转发】
4.使用原则:由小到大尝试:
例如:登录成功之后,已经登录的状态需要保存起来,可以将登录成功的这个状态保存到session对象中
登录成功状态不能保存到request范围中,因为一次请求对应一个新的request对象
登录成功状态也不能保存到application范围中,因为登录成功状态是属于回会话级别的,不能所有用户共享