Servlet關(guān)于RequestDispatcher的原理詳解
RequestDispatcher簡介
RequestDispatcher 代表請求的派發(fā)者。它有2個動作:forward 和 include ??蛻舳藢τ谌魏我粋€請求,可以根據(jù)業(yè)務(wù)邏輯需要,選擇不同的處理辦法:
1、請求的是誰,誰就自己處理并響應(yīng),例如請求的是一個html,則web瀏覽器顯示的就是這個HTML的內(nèi)容。
2、使用RequestDispatcher讓其它的資源參與進來,協(xié)同完成的響應(yīng),這就是RequestDispatcher的主要作用。
RequestDispatcher 有一個特點,就是瀏覽器上顯示的URL是最先請求的目標資源的URL,不會因為使用了forward、include方法而改變。因此forward和include的調(diào)用對于用戶來說是透明的。
RequestDispatcher 實質(zhì)是一個接口,有2個方法分別代表這2個動作。下面一 一介紹。
public interface RequestDispatcher
{
public void forward(ServletRequest request, ServletResponse response)
throws ServletException, IOException;
public void include(ServletRequest request, ServletResponse response)
throws ServletException, IOException;
}
RequestDispatcher.forward(request, response)
這個方法將請求從一個 Servlet or JSP目標資源 上 轉(zhuǎn)發(fā)到服務(wù)器上的另一個資源(servlet、JSP 文件或 HTML 文件,這些資源必須是當前Web上下文中的),讓其它的資源去生成響應(yīng)數(shù)據(jù)。
例如用戶請求的是目標資源A,A接受到請求后,轉(zhuǎn)發(fā)到B,真正產(chǎn)生響應(yīng)數(shù)據(jù)是被轉(zhuǎn)發(fā)的資源B,而A只是起個引導轉(zhuǎn)發(fā)作用。瀏覽器的地址欄不會變,依然是A的URL。
這個方法可以允許被請求的目標資源做一些準備工作后,再讓轉(zhuǎn)發(fā)的資源去響應(yīng)請求。例如下面的例子1。
注意事項:
1、在目標資源中調(diào)用forward方法時,必須保證此響應(yīng)沒有提交。也就是不要使用 ServletResponse 對象的輸出流對象,因為即便你寫入了數(shù)據(jù)到響應(yīng)緩沖區(qū),最后也會被清空,如果緩沖區(qū)數(shù)據(jù)被刷新提交(out.flush),還會拋出IllegalStateException異常。
2、對于forward方法傳遞的request對象:雖然我們從調(diào)用上看,好像是將request對象傳遞給轉(zhuǎn)動的資源上去了,但是我發(fā)現(xiàn)目標資源使用的request對象和轉(zhuǎn)發(fā)的資源使用的request對象不是同一個request對象,因為分別從這2個request中獲取RequestURL,發(fā)現(xiàn)是不一樣的。但是在目標資源request提取的Paramter 和 Attribute ,在轉(zhuǎn)發(fā)后的資源的request對象中,依然都可以提取到,且是相同的。所以,二者只是在請求路徑相關(guān)的屬性上不同,其它API調(diào)用返回的都是一樣的。
3、在forward語句的前后,都不應(yīng)該有響應(yīng)輸出的語句,應(yīng)該會被忽略。
例子1:一個簡單的 MVC演示。Servlet充當控制器,轉(zhuǎn)發(fā)到view層的jsp。
User.java
public class User{
private String name;
private int age;
public String getName(){
return name ;
}
public void setName( String name ){
this .name = name ;
}
public int getAge() {
return age ;
}
public void setAge( int age ){
this .age = age ;
}
}
UsersServlet.java
public class UsersServlet extends HttpServlet {
private static final long serialVersionUID = 1L ;
protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException , IOException {
/*****************一般實際開發(fā)這些用戶數(shù)據(jù)都是從數(shù)據(jù)庫查出來的*********/
List <User > users = new ArrayList <> ();
User u1 = new User () ;
u1 .setAge ( 20) ;
u1 .setName ( "Bob") ;
User u2 = new User () ;
u2 .setAge ( 21) ;
u2 .setName ( "Tony") ;
users .add ( u1) ;
users .add ( u2) ;
/*********************************************/
request .setAttribute ( "users", users) ; //對request 進制預處理準備工作
request .getRequestDispatcher ( "users.jsp").forward( request , response );//轉(zhuǎn)發(fā)到users.jsp,讓他去具體響應(yīng)
}
}
users.jsp
<%@ page contentType= "text/html; charset=UTF-8" pageEncoding ="UTF-8" trimDirectiveWhitespaces= "true"
session ="true" %>
<%@ taglib prefix= "c" uri = "http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
< html>
<head>
<meta http-equiv = "Content-Type" content ="text/html; charset=UTF-8">
<title> 用戶列表</title>
</head>
<body>
<p> -----------------轉(zhuǎn)發(fā)到的資源users.jsp產(chǎn)生的響應(yīng)數(shù)據(jù)------------------ </p>
< c:forEach var ="user" items= " ${users}" >
用戶姓名:${user.name} 用戶年齡:${user.age} <br />
</ c:forEach>
</body>
</html>

