Servlet学习笔记5——线程安全、转发与重定向

关于程序中出现的乱码问题以及解决方案

1、乱码可能出现在哪里?

	1) 数据“传递”过程中的乱码
	2) 数据“展示”过程中的乱码
	3) 数据“保存”过程中的乱码

2、数据“展示”过程中的乱码

	1) 在response获取标准输出流之后,使用标准输出流输出到浏览器上中文的时候,出现的乱码
	2) 怎么解决?

response.setContentType(“text/html;charset=UTF-8”);

3、数据“保存”过程中的乱码

1) 数据最终保存到数据库的时候,表中存储的数据出现乱码问题
2) 数据库表中存储的数据出现乱码,为什么?

可能是在保存之前就已经出现乱码,保存到数据库表中的数据一定是乱码,保存之前数据没有出现乱码,但是数据库本身不支持简体中文,也可能出现乱码
3) 怎么解决?

  • 若保存之前就是乱码,和数据库没有关系,先解决保存之前的乱码问题
  • 让数据库本身支持简体中文

4、数据“传递”过程中的乱码

  1. 浏览器提交数据给服务器,在传送的过程中出现中文乱码问题
  2. 为什么在传递过程中会出现乱码呢?

浏览器在提交表单数据的时候,不管这个语言是哪个国家的语言,不管这个文字是哪个国家的文字,都是采用ISO-8859-1的这种编码方式提交数据的。

换句话说,在网络中传送的数据包中涉及到文字的话,该文字一定是ISO-8859-1的编码方式

发送给Tomcat服务器之后,Tomcat服务器接收到数据,只是接收到一堆ISO-8859-1编码方式的数据,不知道该数据以前是哪个国家的文字

浏览器是这样提交数据的:username=%E5%BC%A0%E4%B8%89

%E5%BC%A0%E4%B8%89是ISO-8859-1的编码方式编码之后的数据,以前是什么,不知道!!!
3) 解决方案:
第一种方案:万能方案,适合于所有的请求,包括GET请求和POST请求

将从request对象中获取到的数据,经过ISO-8859-1的编码方式进行解码还原,回归最原始的二进制码

再找一种编码方式,这种编码方式和浏览器页面的编码方式相同,进行重新编码
第二种方案:只能解决POST请求中的乱码问题

request.setCharacterEncoding(“UTF-8”);

以上代码在从request对象中获取任何数据之前设置有效果

以上的代码只对请求体中的数据进行编码,无法对请求行上的数据进行编码,所以只能解决POST,不能解决GET
第三种方案:只能解决GET请求中的乱码问题

修改CATALINA_HOME/conf/server.xml文件

<Connector port="80" protocol="HTTP/1.1"
    connectionTimeout="20000"
    redirectPort="8443"
   URIEncoding="UTF-8"/>

server.xml文件中的Connector标签中可以编写什么属性呢?

参考:C:\tomcat7\webapps\docs\config\http.html

port 设置端口号

URIEncoding 设置URI的编码方式

maxThreads 设置Tomcat服务器可支持的最大线程数量

5、建议:

为了避免乱码的出现,建议整个工程中凡是涉及到编码方式的,最好所有的编码方式统一。

数据库的编码方式、HTML页面的编码方式、java程序中使用的编码方式、myeclipse工作区的编码方式…都采用UTF-8

关于Servlet的线程安全问题

1、Servlet在什么环境下运行?

Servlet在“单实例”多线程的环境下运行。

某种类型的Servlet对象在容器中只有一个。

2、什么情况下程序会存在线程安全问题?

多线程环境

有共享数据

共享数据涉及到修改操作

3、对于多线程来说,JVM内存中哪些内存空间是共享的,哪些内存空间是独立的?

方法区和堆区内存共享

栈内存独立,一个线程一个栈

局部变量内存不共享,所以局部变量不存在线程安全问题

静态变量和实例变量,静态变量在方法区中,实例变量堆内存的java对象内部,静态变量和实例变量存在线程安全问题

4、怎么解决线程安全问题呢?

第一种方案:尽量使用局部变量代替实例变量
第二种方案:若必须使用实例变量,那么可以考虑,让对象变成多例,不再是单例。
第三种方案:若必须使用实例变量,必须是单例的,这个时候必须使用线程同步机制:synchronized

