




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
第7章Web服務(wù)器端編程與JavaServlet7.1Web服務(wù)器端編程概述
7.2客戶端數(shù)據(jù)的提交和服務(wù)器端的提取7.3Web程序中數(shù)據(jù)的分類7.4Web數(shù)據(jù)傳輸協(xié)議——HTTP7.5Servlet的提出和工作原理7.6Servlet的設(shè)計和實現(xiàn)
7.7ServletAPI常用接口和類7.8Servlet應(yīng)用實例習(xí)題
7.1Web服務(wù)器端編程概述
Web服務(wù)器端編程負(fù)責(zé)完成在服務(wù)器端運行的程序的設(shè)計和編碼,這些程序的主要功能一般是進行業(yè)務(wù)邏輯的處理。通常的做法是數(shù)據(jù)的存儲使用數(shù)據(jù)庫系統(tǒng)及文件系統(tǒng),通過使用數(shù)據(jù)庫訪問接口訪問數(shù)據(jù)庫系統(tǒng)中存儲的數(shù)據(jù),根據(jù)應(yīng)用系統(tǒng)業(yè)務(wù)邏輯對這些數(shù)據(jù)進行處理,然后把相關(guān)處理結(jié)果發(fā)送到客戶端,或者存儲到文件或數(shù)據(jù)庫系統(tǒng)中。Web服務(wù)器端編程模型如圖7.1所示。圖7.1Web服務(wù)器端編程模型根據(jù)以上模型可知,開發(fā)Web系統(tǒng)首要的工作就是系統(tǒng)模塊劃分。從邏輯層次上可以將系統(tǒng)模塊劃分為如下三層:
(1)界面層:主要用于界面展示、客戶輸入數(shù)據(jù)。該部分用HTML(HyperTextMarkupLanguage)格式的文件作為客戶端軟件解析、顯示的數(shù)據(jù)文件,即通常所說的網(wǎng)頁。在HTML文件中可以包含在客戶端瀏覽器環(huán)境下解釋執(zhí)行的客戶端腳本(如受瀏覽器支持的廣泛使用的JavaScript腳本)、用來控制顯示樣式的CSS(CascadingStyleSheets)文件、用來繪制動態(tài)圖形和完成簡單客戶端功能的JavaApplet等。
(2)應(yīng)用邏輯處理層:也稱為業(yè)務(wù)處理層,主要完成系統(tǒng)數(shù)據(jù)處理,是系統(tǒng)的核心。數(shù)據(jù)是業(yè)務(wù)在計算機中的表示,根據(jù)系統(tǒng)業(yè)務(wù)流程設(shè)計出程序流程圖,進而去編碼實現(xiàn)。根據(jù)服務(wù)器支持的運行環(huán)境,可選用不同的開發(fā)腳本語言。如果服務(wù)器端具備Java運行環(huán)境,可以使用JSP(JavaServerPages)腳本、Servlet程序;如果具備PHP(HypertextPreprocessor)運行環(huán)境,可以使用PHP腳本。當(dāng)今可以選用的開發(fā)腳本語言多達十多種,在制定系統(tǒng)方案時,可以根據(jù)系統(tǒng)應(yīng)用環(huán)境和成本、開發(fā)難度,選取一種開發(fā)腳本語言來作為系統(tǒng)開發(fā)腳本語言。
(3)數(shù)據(jù)存儲層:用來存儲數(shù)據(jù),依據(jù)系統(tǒng)設(shè)計階段設(shè)計的數(shù)據(jù)字典,將系統(tǒng)數(shù)據(jù)存儲在數(shù)據(jù)庫系統(tǒng)文件中,然后通過數(shù)據(jù)庫訪問接口訪問該數(shù)據(jù)庫系統(tǒng)中的數(shù)據(jù)。數(shù)據(jù)庫的系統(tǒng)維護和管理由數(shù)據(jù)庫系統(tǒng)管理員負(fù)責(zé),數(shù)據(jù)庫數(shù)據(jù)的訪問通過腳本程序進行,這就實現(xiàn)了數(shù)據(jù)庫的透明訪問,提高了系統(tǒng)的安全性和可靠性。
該種劃分法從邏輯層次到物理實現(xiàn)的映射可用圖7.2來表示。系統(tǒng)設(shè)計和分析人員通常根據(jù)這種邏輯劃分來分析和定義系統(tǒng)模塊。程序設(shè)計的流程按照數(shù)據(jù)的讀取、保存和處理來分別進行。由于采用了數(shù)據(jù)庫系統(tǒng),故數(shù)據(jù)的讀取和保存變得容易,使程序設(shè)計者能夠把主要精力放在業(yè)務(wù)流程對應(yīng)的數(shù)據(jù)處理上。系統(tǒng)和用戶的交互界面一般是瀏覽器支持的GUI圖形界面,即由文本、圖片、表單構(gòu)成的頁面,程序設(shè)計者應(yīng)該站到系統(tǒng)界面的角度去設(shè)計頁面,與傳統(tǒng)的主要用來展示文本內(nèi)容的網(wǎng)頁區(qū)分開來。圖7.2Web應(yīng)用程序從邏輯層次到物理實現(xiàn)的映射7.2客戶端數(shù)據(jù)的提交和服務(wù)器端的提取
客戶端用戶向服務(wù)器端程序提交數(shù)據(jù)主要通過表單來進行。關(guān)于表單的標(biāo)準(zhǔn)理論請參考網(wǎng)上文檔,網(wǎng)址為/TR/html401/interact/forms.html#h-17.1。表單(Form)是對客戶端瀏覽器軟件向服務(wù)器端程序提交的數(shù)據(jù)進行封裝的控件。提供給編程人員的接口是表單和表單項。表單項的名稱用來關(guān)聯(lián)數(shù)據(jù)項名稱,在軟件中常把這種方法叫做“Key-Value”,即鍵—值對,鍵即表單項名稱,值即填入表單控件中的文本。服務(wù)器端程序提取數(shù)據(jù)時主要通過表單項來獲取對應(yīng)值,然后使用這些數(shù)據(jù)進行處理。表單通過如下形式
定義:
<formname=“l(fā)ogin”action=“forward.jsp”method=“get”>
<inputtype=“text”name=“username”value=“l(fā)iuyongping”>
</form>
或
<formname=“form1”action=“process.jsp”method=“post”>
</form>
其中,action和method屬性非常關(guān)鍵。action屬性定義了該表單信息和哪個文件相關(guān)聯(lián),即由哪個服務(wù)器端程序來提取和處理該表單數(shù)據(jù)。如在此例中由process.jsp這個程序來處理該表單數(shù)據(jù)。method屬性定義了該表單信息的傳送(傳輸)方式,可選post和get中的任意一種。采用post方式時,表單的數(shù)據(jù)是被客戶端瀏覽器封裝在請求消息體中來進行傳送的,故保密性強。而采用get方式時,表單的數(shù)據(jù)是附在URL之后,以問號“?”隔開,數(shù)據(jù)以name=value對的形式出現(xiàn)的,多個數(shù)據(jù)對使用符號“&”隔開。get方式常用于少量數(shù)據(jù)傳送(<1KByte),由于發(fā)送時數(shù)據(jù)顯示在瀏覽器地址欄中,容易被看到和記錄,故保密性差。
【例7.1】以get方式向服務(wù)器提交表單數(shù)據(jù),該表單完成用戶名和密碼的提交,如圖7.3所示。請注意觀察圖7.4的URL地址欄內(nèi)容。圖7.3登錄界面源文件login_get_method.html內(nèi)容如下:
<!DOCTYPEhtmlPUBLIC“-//W3C//DTDHTML4.01
Transitional//EN”“/TR/html4/loose.dtd”>
<html>
<head>
<metahttp-equiv=“Content-Type”content=“text/html;
charset=gb2312”>
<title>登錄模塊</title>
</head>
<bodybgcolor=“FFFFFF”>
<h1align="center"><b>歡迎登錄系統(tǒng)</b></h1>
<formaction=“forward.jsp”method=“get”>
<!--指定由forward.jsp來處理該表單,提交數(shù)據(jù)方式為post-->
<p></p>
<!--以2行2列的表格來布局-->
<tablewidth=“52%”border=“2”align=“center”>
<trbgcolor=“#FFFFCC”>
<tdalign=“center”width=“43%”><divalign=“center”>用戶名:
</div></td>
<tdwidth=“57%”><divalign=“l(fā)eft”>
<inputtype=“text”name=“username”>
</div></td>
</tr>
<trbgcolor=“#CCFF99”>
<tdalign=“center”width=“43%”><divalign=“center”>密碼:</div></td>
<tdwidth=“57”><divalign=“l(fā)eft”>
<inputtype=“password”name=“password”>
</div></td>
</tr>
</table>
<!--以段的形式來組織兩個按鈕-->
<palign=“center”>
<inputtype=“reset”name=“Reset”value=“重置”>
<inputtype=“submit”name=“Submit2”value=“提交”>
</p>
</form>
</body>
</html>
源文件forward.jsp內(nèi)容如下:
<%@pagelanguage=“java”contentType=“text/html;
charset=gb2312”
pageEncoding="gb2312"%>
<!DOCTYPEhtmlPUBLIC“-//W3C//DTDHTML4.01
Transitional//EN”“/TR/html4/loose.dtd”>
<html>
<head>
<metahttp-equiv=“Content-Type”content=“text/html;
charset=gb2312”>
<title>loginforward</title>
</head>
<body>
<%
Stringusername=null;
Stringpassword=null;
request.setCharacterEncoding(“gb2312”);
username=request.getParameter(“username”);
password=request.getParameter(“password”);
if(!username.equals(“”)&&!password.equals(“”))
{
%>
<jsp:forwardpage=“l(fā)ogin_ok.jsp”>
<jsp:paramname=“username”value=‘<%=username%>’/>
<jsp:paramname="username"value='<%=password%>'/>
</jsp:forward>
<%
}
else
{
%>
<jsp:forwardpage=“error.jsp”>
<jsp:paramname=“username”value=‘<%=username%>’/>
<jsp:paramname=“username”value=‘<%=password%>’/>
</jsp:forward>
<%
}
%>
</body>
</html>
其中,login_ok.jsp和error.jsp文件源代碼請參考9.6.1小節(jié)的例9.1中這兩個文件的源代碼。該實例運行結(jié)果如圖7.4所示。圖7.4處理數(shù)據(jù)界面
1.表單數(shù)據(jù)的填入
當(dāng)表單中的輸入控件項獲得焦點時,用戶輸入或者選擇數(shù)據(jù)。表單中可使用的控件有文本輸入控件、密碼輸入控件、單選控件、復(fù)選控件等。
【例7.2】以post方式向服務(wù)器提交表單數(shù)據(jù),該表單發(fā)送某單位工作人員的周小結(jié)給部門經(jīng)理,供部門經(jīng)理閱讀,界面如圖7.5所示。注意與例7.1的以get方式提交數(shù)據(jù)進行比較。圖7.5表單界面源文件weeklog.html內(nèi)容如下:
<!DOCTYPEhtmlPUBLIC“-//W3C//DTDXHTML1.0Strict//EN”
“/TR/xhtml1/DTD/xhtml1-strict.dtd”>
<htmlxmlns=“/1999/xhtml”>
<head><metahttp-equiv=“Content-Type”content=“text/html;
charset=gb2312”>
<title>工作周志</title></head>
<body>
<formaction=“process.jsp”method=“post”>
<div><center>
<label>姓名<inputtype=“text”name=“username”
size=“10”/>
</label><br/><br/><label>周小結(jié)<br/>
<textareaname=“weeklog”rows=“5”
cols=“60”></textarea>
</label><br/>
完成情況
<label>
<inputtype=“radio”name=“boxgroup1”value
=“非常好”/>非常好
</label>
<label>
<inputtype=“radio”name=“boxgroup1”value
=“完成”/>完成
</label>
<label>
<inputtype=“radio”name=“boxgroup1”value
=“未完成”/>未完成
</label><br/><br/>
<inputtype=“submit”name=“doit”value=“提交”/>
</center></div>
</form>
</body>
</html>
其中,<metahttp-equiv=“Content-Type”content=“text/html;charset=gb2312”>中的meta標(biāo)簽的charset屬性指定請求信息的編碼方式為支持中文字符的gb2312編碼方式,如果不指定其編碼方式,在服務(wù)器端獲取中文時可能出現(xiàn)亂碼。
當(dāng)表單中的控件獲得輸入焦點后,用戶輸入數(shù)據(jù),點擊“提交”按鈕,客戶端瀏覽器軟件完成表單數(shù)據(jù)的封裝和發(fā)送。
2.表單數(shù)據(jù)的傳送
表單數(shù)據(jù)被客戶端軟件封裝后通過網(wǎng)絡(luò)傳送到服務(wù)器端,傳送的方式由表單的method屬性指定。如果選擇的是post方式,數(shù)據(jù)放在HTTP請求消息體中;如果選擇的是get方式,數(shù)據(jù)放在HTTP請求消息頭中。服務(wù)器如Tomcat把HTTP請求消息用Java語言封裝成HttpServletRequest對象。
3.表單數(shù)據(jù)的提取
服務(wù)器軟件接收到傳送來的請求消息后,先進行消息解析,然后根據(jù)流程進行處理,最后將處理結(jié)果通過響應(yīng)消息返回給客戶端軟件。如果由JSP引擎來接收消息,該引擎將消息封裝成HttpServletRequest類型的對象并提供給JSP程序訪問,通過調(diào)用HttpServletRequest類型的對象request的方法getParameter(Stringname)來完成數(shù)據(jù)的提取。該方法的原型為ObjectgetParameter(Stringname),如提取例7.1和例7.2中的用戶名和周小結(jié)數(shù)據(jù),可使用如下語句:
Stringstr_Username=request.getParameter(“username”);
Stringstr_Weeklog=request.getParameter("weeklog");
【例7.3】提取表單數(shù)據(jù)示例。提取例7.2中的表單數(shù)據(jù)并顯示在頁面上,顯示結(jié)果如圖7.6所示。
源文件process.jsp代碼如下:
<%@pagelanguage=“java”contentType=“text/html;
charset=gb2312”
pageEncoding=“gb2312”%>
<html><head>
<metahttp-equiv=“Content-Type”content=“text/html;
charset=gb2312”>
<title>提取表單數(shù)據(jù)</title></head>
<body>
<%
request.setCharacterEncoding(“gb2312”);
response.setContentType(“text/html;charset=gb2312”);
out.println(request.getParameter(“username”));
out.println(request.getParameter(“weeklog”));
out.println(request.getParameter(“boxgroup1”));
%>
</body></html>圖7.6處理數(shù)據(jù)界面
7.3Web程序中數(shù)據(jù)的分類
Web應(yīng)用程序?qū)Υ罅繑?shù)據(jù)進行處理時,這些數(shù)據(jù)的表示方式、存儲方式、使用范圍和使用時限各不相同,為此,需要分類來進行表示。按數(shù)據(jù)的使用范圍可將Web應(yīng)用程序中的數(shù)據(jù)分為全局?jǐn)?shù)據(jù)、頁面內(nèi)數(shù)據(jù)和方法體內(nèi)數(shù)據(jù),如圖7.7所示。圖7.7Web應(yīng)用程序中數(shù)據(jù)的劃分
1.全局?jǐn)?shù)據(jù)
模塊(多個文件)之間需要通過全局?jǐn)?shù)據(jù)來交換信息,這類型的數(shù)據(jù)由系統(tǒng)開辟一塊公共存儲區(qū)進行存放,供各文件訪問。JSP系統(tǒng)提供了application、session、servletcontext等全局?jǐn)?shù)據(jù)對象,開發(fā)者可以方便地使用這些對象完成模塊之間的數(shù)據(jù)交換。
在實際應(yīng)用中,需要記錄和跟蹤用戶訪問系統(tǒng)的情況,此時需要使用全局?jǐn)?shù)據(jù)對象session。用戶在訪問不同的頁面并完成一次與系統(tǒng)的交互后,相關(guān)信息可以保存到session對象中,通過調(diào)用session對象提供的getSession和setSession方法完成從ession對象中讀取數(shù)據(jù)和向session對象中寫入數(shù)據(jù)。session數(shù)據(jù)區(qū)示意圖如圖7.8所示。圖7.8session數(shù)據(jù)區(qū)示意圖
2.頁面內(nèi)數(shù)據(jù)
頁面內(nèi)數(shù)據(jù)的可見范圍為頁面,有效期為頁面初始化到頁面執(zhí)行結(jié)束,它為頁面內(nèi)不同方法之間進行數(shù)據(jù)交換提供了途徑。JSP系統(tǒng)定義的頁面內(nèi)數(shù)據(jù)對象有page、pageContext、out、request、response、exception等,開發(fā)者可以采用類似<%!Stringstr=null;%>的語法定義各種類型的對象。一個頁面被Servlet翻譯成一個Servlet對象,頁面內(nèi)數(shù)據(jù)被Servlet翻譯成Servlet類的公共數(shù)據(jù),供Servlet的各個方法相互訪問。
3.方法體內(nèi)數(shù)據(jù)
方法體內(nèi)數(shù)據(jù)即局部變量,其可見范圍為Java類的方法體內(nèi)。JSP系統(tǒng)在腳本<%%>中定義的對象即屬于局部對象,如果數(shù)據(jù)只在方法內(nèi)使用,則定義為方法體內(nèi)數(shù)據(jù)。
在一個具體的應(yīng)用系統(tǒng)中,用戶的鑒權(quán)、認(rèn)證、跟蹤一般是必需的,Web系統(tǒng)廣泛采用的是cookie和session兩種機制。cookie用來在客戶端記錄信息,session用來在服務(wù)器端記錄信息。cookie是客戶端通過開辟一個文件供服務(wù)器端程序動態(tài)地寫入和讀出的信息集合體,服務(wù)器端程序生成cookie內(nèi)容并填入響應(yīng)消息的特定字段,再傳輸?shù)娇蛻舳?,由客戶端寫入文件保存??蛻舳讼蚍?wù)器端傳輸請求信息時,可以把cookie內(nèi)容傳輸?shù)椒?wù)器端,供服務(wù)器程序處理。
【例7.4】通過session對象保存用戶名和密碼,記錄和跟蹤系統(tǒng)用戶信息。登錄界面如圖7.9所示,登錄成功后的界面如圖7.10所示,使用系統(tǒng)業(yè)務(wù)功能界面如圖7.11所示。圖7.9登錄界面源文件firstlogin.html內(nèi)容如下:
<!DOCTYPEhtmlPUBLIC“-//W3C//DTDHTML4.01
Transitional//EN”“/TR/html4/loose.dtd”>
<html>
<head>
<metahttp-equiv=“Content-Type”content=“text/html;
charset=gb2312”>
<title>登錄模塊</title>
</head>
<bodybgcolor=“FFFFFF”>
<h1align="center"><b>歡迎登錄系統(tǒng)</b></h1>
<formaction=“savelogin.jsp”method=“post”>
<!--指定由savelogin.jsp來處理該表單,提交數(shù)據(jù)方式為post-->
<p></p>
<!--以2行2列的表格來布局-->
<tablewidth=“52%”border=“2”align=“center”>
<trbgcolor=“#FFFFCC”>
<tdalign=“center”width=“43%”><divalign=“center”>用戶名:</div></td>
<tdwidth=“57%”><divalign=“l(fā)eft”>
<inputtype="text"name="username">
</div></td>
</tr>
<trbgcolor=“#CCFF99”>
<tdalign=“center”width=“43%”><divalign=“center”>密碼:</div></td>
<tdwidth=“57”><divalign=“l(fā)eft”>
<inputtype=“password”name=“password”>
</div></td>
</tr>
</table>
<!--以段的形式來組織兩個按鈕-->
<palign=“center”>
<inputtype=“reset”name=“Reset”value=“重置”>
<inputtype=“submit”name=“Submit2”value=“提交”>
</p>
</form>
</body>
</html>圖7.10登錄成功后的界面源文件savelogin.jsp內(nèi)容如下:
<%@pagelanguage=“java”contentType=“text/html;
charset=gb2312”
pageEncoding=“gb2312”%>
<!DOCTYPEhtmlPUBLIC“-//W3C//DTDHTML4.01Transitional//EN”
“/TR/html4/loose.dtd”>
<html>
<head>
<metahttp-equiv=“Content-Type”content=“text/html;
charset=gb2312">
<title>Savelogininformation</title>
</head>
<body>
<%!Stringurl=null;%>
<%
Stringusername=null;
Stringpassword=null;
request.setCharacterEncoding(“gb2312”);
response.setContentType(“text/html;charset=gb2312”);
username=request.getParameter("username");
password=request.getParameter(“password”);
session.setAttribute(“username”,username);
session.setAttribute(“password”,password);
url=response.encodeURL(“business.jsp”);
%>
<ahref=‘<%=url%>’>usesystem!</a>
</body>
</html>圖7.11使用系統(tǒng)業(yè)務(wù)功能界面源文件business.jsp內(nèi)容如下:
<%@pagelanguage=“java”contentType=“text/html;
charset=gb2312”
pageEncoding=“gb2312”%>
<!DOCTYPEhtmlPUBLIC“-//W3C//DTDHTML4.01
Transitional//EN”
“/TR/html4/loose.dtd”>
<html>
<head>
<metahttp-equiv=“Content-Type”content=“text/html;
charset=gb2312">
<title>Usesystem</title>
</head>
<body>
<%
Stringusername;
Stringpassword;
username=(String)session.getAttribute(“username”);
password=(String)session.getAttribute(“password”);
out.println(“Currentuser[<fontcolor=red>”+username+“</font>]”);
%>
</body>
</html>
【例7.5】寫入cookie信息和從cookie中讀出信息,用于客戶端跟蹤和驗證。寫入cookie信息界面如圖7.12所示,讀出cookie信息界面如圖7.13所示。圖7.12寫入cookie信息界面源文件writecookie.jsp內(nèi)容如下:
<%@pagelanguage=“java”contentType=“text/html;charset=ISO-8859-1”
pageEncoding=“ISO-8859-1”%>
<!DOCTYPEhtmlPUBLIC“-//W3C//DTDHTML4.01
Transitional//EN”“/TR/html4/loose.dtd”>
<html>
<head>
<metahttp-equiv="Content-Type"content="text/html;charset=ISO-8859-1">
<title>writecookie</title>
</head>
<body>
<divalign=“center”>writecookie</div>
<%
Cookiecook=newCookie(“KeyName”,“Iamakeyvalue”);
cook.setMaxAge(30*60);
response.addCookie(cook);
out.print(“Finishingwritecookie”);
%>
<br>
<br>
<ahref=“readcookie.jsp”>readcookie
</body>
</html>圖7.13讀出cookie信息界面源文件readcookie.jsp內(nèi)容如下:
<%@pagelanguage=“java”contentType=“text/html;charset=ISO-8859-1”
pageEncoding=“ISO-8859-1”%>
<!DOCTYPEhtmlPUBLIC“-//W3C//DTDHTML4.01Transitional//EN”
“/TR/html4/loose.dtd”>
<html>
<head>
<metahttp-equiv=“Content-Type”content=“text/html;
charset=ISO-8859-1">
<title>readcookie</title>
</head>
<body>
<%
Cookiecookies[]=request.getCookies();
CookiesCookie=null;
StringstrValue=null;
StringstrKeyName=null;
if(cookies==null)
{
out.println("nocookierecord");
}
else
{
out.println(“<br>thenumberofcookie:”+cookies.length);
for(inti=0;i<cookies.length;i++)
{
sCookie=cookies[i];
strKeyName=sCookie.getName();
strValue=sCookie.getValue();
out.println(“<br>cookiekeyname:”+strKeyName+“,
thevalueof:”+strValue);
}
7.4Web數(shù)據(jù)傳輸協(xié)議——HTTP
Web服務(wù)器軟件和客戶端軟件之間的通信一般使用HTTP或HTTPS協(xié)議。HTTP(HypertextTransferProtocol,超文本傳輸協(xié)議)主要用來在網(wǎng)絡(luò)上傳輸文本內(nèi)容,采用請求/響應(yīng)的模式來工作,如圖7.14所示。圖7.14HTTP協(xié)議示例圖
HTTP協(xié)議包括兩種類型的消息,分別是請求消息和響應(yīng)消息。請求消息用來發(fā)送請求內(nèi)容,響應(yīng)消息用來攜帶返回服務(wù)器端的處理結(jié)果內(nèi)容。響應(yīng)消息經(jīng)常返回一個服務(wù)器的資源文件,如HTML文件等。
兩種類型的消息字段如圖7.15所示。圖7.15HTTP消息示例下面是一個HTTP請求消息的例子。
Get/sample.jspHTTP/1.1
Accept:image/gif,image/jpeg,*/*
Accept-Language:zh-cn
Connection:Keep-Alive
Host:localhost
User-Agent:Mozilla/4.0(compatible;MSIE5.01;WindowsNT5.0)
Accept-Encoding:gzip,deflate
Username=liuyongping&password=liuyongping
HTTP請求消息一般包含如下三部分:
第一部分:請求方法URI協(xié)議/版本。上面的請求消息示例第一行即指名請求方法為Get,請求的文件為?/sample.jsp,協(xié)議為HTTP,版本號為1.1。其中HTTP1.1支持get、post、head、put、delete、trace、options請求方法,如表7.1所示。其中g(shù)et和post是最常用的請求方法。該部分的URI完整地指定了要訪問的網(wǎng)絡(luò)資源,通常只要給出相對于服務(wù)器的根目錄的相對目錄即可,因此總是以“/”開頭。緊接著是協(xié)議名稱,最后以“/”分割后面的版本號說明,即所使用的HTTP的版本。
get方法通常只用于請求指定服務(wù)器上的資源,這種資源可以是靜態(tài)的HTML頁面或其他文件,也可以是由CGI程序生成的結(jié)果數(shù)據(jù)。使用get方法請求文件也可以向服務(wù)器傳送一些數(shù)據(jù),例如:
get/login.jsp?name=liuyongping&password=***HTTP/1.1
以post方法提交的數(shù)據(jù)是通過消息體的形式進行傳送的,提交的數(shù)據(jù)量一般比較大,而且比較安全,例如:
<formname="form1"method="post"action="showdata.jsp">第二部分:請求頭。該部分包含許多客戶端環(huán)境信息和請求體的重要信息,具體可包含Accept、Accept-Language、Use-Agent、Ua-Cpu、Accept-Encoding、Host、Connnection、Cookie等字段。上面的請求消息示例從第二行到“Accept-Encoding:gzip,deflate”行結(jié)尾部分都為請求頭部分。
第三部分:請求正文。上面的請求消息示例的請求頭下面隔一個空行后即為正文部分,或稱為請求消息體。該部分可以包含客戶提交的查詢字符串信息等。請求頭部分和請求正文部分的分割是一個空行。響應(yīng)消息也由三部分組成:第一行為第一部分,如“HTTP/1.1200OK”,其中HTTP是協(xié)議說明,1.1是版本號,200是響應(yīng)碼,“OK”是響應(yīng)碼說明;第二部分為響應(yīng)消息頭,包含Server、Date、Content-Type、Last-Modified、Content-Length等字段,這些字段用來說明響應(yīng)信息內(nèi)容的重要屬性,如內(nèi)容的類型(編碼方式)、內(nèi)容的長度等,客戶端軟件可根據(jù)此處理消息體內(nèi)容;第三部分為消息內(nèi)容體,若響應(yīng)頭Content-Type字段為text/html,則該部分消息內(nèi)容為超文本字符。消息頭和消息體之間通過一個空行分隔。下面是一個HTTP響應(yīng)消息的例子,顯示結(jié)果如圖7.16
所示。圖7.16HTTP響應(yīng)體內(nèi)容顯示
HTTP/1.1200OK
Server:ApacheTomcat/6.0.10
Date:Mon,6Oct200313:13:33GMT
Content-Type:text/html
Last-Modified:Mon,6Oct200313:23:42GMT
Content-Length:112
<html>
<head>
<title>HTTPresponseexample</title>
</head>
<body>
ThisisHTTPresponsebodycontent.
</body>
</html>
【例7.6】使用Java語言實現(xiàn)一個簡單的Web服務(wù)器。該Web服務(wù)器的服務(wù)端口為1000,用戶使用瀏覽器和該服務(wù)器交互。在瀏覽器地址欄中輸入“http://localhost:1000/”,服務(wù)器接收到該命令請求后,在控制臺上打印該HTTP請求信息內(nèi)容,并向客戶端返回一個只有文本“SimpleWebServer”的頁面。源程序文件SimpleWebServer.java如下:
packagecom.test.java;
importjava.io.*;
import.*;
import.ssl.*;
publicclassSimpleWebServer
{
publicstaticvoidprocess()throwsIOException
{
ServerSocketServSock=newServerSocket(1000);
//在1000端口上監(jiān)聽客戶端請求
while(true)
{
try
{
Socketsock=ServSock.accept(); OutputStreamout=sock.getOutputStream();
//關(guān)聯(lián)輸出字節(jié)流,用來寫數(shù)據(jù)
BufferedReaderin=new
BufferedReader(newInputStreamReader(sock.getInputStream()));
//關(guān)聯(lián)輸入字節(jié)流,將字節(jié)流轉(zhuǎn)化為字符流,封裝為
帶緩沖的字符流,一次讀
//取一個文本行數(shù)據(jù)
//讀取請求并顯示到頁面上
Stringline=null;
while(((line=in.readLine())!=null)&&
(!("".equals(line)))) {
System.out.println(line);
}
System.out.println(“”);
//構(gòu)造響應(yīng)消息
StringBufferbuffer=newStringBuffer();
buffer.append(“<head><title>SimpleWebServer
</title></head>\n");
buffer.append("<body>\n");
buffer.append("SimpleWebServer\n"); buffer.append(“</body>\n”);
buffer.append(“</html>\n”);
//求取發(fā)送內(nèi)容的長度
Stringstr=buffer.toString();
byte[]data=str.getBytes();
out.write(“HTTP/1.0200OK\n”.getBytes());
out.write(newString(“Content-Length:”+
data.length+"\n").getBytes());
out.write("Content-Type:text/html\n".getBytes()); out.write(data);
out.flush();
//關(guān)閉流和端口
out.close();
in.close();
sock.close();
}
catch(Exceptione)
{
e.printStackTrace(); }
}
}
publicstaticvoidmain(String[]args)throwsIOException
{
process();
}
}
執(zhí)行時控制臺打印出的HTTP請求消息內(nèi)容如下:
GET/HTTP/1.1
Accept:image/gif,image/jpeg,image/pjpeg,image/pjpeg,
application/vnd.ms-excel,application/vnd.ms-powerpoint,
application/msword,application/x-silverlight,application/x-shockwave-flash,*/*
Accept-Language:zh-cn
User-Agent:Mozilla/4.0(compatible;MSIE8.0;WindowsNT5.1;Trident/4.0)
UA-CPU:x86
Accept-Encoding:gzip,deflate
Host:localhost:1000
Connection:Keep-Alive
GET/favicon.icoHTTP/1.1
Accept:*/*
UA-CPU:x86
Accept-Encoding:gzip,deflate
User-Agent:Mozilla/4.0(compatible;MSIE8.0;WindowsNT5.1;Trident/4.0)
Host:localhost:1000
Connection:Keep-Alive
客戶端瀏覽器的結(jié)果如圖7.17所示。圖7.17客戶端瀏覽器頁面請使用網(wǎng)絡(luò)協(xié)議分析器Ethereal(已更名為Wireshark,可到網(wǎng)站下載)安裝并捕獲分組、分析分組以及HTTP協(xié)議內(nèi)容和工作過程。Wireshark工作臺主界面如圖7.18所示。圖7.18Wireshark工作臺主界面
7.5Servlet的提出和工作原理
在Web層次模型中,Web服務(wù)器端程序的核心功能是業(yè)務(wù)邏輯數(shù)據(jù)的處理。當(dāng)服務(wù)器接收到客戶端通過網(wǎng)絡(luò)傳輸?shù)腍TTP、HTTPS請求數(shù)據(jù)后,需要按照設(shè)計的業(yè)務(wù)數(shù)據(jù)處理流程進行處理,處理完成之后構(gòu)造響應(yīng)消息返回給客戶端??蛻舳耸荳eb系統(tǒng)的輸入、輸出界面。業(yè)務(wù)邏輯數(shù)據(jù)的處理可以通過定義Servlet來完成。
Servlet對業(yè)務(wù)流程數(shù)據(jù)按業(yè)務(wù)流程進行處理,其中業(yè)務(wù)數(shù)據(jù)可能來自請求,也可能來自公共存儲區(qū)對象,處理結(jié)果數(shù)據(jù)可以更新公共存儲區(qū)對象,也可以構(gòu)造響應(yīng)消息返回給客戶端。最常見的輸出是通過響應(yīng)消息輸出到客戶端,輸出數(shù)據(jù)利用輸出流來緩存,數(shù)據(jù)主要是文本。
開發(fā)Servlet時要重點把握數(shù)據(jù)的來源(輸入)、處理、輸出,可以簡單總結(jié)為“分析請求消息”、“處理消息”、“構(gòu)造響應(yīng)消息”三個步驟。一般將每個步驟的執(zhí)行代碼分別放在不同的方法體內(nèi),這樣Servlet運行環(huán)境在調(diào)用Servlet方法時可以根據(jù)不同的步驟調(diào)用不同的方法。
Servlet是用Java語言編寫的特殊的類,該類對象的創(chuàng)建(為對象分配內(nèi)存)、初始化和方法調(diào)用由服務(wù)器軟件負(fù)責(zé),開發(fā)人員只需要搞清楚調(diào)用時刻和調(diào)用參數(shù),再通過直接繼承javax.servlet.http.HttpServlet,覆蓋Servlet的部分方法(函數(shù))就可以快速定義和實現(xiàn)一個Servlet。這些方法有doGet()、doPost()等,用于處理HTTP請求。
Servlet對象和Servletr容器的通信主要通過參數(shù)傳遞進行,由Servlet容器來判斷Servlet對象的調(diào)用時刻和調(diào)用方法。Servlet對象的生命過程如圖7.19所示。Servlet容器負(fù)責(zé)管理Servlet對象,它裝入(創(chuàng)建)對象、初始化Servlet(調(diào)用init()方法)、調(diào)度Servlet的方法、銷毀Servlet對象(釋放內(nèi)存)。圖7.19Servlet對象的生命過程
Servlet容器響應(yīng)Web客戶請求的工作過程如下:
(1)?Web客戶向Web服務(wù)器發(fā)出HTTP請求。
(2)?Web服務(wù)器解析該請求。
(3)?Servlet容器創(chuàng)建一個封裝HTTP請求消息的HttpRequest對象。
(4)Servlet容器創(chuàng)建一個HttpResponse對象,該對象是HTTP響應(yīng)消息的封裝。HttpRequest對象和HttpResponse對象是容器調(diào)用Servlet的service方法傳入的參數(shù)對象引用(Reference)。Servlet處理HttpRequest對象的結(jié)果寫入HttpResponse對象,Servlet容器從HttpResponse對象數(shù)據(jù)獲取Servlet的處理結(jié)果。
(5)Servlet容器調(diào)用HttpServlet的service方法,把HttpRequest和HttpReponse對象作為service方法的參數(shù)傳遞給HttpServlet對象。
(6)?HttpServlet調(diào)用HttpRequest的相關(guān)方法,獲取HTTP請求信息。
(7)?HttpServlet調(diào)用HttpResponse的相關(guān)方法,構(gòu)造生成響應(yīng)消息數(shù)據(jù)。
(8)?Web服務(wù)器將HttpServlet的響應(yīng)結(jié)果傳給Web客戶。
關(guān)于Servlet的功能和工作過程可以參考Servlet規(guī)范的制定者Sun公司在網(wǎng)站上發(fā)表的文章,地址為/pub/a/onjava/2003/05/14/java_webserver.html。7.6Servlet的設(shè)計和實現(xiàn)
Servlet的設(shè)計主要是繼承javax.servlet.http.HttpServlet類,覆蓋doGet()、doPost()方法。在方法體中分析請求消息javax.servlet.http.HttpServletrequest對象request,處理消息,最后構(gòu)造響應(yīng)消息javax.servlet.http.HttpServletResponse對象response,將結(jié)果數(shù)據(jù)輸出到界面。
在分析request對象中的數(shù)據(jù)時,常使用的方法是:
java.lang.StringgetParameter(java.lang.String
name)
java.util.EnumerationgetParameterNames()
java.lang.String[]getParameterValues(java.lang.String
name)在構(gòu)造響應(yīng)消息數(shù)據(jù)時,通常需要建立PrintWriter類型對象的輸出流,向該流中寫數(shù)據(jù)。在每個Servlet文件編輯完成之后,還需要在Servlet注冊管理文件web.xml中注冊每個Servlet,這樣Web應(yīng)用程序才能尋找到Servlet的代碼,進而調(diào)用其方法。
Servlet由容器調(diào)用執(zhí)行,容器根據(jù)web.xml配置文件來定位和裝載Servlet的代碼,進而生成該類的實例。
Servlet的實現(xiàn)步驟是:
(1)通過編輯web.xml注冊Servlet。主要修改web.xml中的<servlet>…</servlet>和<servlet-mapping>…</servlet-mapping>這兩個數(shù)據(jù)項中的內(nèi)容。
<servlet>數(shù)據(jù)項中有很多數(shù)據(jù)子項,一般最常用的是<servlet-name>和<servlet-class>數(shù)據(jù)項,這兩個數(shù)據(jù)項用來定義Servlet的名稱和Servlet的類代碼。<servlet-mapping>中的兩個數(shù)據(jù)子項為<servlet-name>和<url-pattern>,其中<servlet-name>數(shù)據(jù)項對應(yīng)<servlet><servlet-name>中的數(shù)據(jù)項,<url-pattern>為該Servlet被訪問時的URL定義。
當(dāng)使用Eclipse+Lomboz插件開發(fā)的時候,以上4個數(shù)據(jù)項的內(nèi)容會由模板自動生成,開發(fā)人員可以查看、編輯修改web.xml文件的內(nèi)容。
(2)客戶端去請求該Servlet,觸發(fā)容器調(diào)用Servlet。7.7ServletAPI常用接口和類
JavaServletAPI是一組接口和類,以包的形式提供給開發(fā)人員使用。JavaServletAPI提供了兩個包:
(1)?javax.servlet包。該包實現(xiàn)了未定義通信協(xié)議的Servlet接口和類。
(2)?javax.servlet.http包。該包實現(xiàn)了使用HTTP協(xié)議通信的Servlet接口和類。
API的詳細(xì)說明文檔如表7.2所示。讀者可以到Sun公司網(wǎng)站查閱和下載,網(wǎng)址為/products/servlet/2.5/docs/servlet-2_5-mr2/index.html。
ServletAPI接口和類一覽表如表7.3所示。最常用的接口是Servlet、ServletConfig、ServletRequest、ServletResponse、HttpServletRequest、HttpServletResponse、HttpSession,其中HttpServletRequest繼承ServletRequest,HttpServletResponse繼承ServletResponse。最常用的類是GenericServlet、HttpServlet以及異常類ServletException、UnavailableException。這些常用接口和類提供了創(chuàng)建Servlet、創(chuàng)建請求消息和響應(yīng)消息對象的類型。
Servlet接口的常用方法如下:
voiddestroy()
ServletConfiggetServletConfig()
java.lang.StringgetServletInfo()
voidinit(ServletConfigconfig)
voidservice(ServletRequsetreq,ServletResponseres)
關(guān)于方法的說明請參考相關(guān)文檔。
GenericServlet類是一個抽象類,它實現(xiàn)了Servlet接口,具有Servlet的所有方法。該類的聲明為:
publicabstractclassGenericServlet
extendsjava.lang.Object
implementsServlet,ServletConfig,java.io.Serializable
該類最重要的抽象方法是:
abstractvoidservice(ServletRequestreq,ServletResponseres)
繼承該類,必須實現(xiàn)該抽象方法。該抽象方法的兩個最重要的參數(shù)是req和res,實參由Servlet容器傳入,該方法負(fù)責(zé)修改實參對象數(shù)據(jù),通過res參數(shù)將處理結(jié)果傳回Servlet容器。
GenericServlet類的常用方法如下:
java.lang.String getInitParameter(java.lang.String
name)
java.util.Enumeration getInitParameterNames()
ServletConfig getServletConfig()
ServletContext getServletContext()
java.lang.String getServletInfo()
java.lang.String getServletName()
void init()
void init(ServletConfig
config)
abstract
void service(ServletRequest
req,ServletResponse
res)
HttpServlet類是設(shè)計和開發(fā)Servlet的人員直接繼承的最常用的頂層Servlet,該類繼承了GenericServlet類。如果Servlet類繼承了HttpServlet類,通常不直接實現(xiàn)service方法,因為該類已經(jīng)實現(xiàn)了service方法。在service方法中,根據(jù)不同的請求方式調(diào)用不同的方法,開發(fā)人員主要實現(xiàn)這些不同的請求處理方法。
請求處理方法和請求類型的對應(yīng)關(guān)系如表7.5所示。7.8Servlet應(yīng)用實例
【例7.7】實現(xiàn)一個向客戶端輸出簡單文本的Servlet。
分析:該Servlet不需要分析請求消息數(shù)據(jù),只需要構(gòu)造一個響應(yīng)消息數(shù)據(jù),向響應(yīng)消息輸出流寫入數(shù)據(jù),即可向客戶端輸出簡單文本。
操作:先建立代碼框架,然后實現(xiàn)方法,并在web.xml中注冊。
在Eclipse環(huán)境中的操作步驟如下:
(1)在前面建立的工程中添加Servlet,在工程視圖欄(ProjectExplorer)的目錄中選定存放Servlet源代碼的目錄(JavaResources:src),如圖7.20所示。圖7.20選定存放Servlet源代碼目錄的界面
(2)在該目錄下添加一個Servlet。選中JavaResources:src目錄,單擊鼠標(biāo)右鍵,彈出【New】菜單,在【New】菜單下選擇【Other…】,如圖7.21所示。圖7.21選擇添加Servlet
(3)在彈出的如圖7.22所示界面中添加Web項內(nèi)容,選擇【W(wǎng)eb】下的【Servlet】。圖7.22選擇Web的子項Servlet
(4)定制Servlet的名稱(Classname),定義或者選擇包名(在Javapackage項后的文本框中填寫),如圖7.23所示。填好后單擊【Next】按鈕。圖7.23定制Servlet名稱和存儲目錄
(5)在出現(xiàn)的如圖7.24所示界面中,定制web.xml中關(guān)于Servlet的配置項。如果不定義初始參數(shù),可以選擇默認(rèn),直接單擊【Next】按鈕。圖7.24定制web.xml中關(guān)于該Servlet的配置項
(6)定制Servlet的修飾符及方法。一般選擇不作任何改動。單擊【Finish】按鈕,完成Servlet源代碼模板的創(chuàng)建,如圖7.25所示。圖7.25定制Servlet的方法和修飾符
(7)單擊【Finish】按鈕后,生成Servlet源代碼框架模板和web.xml配置文件。在此文件中添加處理代碼,最后的結(jié)果如下:
①??HelloWorld.java文件:
packageservlet.web;
importjava.io.IOException;
importjava.io.PrintWriter;//輸出字符流,用來寫文本
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServletRequest;
//請求消息信息類,請求消息對象的類型,請求消息被作為doXXX的參數(shù)之一
importjavax.servlet.http.HttpServletResponse;
//響應(yīng)消息信息類,響應(yīng)消息對象的類型,響應(yīng)消息被作為doXXX的參數(shù)之一
publicclassHelloWorldextendsjavax.servlet.http.HttpServletimplementsjavax.servlet.Servlet{
publicHelloWorld(){
super();
}
//一般只修改此方法即可,此方法的功能是分析請求消息數(shù)據(jù),構(gòu)造響應(yīng)消息數(shù)據(jù)。
//對該題不需要分析請求消息數(shù)據(jù),只需要構(gòu)造響應(yīng)消息 protectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{
//該題只構(gòu)造響應(yīng)消息
response.setContentType("text/html;charset=gb2312");
//用來設(shè)置響應(yīng)消息的MIME類型:此處為text/html,客戶端將該響應(yīng)消息的
//正文(Body)數(shù)據(jù)按text/html格式解析。字符集編碼方式為gb2312
PrintWriterout=response.getWriter();
//創(chuàng)建字符輸出流對象,該字符輸出流可以把任意類型的數(shù)據(jù)按字符的方式寫入
//這樣響應(yīng)消息正文(Body)中的數(shù)據(jù)都按文本的方式對待。注意此處的out對象為
//PrintWriter類型的對象的引用名,可以用其他名稱代替,不是System.out中
//的System數(shù)據(jù)成員out,不要混淆。程序員只需要向響應(yīng)消息提供的這個流對象
//中寫數(shù)據(jù)即可,最后流中的數(shù)據(jù)是被服務(wù)器以響應(yīng)消息的形式發(fā)往客戶端瀏覽器,
//最終被瀏覽器軟件解析并顯示出來的
//以下用來構(gòu)造響應(yīng)消息正文,即向流對象中寫入一個簡單文本。這部分需要熟悉
//html各標(biāo)簽的含義及頁面的寫法
out.println(“<html>”);
out.println(“<head><title>HelloWorld</title></head>”);
out.println(“<bodybgcolor=\”FFFFFF\“>”);
out.println(“<center><H1>HelloWorld!
</H1></center>");
out.println("</body>"); out.println(“</html>”);
}
protectedvoiddoPost(HttpServletRequestrequest,
HttpServletResponseresponse)throwsServletException,
IOException{
}
}
②?web.xml文件:
<?xmlversion=“1.0”encoding=“UTF-8”?>
<web-appid=“WebApp_ID”version=“2.4”
xmlns="/xml/ns/j2ee"
xmlns:xsi=/2001/XMLSchema-instance
xsi:schemaLocation=“/xml/ns/j2ee
/xml/ns/j2ee/web-app_2_4.xsd”>
<display-name>WebExample</display-name>
<servlet>
<description></description>
<display-name>HelloWorld</display-name>
<servlet-name>HelloWorld</servlet-name>
<servlet-class>servlet.web.HelloWorld</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorld</servlet-name>
<url-pattern>/HelloWorld</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
該web.xml文件從<servlet>開始到</servlet-mapping>部分屬于Servlet配置項內(nèi)容。
(8)運行該Servlet。在Porject視圖欄中選中該Servlet,單擊鼠標(biāo)右鍵,選擇在服務(wù)器上運行,如圖7.26所示。圖7.26運行Servlet
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 海南思政黨史課題申報書
- 材料課題申報書范文
- 辦公隔斷合同范例
- 假肢定制合同范例
- 臨滄代理記賬合同范本
- 甘肅教學(xué)課題申報書
- 下水道整修合同范本
- 合法派遣合同范本
- 書畫掛牌合同范本
- 公司激勵股合同范本
- 急診醫(yī)院感染與控制課件
- 人教版 七年級英語下冊 UNIT 2 單元綜合測試卷(2025年春)
- 2024年“新能源汽車裝調(diào)工”技能及理論知識考試題與答案
- 【地理】非洲-位置與范圍 高原為主的地形課件-2024-2025學(xué)年湘教版(2024)七下
- 搶救車的管理
- GB/T 44927-2024知識管理體系要求
- GB/T 17350-2024專用汽車和專用掛車分類、名稱及型號編制方法
- 2024年07月山東省泰山財產(chǎn)保險股份有限公司2024年夏季校園招考29名工作人員筆試歷年參考題庫附帶答案詳解
- 臨床護理死亡病例討論
- 2025年廣東韶關(guān)城投集團招聘筆試參考題庫含答案解析
- 醫(yī)療器械生產(chǎn)企業(yè)并購合同
評論
0/150
提交評論