例子2:不使用Attribute,使用Paramter向轉(zhuǎn)發(fā)的資源傳遞參數(shù)。
雖然request對象沒有setParameter方法來設(shè)置參數(shù),但是我們可以在轉(zhuǎn)發(fā)的URL后通過QueryString 的方式添加。JSP中的<jsp:foward>標簽下的<jsp:param>標簽就是使用的這個原理。

AimServlet.java
public class AimServlet extends HttpServlet {
private static final long serialVersionUID = 1L ;
protected void doGet( HttpServletRequest request , HttpServletResponse response) throws ServletException , IOException {
request .getRequestDispatcher ( "foo.jsp?num=1") . forward( request , response );
}
}
foo.jsp
<%@ page contentType= "text/html; charset=UTF-8" pageEncoding ="UTF-8" trimDirectiveWhitespaces= "true"
session ="true" %>
<%@ taglib prefix= "c" uri = "http://java.sun.com/jsp/jstl/core" %>
<! DOCTYPE html>
<html>
<head>
<meta http-equiv = "Content-Type" content ="text/html; charset=UTF-8">
<title> 標題</title>
</head>
<body>
通過forward傳遞過來的參num=${param.num}
</body>
</html>
RequestDispatcher.include(request, response)
此方法用于包含響應(yīng)中某個資源(servlet、JSP 頁面和 HTML 文件)的內(nèi)容。
調(diào)用者指定一個被包含的資源,將這個包含的資源(JSP,Servlet,HTML)的響應(yīng)數(shù)據(jù)包含到自己的響應(yīng)體中。被包含的數(shù)據(jù)是在服務(wù)器上經(jīng)過運行產(chǎn)生的,因此是動態(tài)包含,而不同于JSP中的include指令,它是JSP轉(zhuǎn)譯期的靜態(tài)包含,類似于C語言中的宏一樣。
這個過程實質(zhì)是用一個相同的Request再請求一次被包含的資源,將被包含的資源的響應(yīng)數(shù)據(jù)包含到原本的資源中去,構(gòu)成它的響應(yīng)數(shù)據(jù)的一部分。