注意:使用线程同步机制,是不得以的时候再使用,因为这种方式会让程序的吞吐量降低,用户排队访问,用户体验差。

Servlet线程安全问题及解决方案 (Servlet单实例多线程)

Servlet体系结构是建立在Java多线程机制之上的,它的生命周期是由Web容器负责的。当客户端第一次请求某个Servlet时,Servlet容器将
会根据web.xml配置文件实例化这个Servlet类。当有新的客户端请求该Servlet时,一般不会再实例化该Servlet类,也就是有多个线程在使
用这个实例。这样的话,当两个或多个线程同时访问同一个Servlet时,可能会发生多个线程同时访问同一资源的情况,数据可能会变得不
一致,所以就很容易造成一系列的一些安全性问题。为了保证数据的安全性可以采用下列方式:
同步对共享数据的操作
使用synchronized 关键字能保证一次只有一个线程可以访问被保护的区段,同步后的代码如下:
public class XXXServlet extends HttpServlet {
…………
synchronized (this){XXXX}

}

避免使用实例变量、静态变量
线程安全问题还有些是由实例变量、静态变量造成的,只要在Servlet里面的任何方法里面都不使用实例变量、静态变量,那么该Servlet就是线程安全的。
对上面的两种种方法进行测试,可以表明用它们都能设计出线程安全的Servlet程序。但是在程序中使用同步来保护要使用的共享的数据,会使系统的性能大大下降。这是因为被同步的代码块在同一时刻只能有一个线程执行它,使得其同时处理客户请求的吞吐量降低,而且很多客户处于阻塞状态。另外为保证主存内容和线程的工作内存中的数据的一致性,要频繁地刷新缓存,这也会大大地影响系统的性能。所以在实际的开发中也应避免或最小化Servlet 中的同步代码;在Serlet中避免使用实例变量是保证Servlet线程安全的最佳选择。从Java 内存模型也可以知道,方法中的临时变量是在栈上分配空间,而且每个线程都有自己私有的栈空间,所以它们不会影响线程的安全。

小结

Servlet的线程安全问题只有在大量的并发访问时才会显现出来,并且很难发现,因此在编写Servlet程序时要特别注意。线程安全问题主要
是由实例变量造成的,因此在Servlet中应避免使用实例变量。如果应用程序设计无法避免使用实例变量,那么使用同步来保护要使用的实
例变量,但为保证系统的最佳性能,应该同步可用性最小的代码路径。

转发与重定向的区别(资源跳转)

在这里插入图片描述

代码上的实现:


转发:request.getRequestDispatcher(/servletPath”).forward(request,response); 

重定向:response.sendRedirect(/webcontextPath/servletPath”);

注意资源路径:

在转发中不需要编写web应用的根路径名称。
但是在重定向的时候需要编写web应用的根路径名称,假设web应用的根路径名称PrjStudyServlet,该web应用的根下有a资源,如果是转发则路径写 /a,如果是重定向则 /PrjStudyServlet/a
  • forward方法只能将请求转发给同一个WEB应用中的资源(这个资源可能是Servlet、JSP、HTML等),而HttpServletResponse.sendRedirect 方法不仅可以重定向到当前应用程序中的其他资源,还可以重定向到另一个WEB应用中的资源

  • 调用HttpServletResponse.sendRedirect方法重定向的访问过程结束后,浏览器地址栏中显示的URL会发生改变,由初始的URL地址变成重定向的目标URL;而调用RequestDispatcher.forward 方法的请求转发过程结束后,浏览器地址栏保持初始的URL地址不变。

  • HttpServletResponse.sendRedirect方法对浏览器的请求直接作出响应,响应的结果就是告诉浏览器去重新发出对另外一个URL的 访问请求,这个过程好比有个绰号叫“浏览器”的人写信找张三借钱,张三回信说没有钱,让“浏览器”去找李四借,并将李四现在的通信地址告诉给了“浏览器”。于是,“浏览器”又按张三提供通信地址给李四写信借钱,李四收到信后就把钱汇给了“浏览器”。可见,“浏览器”一共发出了两封信和收到了两次回复, “浏览器”也知道他借到的钱出自李四之手。

  • RequestDispatcher.forward方法在服务器端内部将请求转发给另外一个资源,浏览器只知道发出了请求并得到了响应结果,并不知道在服务器程序内部发生了转发行为。这个过程好比绰号叫“浏览器”的人写信找张三借钱,张三没有钱,于是张三找李四借了一些钱,甚至还可以加上自己的一些钱,然后再将这些钱汇给了“浏览器”。可见,“浏览器”只发 出了一封信和收到了一次回复,他只知道从张三那里借到了钱,并不知道有一部分钱出自李四之手。

  • RequestDispatcher.forward方法的调用者与被调用者之间共享相同的request对象和response对象,它们属于同一个访问请求和响应过程;

  • 而HttpServletResponse.sendRedirect方法调用者与被调用者使用各自的request对象和response对象,它们属于两个独立的访问请求和响应过程。对于同一个WEB应用程序的内部资源之间的跳转,特别是跳转之前要对请求进行一些前期预处理,并要使用HttpServletRequest.setAttribute方法传递预处理结果,那就应该使用RequestDispatcher.forward方法。不同WEB应用程序之间的重定向,特别是要重定向到另外一个WEB站点上的资源的情况,都应该使用HttpServletResponse.sendRedirect方法。

