1. 请求调派的目的:
1) 在Web应用程序中一个请求通常会由多个Servlet相互协调来完成相应,因此需要在程序中调派各个Servlet协作响应;
2) 请求调派主要有两种类型:
i. include:包含,即将另一个Servlet的执行流程包含在当前Servlet中;
ii. forward:转发,将请求转发给另一个Servlet来处理;
iii. 两种方法特点和不同:
a. 特点:如果在一个Servlet中调用了include或forward,则都会等待调用结束返回后才会执行接下来的语句(函数调用后面的语句),即都是等待式的;
b. 不同:不同主要表现在输出上,include调用后的页面输出语句有效,forward调用后的页面输出语句无效(最终不会出现在客户端的相应页面中),即forward将输出流转发了,被调用的Servlet响应完成后直接将输出流返回给客户端了,而调用它的Servlet将只能得到一个空的无法再输出的输出流(PrintWriter out);
2. 请求调派的实现步骤:
1) 首先需要创建一个调派员引用RequestDispatcher dispatcher;
2) 然后再利用HttpServletRequest的getRequestDispatcher函数获取一个调派员对象来初始化dispatcher引用:RequestDispatcher getRequestDispatcher(String path);
3) 其中getRequestDispatcher的参数path需要指定要将响应调派给谁,填写的应该是目标Servlet的URL模式;
4) 接下来在需要的地方调用dispatcher的include或forward方法进行包含或转发:
i. include:void include(ServletRequest request, ServletResponse response);
ii. forward:void forward(ServletReqeust request, ServletResponse response);
!!传的参数就是主调Servlet的request和response;
5) 举例:
ReuqestDispatcher dispatcher = request.getRequestDispatcher("other.do?data=123456");
request.include(request, response); // request.forward(request, response);
!!可以在设定调派目的地时添加新的请求参数!上面的例子就是,在other.do后面添加了新的请求参数data=123453. 共享对象——请求范围参数:
1) 有时候在调派前可能在主调Servlet中会创建一些对象或其它数据想让被调Servlet利用,但是调派时只能传递请求对象中的原来就保存好的那些字符串数据;
2) HttpServletRequest提供了请求范围参数的功能,可以让你将一些临时创建的对象作为请求的一个参数专递给其它的Servlet,而这些属性可以称为对象属性,因为相对于URL中指定的参数属性来说(URL中的参数属性都是纯字符串类型的);
3) 为什么叫做请求范围参数呢?因为这些对象属性的生命周期是伴随请求对象的,当请求对象被销毁时它们也会被销毁,因此它们的生命范围就是请求的有效范围,但是为了便于理解后面还是会称为对象参数;
4) 如何设置和提取对象属性呢?
i. 设置:void HttpServletRequest.setAttribute(String name, Object o);
ii. 提取:Object HttpServletRequest.getAttribute(String name);
!!其中name就是自己起的对象参数名,前者在调派前设定,后者在被调者中提取并利用;
iii. 示例:
// 主调中
List<Book> books = bookDAO.query("C++");
request.setAttribute("books", books);
request.getRequestDispatcher("do.view").include(request, response);
// 被调中
List<Book> books = (List<Book>)request.getAttribute("books");
...
5) 获取所有对象属性的参数名:Enumeration<String> ServletRequest.getAttributeNames(); // 这是一个从ServletRequest继承而来的方法
6) 移除指定的对象属性:void ServletRequest.removeAttribute(String name);
4. Model 2设计思想简述:
1) 这里利用刚学的请求调派,因此顺便介绍一下请求调派在Model 2设计思想的运用;
2) Model 2大致的设计流程:
i. 请求进入控制器;
ii. 控制器获取请求参数,然后将请求参数交给相应的模型处理(业务逻辑层,直接解决问题)获得运算结果;
iii. 模型运算完毕回到控制器中,同时将运算结果返回给控制器;
iv. 控制器将模型的运算结果作为对象属性插入到请求对象中,再将请求对象调派给专门负责显示画面的Servlet处理;
v. 负责画面输出的Servlet提取请求对象中模型的运算结果,将数据运算结果可视化返回给客户端浏览器显示;
3) 因此可以看出:
i. 控制器必然是一个Servlet,控制器起到了获取请求参数、模型/视觉输出之间进行协调的作用;
ii. 模型完全可以是一个纯Java类,负责运算、处理数据,并返回结果;
iii. 画面输出Servlet只负责画面输出;
!!因此可以看到在Model 2设计思想中控制器中绝对不能包含画面输出语句,模型最好是纯Java类值负责运算、处理而不要包含任何和请求/响应有关的任何语句,画面输出Servlet最好只包含画面输出语句,不要包含任何和业务逻辑处理有关的任何语句,因此在画面输出这一环通常是由JSP实现的;
5. Model 2设计思路示例:
1) 这里我们展示一个Model 2设计思路下的一个程序实例,由于还没学JSP,因此画面输出这一环先使用Servlet来实现;
2) 控制器:HelloController.java
package com.lirx;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class HelloController
*/
@WebServlet("/hello.do")
public class HelloController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public HelloController() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
private HelloModel model = new HelloModel();
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//response.getWriter().append("Served at: ").append(request.getContextPath());
String name = request.getParameter("user");
String message = model.doHello(name); // 获取请求参数并交由模型来处理
request.setAttribute("message", message); // 将模型的运算结果插入到请求对象中
request.getRequestDispatcher("hello.view").forward(request, response); // 转发给画面输出进行输出
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
3) 模型:HelloModel.java
package com.lirx;
import java.util.HashMap;
import java.util.Map;
public class HelloModel {
private Map<String, String> messages = new HashMap<String, String>();
public HelloModel() {
messages.put("Peter", "Hello");
messages.put("Tom", "Welcome");
messages.put("Marry", "Hi");
}
public String doHello(String user) {
return messages.get(user) + ", " + user + "!";
}
}
4) 画面输出:HelloView.java
package com.lirx;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class HelloView
*/
@WebServlet("/hello.view")
public class HelloView extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public HelloView() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
private String htmlTemplate =
"<html>" +
" <head>" +
" <meta http-equiv='Content-Type'" +
" content='text/html;charset=UTF-8'>" +
" <title>%s</title>" +
" </head>" +
" <body>" +
" <h1>%s</h1>" +
" </body>" +
"</html>";
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//response.getWriter().append("Served at: ").append(request.getContextPath());
String user = request.getParameter("user");
String message = (String)request.getAttribute("message");
String html = String.format(htmlTemplate, user, message);
response.getWriter().print(html);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
5) 测试URL:http://localhost:8080/HelloModel2/hello.do?user=Peter,结果在页面中返回Hello, Peter!
6) 和HelloView等效的JSP:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>${param.user}</title>
</head>
<body>
<h1>${message}</h1>
</body>
</html>
!可以看到该JSP里不包含任何Java代码;
!!同时控制器中不包含任何画面输出语句;
!!而且模型也只是一个纯Java类;