注意事項:
1、被包含者不能設(shè)置ServletResponse的響應(yīng)狀態(tài)和響應(yīng)頭(否則并不會產(chǎn)生效果),因為這些都是包含者做的事,被包含者只需要產(chǎn)生響應(yīng)數(shù)據(jù)解可以了。
2、不同于 forward中的request的傳遞特性:在被包含的資源中從request中獲取請求路徑相關(guān)的信息,發(fā)現(xiàn)依然是原始請求的路徑,也就是瀏覽器地址欄相關(guān)的路徑,也就是說被包含的資源獲得的request對象的路徑屬性和原始請求資源的路徑一樣(見下面的例子1)。其它的API調(diào)用也是一樣的(Attribute 和Parameter)。
例子1
TargetServlet.java
public class TargetServlet extends HttpServlet {
private static final long serialVersionUID = 1L ;
protected void doGet( HttpServletRequest request , HttpServletResponse response) throws ServletException , IOException {
response .setContentType ( "text/html;charset=utf-8" );
PrintWriter out = response .getWriter () ;
out .println ( "----------來自TargetServlet的告白----------------<br />" ) ;
out .print ( "我偷懶了,下面的響應(yīng)數(shù)據(jù)并不是我自己產(chǎn)生的,而是包含的其它資源產(chǎn)生的<br/>" ) ;
request .getRequestDispatcher ( "test.jsp") . include( request , response );
out .flush () ;
out .close () ;
}
}
test.jsp
<%@ page contentType= "text/html; charset=UTF-8" pageEncoding = "UTF-8" trimDirectiveWhitespaces = "true"
session = "false"
%>
<p> ------------------------來自test.jsp的告白-------------------------- </p>
<p> 我輸出的響應(yīng)數(shù)據(jù)將被其它的資源包含 </p>
請的URL是 <%= request.getRequestURL().toString() %> ,可以看出客戶端真正請求的不是我,我只是幕后工作者。
<p> 但我很開心,因為響應(yīng)給客戶端的數(shù)據(jù)一部分來自于我 </p>
例子2:通過包含路徑后追加QueryString來向被包含資源傳遞參數(shù),以及通過request.setAttribute傳遞屬性。
同樣, JSP中的<jsp:include>標簽下的<jsp:param>標簽就是通過在含路徑后追加QueryString達到的傳遞參數(shù)的效果。
public class TargetServlet extends HttpServlet {
private static final long serialVersionUID = 1L ;
protected void doGet( HttpServletRequest request , HttpServletResponse response) throws ServletException , IOException {
response .setContentType ( "text/html;charset=utf-8" );
PrintWriter out = response .getWriter () ;
out .println ( "----------來自TargetServlet的告白----------------<br />" ) ;
out .print ( "我偷懶了,下面的響應(yīng)數(shù)據(jù)并不是我自己產(chǎn)生的,而是包含的其它資源產(chǎn)生的<br/>" ) ;
request .setAttribute ( "sharedatt", "I`m shared attribute") ;
request .getRequestDispatcher ( "test.jsp?sharedparam=Im-shared-parameter" ). include (request , response ) ;
out .flush () ;
out .close () ;
}
}
<%@ page contentType= "text/html; charset=UTF-8" pageEncoding = "UTF-8" trimDirectiveWhitespaces = "true"
session = "false"
%>
<p> ------------------------來自test.jsp的告白-------------------------- </p>
<p> 我輸出的響應(yīng)數(shù)據(jù)將被其它的資源包含 </p>
<p> 從request中提取共享的屬性Attribute : <%= request.getAttribute("s haredatt") %>
<p> 從request中提取共享的參數(shù)Parameter : <%= request.getParameter("sharedparam" ) %>

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot實現(xiàn)異步任務(wù)的項目實踐
本文將使用SpringBoot 去實現(xiàn)異步之間的調(diào)用,提高系統(tǒng)的并發(fā)性能、用戶體驗,具有一定的參考價值,感興趣的可以了解一下2023-10-10
Mybatis攔截器實現(xiàn)數(shù)據(jù)權(quán)限詳解
這篇文章主要介紹了Mybatis攔截器實現(xiàn)數(shù)據(jù)權(quán)限詳解, 通過Mybatis攔截器我們可以攔截某些方法的調(diào)用,我們可以選擇在這些被攔截的方法執(zhí)行前后加上某些邏輯,需要的朋友可以參考下2023-11-11
SpringBoot整合EasyExcel實現(xiàn)導入導出數(shù)據(jù)
這篇文章主要為大家詳細介紹了如何使用Vue、SpringBoot和EasyExcel實現(xiàn)導入導出數(shù)據(jù)功能,感興趣的小伙伴可以跟隨小編一起學習一下2022-05-05
SpringBoot整合Lucene實現(xiàn)全文檢索的詳細步驟
全文搜索(Full-Text?Search)是指對大規(guī)模存儲在計算機系統(tǒng)中的文本數(shù)據(jù)進行檢索和匹配的技術(shù),它允許用戶輸入關(guān)鍵字,然后從海量的文本數(shù)據(jù)中快速找到相關(guān)的信息,本文介紹了SpringBoot整合Lucene實現(xiàn)全文檢索的詳細步驟,需要的朋友可以參考下2024-03-03