无论是转发还是重定向,转发和重定向后面不能再有转发和重定向相关的代码。
(转发和重定向在同一个Servlet中只能有一次。)

怎么选择是重定向还是转发呢?
通常情况下转发更快,而且能保持request内的对象,所以他是第一选择。但是由于在转发之后,浏览器中URL仍然指向开始页面,此时如果重载当前页面,开始页面将会被重新调用。

如果你不想看到这样的情况,则选择转发(页面刷新问题)。 不要仅仅为了把变量传到下一个页面而使用session作用域,那会无故增大变量的作用域,转发也许可以帮助你解决这个问题。

重定向:以前的request中存放的变量全部失效,并进入一个新的request作用域。  
转发:以前的request中存放的变量不会失效,就像把两个页面拼到了一起。

什么是一次请求:

浏览器向服务器发送请求到服务器响应结束为一次请求。
但是重定向发送了两次请求,服务器响应了两次。

1、	如果是web应用之间资源的跳转,必须使用重定向。
2、	为了解决页面的刷新问题,必须使用重定向。
3、	如果在Servlet中向request对象中存储了一个数据,希望在下一个Servlet/JSP页面中把request对象中的数据取出来,这个时候必须使用转发,因为重定向是两次请求,request不能跨请求传递数据。其它情况都可以是用重定向。

1、web系统中资源跳转,怎么做?

需要使用转发机制(forward)和重定向机制(redirect)完成资源跳转

注意:跳转的下一个资源不一定是一个Servlet,下一个资源可能是:JSP、Servlet、html…

2、转发

代码怎么写:
request.getRequestDispatcher("/b").forward(request,response);

  • 转发是一次请求
  • 转发是request对象触发的
  • 用户点击超链接:http://localhost/prj-servlet-16/a,浏览器地址栏上最终显示的地址也是:http://localhost/prj-servlet-16/a
  • 转发的资源路径中不需要添加contextPath
  • 转发只能完成项目内部资源的跳转

3、重定向

- 代码怎么写:
	response.sendRedirect("/prj-servlet-16/b");
- 重定向是两次请求
- 重定向是response对象触发的
- 用户点击超链接:http://localhost/prj-servlet-16/a,
浏览器地址栏上最终显示的地址是:http://localhost/prj-servlet-16/b
- 重定向的资源路径需要添加contextPath
- 重定向可以完成跨项目资源跳转

4、转发和重定向的区别

5、什么时候使用转发?什么时候使用重定向?

若跨项目跳转只能用重定向

大部分情况下,重定向使用较多

在上一个程序中向request范围中存储了数据,希望能从下一个程序中将request范围中的数据取出,必须使用转发

重定向可以解决页面刷新问题(F5)

6、这个描述不正确
在浏览器上用户点击超链接,到最终网页停下来,是一次请求。这个描述已经不正确了。
因为这个过程中可能发生重定向操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值