Servlet第二篇-Servlet单例及请求响应对象

Servlet是单例的

为什么Servlet是单例的

浏览器多次对Servlet的请求,一般情况下,服务器只创建一个Servlet对象,也就是说,Servlet对象一旦创建了,就会驻留在内存中,为后续的请求做服务,直到服务器关闭。对应着一个Servlet的生命周期(上节已经讲过)

之所以是单例的,这是一种设计模式的选择,而不是缺陷。

每次访问请求对象和响应对象都是新的

而对于每次访问请求,Servlet都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service方法再根据请求方式分别调用doPost或doGet方法。

单例导致的线程安全问题

当多个用户访问Servlet的时候,服务器会为每个用户创建一个线程。当多个用户并发访问Servlet共享资源的时候就会出现线程安全问题。

因此:

  1. 如果一个变量需要多个用户共享,则应当在访问该变量的时候,加同步机制synchronized (JavaSE基础)
  2. 如果一个变量不需要共享,则直接在 doGet() 或者 doPost()定义.这样不会存在线程安全问题



请求对象Request

如果我们想针对请求响应的内容进行操作,我们可以通过以下API 获取请求的相关资源:

①获取请求行数据

​ • String getContextPath():获取虚拟路径
​ • String getRequestURI(): 获取当前资源路径(真实路径是运行在服务器之后的路径,而不是存在于我们本地工作空间的目录)


②获取请求头数据

​ • String getHeader(String name) :通过请求头的名称获取请求头的值
​ • Enumeration getHeaderNames(): 获取所有的请求头名称


③获取请求体数据:

请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数
• BufferedReader getReader() : 获取字符输入流,只可以操作字符数据
• ServletInputStrem getInputSterm():获取字节输入流,可以操作所有类型数据


④请求转发,跳转页面

  • getRequestDispatcher(“b.jsp”).forward(request,response):(当前页面跳转到b.jsp)

⑤获取项目的ServletContext对象

ServletContext对象代表整个web应用,可以和程序的容器(服务器)来交互通信

获取途径:

  • 通过request对象获取 request.getServletContext()
  • 通过HttpServlet获取 this.getServletcContext( )

⑥获取请求参数的方法(get、post方法都通用)

在requests对象中,获取请求参数是公用的,post和get的差别就没有了,所以我们可以在doget中调用dopost(反之亦然)。

  • string getParameter(String name) :根据参数名称获取参数值
  • Map<string, String[]> getParameterMap() :获取所有参数的map集合
  • Void SetCharacterEncoding(“utf-8”):设置post请求的编码格式



响应对象Response

①设置cookies

void addCookie( Cookie cookie );服务端向客户端增加cookie对象


②响应重定向

void sendRedirect(String location ) throws lOException;:页面跳转的一种方式(重定向)


③设置相应格式

void setContentType(String type):设置服务端响应的编码(设置服务端的contentType类型)
当响应格式是json时,应该设置为 application/json;charset=utf-8;



重定向与请求转发的区别:

我们从API可以看到,request有请求转发getRequestDispatcher,而response有重定向sendRedirect。

听上去好像没有什么区别,但其实这两者区别还是蛮大的。我们通过小案例看看先:


我编写了三个简单的jsp页面,不需要看懂,我会事无巨细的讲述里面每一句代码的意思。

我们的关注点只需要在于redirect和forward。

① 首先,我们编写一个HTML表单(login.jsp)

1
2
3
4
5
<form action="check.jsp" method="post">
用户名:<input type="text" name ="uname"><br/>
密码:<input type="password" name ="upwd"><br/>
提交:<input type="submit" value="登陆"><br/>
</form>

②然后,编写一个JSP代码(check.jsp)

1
2
3
4
5
6
7
8
9
10
11
12
<%
// 验证密码登录
request.setCharacterEncoding("utf-8");
String name = request.getParameter("uname");
String pwd = request.getParameter("upwd");
if (name.equals("zs") && pwd.equals("abc")){
request.getRequestDispatcher("success.jsp").forward(request,response);
// 请求转发,可以获取到数据,并且地址栏没有改变(仍然保留转发前的页面)
}else{
out.print("用户名或密码有误");
}
%>
  • <%%> 里面嵌套着Java代码,仅此而已。
  • 我来解释一下其中的代码:
    • request.setCharacterEncoding(“utf-8”):设置请求编码为UTF-8
    • request.getParameter(“uname”):在介绍request的API讲过,意思是获取request请求中的uname属性。这里的传的参数对应着表单中的name属性
    • 接下来,判断是否输入名称为“zs”,以及密码为“abc”,否则打印“用户名或密码有误”
    • request.getRequestDispatcher(“success.jsp”).forward(request,response); 调用request的getRequestDispatcher方法,其接收一个资源文件。后面跟着的.forward表示进行转发,携带request和response参数。也就是说,携带着此次请求的所有参数。

③ 最后,编写一个验证成功的页面(success.jsp)

1
2
3
4
登录成功!
<%
out.print(request.getParameter("uname"));
%>



我们这时候,先注册表单,然后提交:

接着,check.jsp会接收到数据,然后转发到success.jsp。(注意:我们使用的是请求转发)

  • 可以看到,URL并未发生转变,但是内容已经是success.jsp页面,并且已经获取到我们注册的数据 “zs”。

那么,这次获取属性就算成功了。接下来我们看看重定向。

我们只需更改check.jsp的部分代码,而无需改动其他页面。

1
2
3
4
5
6
7
<%
if (name.equals("zs") && pwd.equals("abc")){
response.sendRedirect("success.jsp");// 重定向,会导致数据丢失
}else{
out.print("用户名或密码有误");
}
%>
  • 调用response的sendRedirect方法,传入一个重定向的页面。

同样的,success.jsp会进行数据展示:

  • 但是! 这里的uname却是null,为什么呢? 因为我们在check.jsp中获取到的参数在重定向的过程中丢失掉了。
  • 而且,URL也发生变化,这里显示的success.jsp。



于是,我们得出以下结论:

请求转发 Forward的特点

1. 转发地址栏路径不变
2. 转发只能访问当前服务器下的资源
3. 转发是一次请求,可以使用request对象来共享数据
     4. 转发是服务器跳转只能去往当前web应用的资源

重定向redirect的特点:

1. 地址栏发生变化
2. 重定向可以访问其他站点(服务器)的资源
3. 重定向是两次请求。request中数据会丢失
     4. 重定向是服务器跳转,可以去往任何的资源



请求转发和重定向图解

后记

这个实验涉及到了作用域的问题,我们下次会进行讲解

下回见,peace~