jsp_servlet【基礎】


  綜述:Java Servlet是JSP技術的基礎,而且大型的Web應用程序的開發需要Java Servlet和JSP配合才能完成。現在許多Web服務器都支持Servlet,即使不直接支持Servlet的Web服務器,也可以通過附件的應用服務器和模塊來支持Servlet,這得益於Java的跨平臺特性。另外,由於Servlet內部以線程方式提供提供服務,不必對於每個請求都啓動一個進程,並且利用多線程機制可以同時爲多個請求服務,因此Servlet的效率非常高。

  但它並不是沒有缺點,和傳統的CGI、ISAPI、NSAPI方式相同,Java Servlet也是利用輸出HTML語句來實現動態網頁的,如果用它來開發整個網站,動態部分和靜態頁面的整合過程將變得無法想象。這就是SUN還要推出JSP的原因。

  如何正確理解servlet?

  servlet的基本概念

  一、Servlet的結構


  在具體掌握servlet之前,須對Java語言有所瞭解。我們假設讀者已經具備一定的Java基礎。在Servlet API中最重要的是Servlet接口(interface),所有的servlets都必須實現該接口,途徑有很多:一是直接實現該接口,二是通過擴展類(class)來實現,如 HttpServlet。 這個Servlet接口提供了servlet與客戶端聯繫的方法。Servlet編寫者可以在他們開發 servlet程序時提供更多一些或所有的這樣方法。

  當一個servlet接收來自客戶端的調用請求, 它接收兩個對象:一個是ServletRequest,另外一個是ServletResponse。這個ServletRequest類概括從客戶端到服務器之間的聯繫,而 ServletResponse類概括從servlet返回客戶端的聯繫。

  ServletRequest接口可以獲取到這樣一些信息,如由客戶端傳送的闡述名稱,客戶端正在使用的協議,產生請求並且接收請求的服務器遠端主機名。它也提供獲取數據流的ServletInputStream, 這些數據是客戶端引用中使用HTTP POST 和 PUT 方法遞交的。一個ServletRequest的子類可以讓servlet獲取更多的協議特性數據。例如:HttpServletRequest 包含獲取 HTTP-specific頭部信息的方法。

  ServletResponse接口給出相應客戶端的servlet方法。它允許servlet設置內容長度和迴應的mime類型,並且提供輸出流ServletOutputStream,通過編寫者可以發回相應的數據。ServletResponse子類可以給出更多protocol-specific內容的信息。 例如:HttpServletResponse 包含允許servlet 操作HTTP-specific頭部信息的方法。

  上面有關類和接口的描述,構成了一個基本的Servlet框架。HTTP servlets有一些附加的可以提供session-tracking capabilities的方法。servlet編寫者可以利用這些API,在有他人操作時維護servlet與客戶端之間的狀態。

 


 


  二、Servlet的接口

  我們編寫的Servlet ,一般從Javax包的HttpServlet類擴展而來,在HttpServlet中加入了一些附加的方法,這些方法可以被協助處理HTTP 基本請求的HttpServlet類中的方法service自動地調用。這些方法有:

  · doGet 用來處理HTTP的GET請求。

  這個GET操作僅僅允許客戶從HTTP server上取得(GET)資源。重載此方法的用戶自動允許支持方法HEAD。這個GET操作被認爲是安全的,沒有任何的負面影響,對用戶來說是很可靠的。比如,大多數的正規查詢都沒有副作用。打算改變存儲數據的請求必須用其他的HTTP方法。這些方法也必須是個安全的操作。方法doGet的缺省實現將返回一個HTTP的BAD_REQUEST錯誤。

  方法doGet的格式:
  protected void doGet(HttpServletResquest request, HttpServletResponse response)
  throws ServletException,IOException;

  · doPost 用來處理HTTP的POST請求。

  這個POST操作包含了在必須通過此servlet執行的請求中的數據。由於它不能立即取得資源,故對於那些涉及到安全性的用戶來說,通過POST請求操作會有一些副作用。

  方法doPost的缺省實現將返回一個HTTP的BAD_REQUEST錯誤。當編寫servlet時,爲了支持POST操作必須在子類HttpServlet中實現(implement)此方法。

  此方法的格式:

  protected void doPost(HttpServletResquest request, HttpServletResponse response)
  throws ServletException,IOException;

  · doPut用來處理HTTP的PUT請求。

  此PUT操作模擬通過FTP發送一個文件。對於那些涉及到安全性的用戶來說,通過PUT請求操作也會有一些副作用。

  此方法的格式:

  protected void doPut(HttpServletResquest request,HttpServletResponse response)
  throws ServletException,IOException;

  · doDelete用來處理HTTP的DELETE請求。

  此操作允許客戶端請求一個從server移出的URL。對於那些涉及到安全性的用戶來說,通過DELETE請求操作會有一些副作用。

  方法doDelete的缺省實現將返回一個HTTP的BAD_REQUEST錯誤。當編寫servlet時,爲了支持DELETE操作,必須在子類HttpServlet中實現(implement)此方法。

  此方法的格式:

  protected void doDelete (HttpServletResquest request, HttpServletResponse response)
  throws ServletException,IOException;

  · doHead 用來處理HTTP的HEAD請求。

  缺省地,它會在無條件的GET方法執行時運行,但是不返回任何數據到客戶端。只返回包含內容信息的長度的header。由於用到GET操作,此方法應該是很安全的(沒有副作用)也是可重複使用的。此方法的缺省實現(implement)自動地處理了HTTPDE的HEAD操作並且不需要通過一個子類實現(implement)。

  此方法的格式:

  protected void doHead (HttpServletResquest request,HttpServletResponse response)
  throws ServletException,IOException;

  · doOptions用來處理HTTP的OPTIONS請求。

  此操作自動地決定支持什麼HTTP方法。比如說,如果讀者創建HttpServlet的子類並重載方法doGet,然後方法doOptions會返回下面的header:

  Allow:GET,HEAD,TRACE,OPTIONS

  一般不需要重載方法doOptions。

  此方法的格式:

  protected void doOptions (HttpServletResquest request, HttpServletResponse response)
throws ServletException,IOException;

  · doTrace用來處理HTTP的TRACE請求。

  此方法的缺省實現產生一個包含所有在trace請求中的header的信息的應答(response)。在開發servlet時,多數情況下需要重載此方法。

  此方法的格式:

  protected void doTrace (HttpServletResquest request, HttpServletResponse response)
  throws ServletException,IOException;

  在開發以HTTP爲基礎的servlet中,Servlet開發者關心方法doGet和方法doPost即可。


 


3.Servlet結束時期
  Servlets一直運行到他們被服務器卸載。在結束的時候需要收回在init()方法中使用的資源,在Servlet中是通過destory()方法來實現的。
public void destroy()
{
//回收在init()中啓用的資源,如關閉數據庫的連接等。
}

  JSP與servlet之間是怎樣的關係?

  JSP主要關注於HTML(或者XML)與Java代碼的結合,以及加入其中的JSP標記。如果一個支持JSP的服務器遇到一個JSP頁面,它首先查看該頁面是否被編譯成爲一個servlet。由此可見,JSP被編譯成servlet,即被轉變爲純Java,然後被裝載入服務器執行。當然,這一過程,根據不同的JSP引擎而略有不同。

  JSP和servlet在應用上有什麼區別

  簡單的說,SUN首先發展出SERVLET,其功能比較強勁,體系設計也很先進,只是,它輸出HTML語句還是採用了老的CGI方式,是一句一句輸出,所以,編寫和修改HTML非常不方便。

  後來SUN推出了類似於ASP的嵌套型的JSP,把JSP TAG嵌套到HTML語句中,這樣,就大大簡化和方便了網頁的設計和修改。新型的網絡語言如ASP,PHP都是嵌套型的。

  從網絡三層結構的角度看,一個網絡項目最少分三層:data layer,business layer,,presentation layer。當然也可以更復雜。

  SERVLET用來寫business layer是很強大的,但是對於寫presentation layer就很不方便。JSP則主要是爲了方便寫presentation layer而設計的。當然也可以寫business layer。寫慣了ASP,PHP,CGI的朋友,經常會不自覺的把presentation layer和business layer混在一起。比如把數據庫處理信息放到JSP中,其實,它應該放在business layer中。

  根據SUN自己的推薦,JSP中應該僅僅存放與presentation layer有關的部分,也就是說,只放輸出HTML網頁的部份。而所有的數據計算、數據分析、數據庫聯結處理,統統是屬於business layer,應該放在JAVA BEANS中。通過JSP調用JAVA BEANS,實現兩層的整合。

  實際上,微軟前不久推出的DNA技術,簡單說,就是ASP+COM/DCOM技術。與JSP+BEANS完全類似,所有的presentation layer由ASP完成,所有的business layer由COM/DCOM完成。通過調用,實現整合。

  爲什麼要採用這些組件技術呢?因爲單純的ASP/JSP語言是非常低效率執行的,如果出現大量用戶點擊,純SCRIPT語言很快就到達了他的功能上限,而組件技術就能大幅度提高功能上限,加快執行速度。

  另外一方面,純SCRIPT語言將presentation layer和business layer混在一起,造成修改不方便,並且代碼不能重複利用。如果想修改一個地方,經常會牽涉到十幾頁CODE,採用組件技術就只改組件就可以了。

  綜上所述,SERVLET是一個不完善的產品,寫business layer很好,寫presentation layer就很遜色許多了,並且兩層混雜。所以,推出JSP+BAEN,用JSP寫presentation layer,用BAEN寫business layer。SUN自己的意思也是將來用JSP替代SERVLET。

  所以,學了JSP,不會用JAVA BEAN並進行整合,等於沒學。

 


 

如何調用servlet?

  要調用Servlet或Web應用程序,請使用下列任一種方法:由URL調用、在<FORM>標記中調用、在<SERVLET>標記中調用、在ASP文件中調用。

  1.由URL調用 Servlet

  這裏有兩種用Servlet的URL從瀏覽器中調用該Servlet的方法:

  (1)指定 Servlet 名稱:當用 WebSphere應用服務器管理器來將一個Servlet實例添加(註冊)到服務器配置中時,必須指定"Servlet 名稱"參數的值。例如,可以指定將hi作爲HelloWorldServlet的Servlet名稱。要調用該Servlet,需打開http://your.server.name/servlet/hi。也可以指定Servlet和類使用同一名稱(HelloWorldServlet)。在這種情況下,將由http://your.server.name/servlet/ HelloWorldServlet 來調用Servlet的實例。

  (2)指定 Servlet 別名:用 WebSphere應用服務器 管理器來配置Servlet別名,該別名是用於調用Servlet的快捷URL。快捷URL中不包括Servlet名稱。

  2.在<FORM>標記中指定Servlet

  可以在<FORM>標記中調用Servlet。HTM 格式使用戶能在Web頁面(即從瀏覽器)上輸入數據,並向Servlet提交數據。例如:

 

<FORM METHOD="GET" ACTION="/servlet/myservlet">
<OL>
<INPUT TYPE="radio" NAME="broadcast" VALUE="am">AM<BR>
<INPUT TYPE="radio" NAME="broadcast" VALUE="fm">FM<BR>
</OL>
(用於放置文本輸入區域的標記、按鈕和其它的提示符。)
</FORM>

  ACTION特性表明了用於調用Servlet的URL。關於METHOD的特性,如果用戶輸入的信息是通過GET方法向Servlet提交的,則 Servlet 必須優先使用doGet()方法。反之,如果用戶輸入的信息是通過POST方法向Servlet提交的,則 Servlet 必須優先使用doPost()方法。使用GET方法時,用戶提供的信息是查詢字符串表示的URL編碼。無需對URL進行編碼,因爲這是由表單完成的。然後URL編碼的查詢字符串被附加到Servlet URL中,則整個URL提交完成。URL編碼的查詢字符串將根據用戶同可視部件之間的交互操作,將用戶所選的值同可視部件的名稱進行配對。例如,考慮前面的HTML代碼段將用於顯示按鈕(標記爲AM和FM),如果用戶選擇FM按鈕,則查詢字符串將包含name=value的配對操作爲broadcast=fm。因爲在這種情況下,Servlet將響應HTTP請求,因此Servlet應基於HttpServlet類。Servlet 應根據提交給它的查詢字符串中的用戶信息使用的 GET 或 POST 方法,而相應地使用 doGet() 或 doPost() 方法。

  3.在<SERVLET>標記中指定Servlet

  當使用<SERVLET>標記來調用Servlet時,如同使用<FORM>標記一樣,無需創建一個完整的HTML頁面。作爲替代,Servlet的輸出僅是HTML頁面的一部分,且被動態嵌入到原始HTML頁面中的其它靜態文本中。所有這些都發生在服務器上,且發送給用戶的僅是結果HTML頁面。建議在Java服務器頁面(JSP)文件中使用 <SERVLET> 標記。

  原始HTML頁面中包含<SERVLET> 和</SERVLET> 標記。Servlet將在這兩個標記中被調用,且Servlet的響應將覆蓋這兩個標記間的所有東西和標記本身。如果用戶的瀏覽器可以看到HTML源文件,則用戶將看不到<SERVLET>和</SERVLET>標記。要在 Domino Go Webserver 上使用該方法,請啓用服務器上的服務器端包括功能。部分啓用過程將會涉及到添加特殊文件類型SHTML。當Web服務器接收到一個擴展名爲SHTML的Web頁面請求時,它將搜索 <SERVLET> 和 </SERVLET> 標記。對於所有支持的Web服務器,WebSphere應用服務器將處理SERVLET標記間的所有信息。下列 HTML 代碼段顯示瞭如何使用該技術。

<SERVLET NAME="myservlet" CODE="myservlet.class" CODEBASE="url" initparm1= "value">
<PARAM NAME="parm1" VALUE="value">
</SERVLET>

  使用NAME和CODE屬性帶來了使用上的靈活性。可以只使用其中一個屬性,也可以同時使用兩個屬性。NAME屬性指定了Servlet的名稱(使用WebSphere應用服務器管理器配置的),或不帶.class擴展名的Servlet類名。CODE屬性指定了Servlet類名。使用WebSphere應用服務器時,建議指定NAME和CODE,或當NAME指定了Servlet名稱時,僅指定NAME。如果僅指定了CODE,則會創建一個NAME=CODE的Servlet實例。裝入的Servlet將假設Servlet名稱與NAME屬性中指定的名稱匹配。然後,其它SHTML文件可以成功地使用NAME屬性來指定Servlet的名稱,並調用已裝入的Servlet。NAME的值可以直接在要調用Servlet的URL中使用。如果NAME和CODE都存在,且NAME指定了一個現有Servlet,則通常使用NAME中指定的Servlet。由於Servlet創建了部分HTML文件,所以當創建Servlet時,將可能會使用HttpServlet的一個子類,並優先使用doGet()方法(因爲GET方法是提供信息給Servlet的缺省方法)。另一個選項是優先使用service()方法。另外,CODEBASE是可選的,它指定了裝入Servlet的遠程系統的URL。請使用WebSphere應用服務器管理器來從JAR文件配置遠程Servlet裝入系統。

  在上述的標記示例中,initparm1是初始化參數名,value是該參數的值。可以指定多個"名稱-值"對的集合。利用ServletConfig對象(被傳遞到Servlet的init()方法中)的getInitParameterNames()和getInitParameter()方法來查找參數名和參數值的字符串數組。在示例中,parm1是參數名,並在初始化Servlet後被才被設置某個值。因爲只能通過使用"請求"對象的方法來使用以<PARAM>標記設置的參數,所以服務器必須調用Servlet service()方法,以從用戶處傳遞請求。要獲得有關用戶的請求信息,請使用getParameterNames()、getParameter()和getParameterValues()方法。

  初始化參數是持續的。假設一臺客戶機通過調用一個包含某些初始化參數的SHTML文件來調用Servlet。並假設第二臺客戶機通過調用第二個SHTML文件來調用同一個Servlet,且該SHTML中未指定任何初始化參數。那麼第一次調用Servlet時所設置的初始化參數將一直可用,並且通過所有其它SHTML文件而調用的所有後繼Servlet都不會更改該參數。直到Servlet調用了destroy()方法後,才能重新設置初始化參數。例如,如果另一個SHTML文件指定了另一個不同的初始化參數值,雖然已此時已裝入了Servlet,但該值仍將被忽略。

  4.在ASP文件中調用Servlet
  如果在Microsoft Internet Information Server(IIS)上有遺留的ASP文件,並且無法將ASP文件移植成JSP文件時,可用ASP文件來調用Servlet。在WebSphere應用服務器中的ASP支持包括一個用於嵌入Servlet的ActiveX控制,下面介紹ActiveX控制AspToServlet的方法和屬性。

  該方法說明如下:

  (1)String ExecServletToString(String servletName);執行ServletName,並將其輸出返回到一個字符串中。

  (2)ExecServlet(String servletName);執行ServletName,並將其輸出直接發送至 HTML 頁面。

  (3)String VarValue(String varName);獲得一預置變量值(其它格式)。

  (4)VarValue(String varName, String newVal);設置變量值。變量佔據的總大小應小於0.5個千字節(Kbyte)。且僅對配置文件使用這些變量。

  其屬性如下:

  = Boolean WriteHeaders;若該屬性爲真,則Servlet提供的標題被寫入用戶處。缺省值爲假。
  = Boolean OnTest;若該屬性爲真,服務器會將消息記錄到生成的HTML頁面中。缺省值爲假。
  下列ASP 腳本示例是以Microsoft Visual Basic Scripting(VBScript)書寫的。

<%
' Small sample asp file to show the capabilities of the servlets and the ASP GateWay ...
%>
<H1> Starting the ASP->Java Servlet demo</H1>
<%
' Create a Servlet gateway object and initialize it ...
Set Javaasp = Server.CreateObject("AspToServlet.AspToServlet")
' Setting these properties is only for the sake of demo.
' These are the default values ...
Javaasp.OnTest = False
Javaasp.WriteHeaders = False
' Add several variables ...
Javaasp.VarValue("gal") = "lag"
Javaasp.VarValue("pico")= "ocip"
Javaasp.VarValue("tal") = "lat"
Javaasp.VarValue("paz") = "zap"
Javaasp.VarValue("variable name with spaces") = "variable value with spaces"
%>
<BR>
Lets check the variables
<%
Response.Write("variable gal = ")
Response.Write(Javaasp.VarValue("gal"))
%>
<BR>
<%
Response.Write("variable pico = " & Javaasp.VarValue("pico"))
%>

<BR>
<HR>
<%
galout = Javaasp.ExecServletToString("SnoopServlet")
If Javaasp.WriteHeaders = True Then
%>
Headers were written <%
Else
%>
Headers were not written <%
End If
Response.Write(galout)
%>
<H1> The End ...</H1>

 


 

  如何設置servlet類的路徑?

  因爲各個服務器對訪問servlet的策略不盡相同,所以在設置servlet類路徑時應該視情況而定。
對於開發中的servlet,只需確認包含Javax.servlet 的JAR文檔在您的類路徑中,並運用如Javac的普通開發工具。
  對於 JSDK:JSDK_HOME/servlet.jar
  JSDK_HOME/server.jar
  對於 Tomcat:TOMCAT_HOME/lib/servlet.jar
  對於運行中的servlet,必須爲servlet引擎設置類路徑,這根據不同的引擎,有不同的配置,如哪些庫和目錄應包括,哪些不應包括。注:對於servlet的動態加載引擎如JRun, Apache Jserv, Tomcat,包含servlet類文件的目錄不應在類路徑中,而應在config文件中配置。否則,servlet可以運行,但不能被動態再加載。

  Servlet 2.2 規範認爲以下應被容器自動包括,因此您不必把他們手工添加到類路徑。

  · 所有的類應放在 webapp/WEB-INF/classes目錄下

  · 所有JAR文件放在webapp/WEB-INF/lib 目錄下

  · 對webapps的應用體現在文檔系統中,對已打包進JAR文檔的webapps的應用應放入容器的webapps目錄。(例如,TOMCAT_HOME/webapps/myapp.jar)

  另外,由Gene McKenna([email protected])撰寫的"The Complete CLASSPATH Guide for Servlets"詳細敘述瞭如何爲JavaWebServer和Jrun設置類路徑。

  如何實現servlet與applet的通信?

  這個例子將向讀者展示服務器端程序(Servlet)和小應用程序(Applet)之間是如何完成通信活動的。它由三個文件組成,一個是sendApplet.Java文件,用於實現Applet,一個是receiveservlet.Java,用於實現servlet,還有一個是add-servlet.html,用於調用Applet。

  在sendApplet.Java文件中,最重要的要屬init()函數和Send()函數,其中init()函數用來生成整個Applet的用戶操作界面,包括消息文本框、發送按鈕等等。而消息的發送過程則由Send()函數來完成。請仔細閱讀下面的代碼:

 

private void Send()
{
message = sendText.getText();
//清除用戶的輸入信息
sendText.setText("");
showStatus("Message send!");
//把輸入的字符串轉化爲 x-www-form-urlencoded 格式
String queryString = "/servlet/ReceiveServlet?message=" + URLEncoder.encode ( message ) ;
p("Attempting to send:"+message);

//建立與Servlet的聯接,並取得Servelt的輸出信息
try {
connect = (new URL(chatURL,queryString)).openConnection();
showStatus("open connection!");
//下次連接不用Cache
connect.setDefaultUseCaches(false);
//這次連接也不用Cache
connect.setUseCaches(false);
//打開淂流用於讀數據
connect.setDoInput(true);
//不能用於寫數據
connect.setDoOutput(false);
//服務器與客戶的真正連接
connect.connect();
p("Made connection to "+connect);
showStatus("Open Stream!");
DataInputStream in = new DataInputStream(connect.getInputStream());
showStatus("reading!");
message = in.readLine();
while (message! = null)
{
//在消息文本框顯示Servlet生成的信息
messageText.setText(message);
message = in.readLine();
}
}catch(MalformedURLException e2)
{
System.err.println("MalformedURLException!");
e2.printStackTrace(System.err);
showStatus("MalformedURLException!");
}catch(IOException e1)
{
System.err.println("IOException!");
e2.printStackTrace(System.err);
showStatus("IOException");
}
}

  整個Applet的詳細代碼請見sendApplet.Java。

  當Applet與Servlet建立連接後,工作就可以交給Servlet了,由它來解析客戶端的請求,獲得參數message的值,然後將適當信息返回給客戶端,並由Applet進行顯示。完成該功能的是receiveservlet.Java中的service()函數:

public void service (HttpServletRequest req,HttpServletResponse res)
throws ServletException,IOException
{
res.setContentType("text/plain");
ServletOutputStream out = res.getOutputStream();
out.print("receive user message:");
out.print(req.getParameter("message"));
}

  該Servlet的詳細源代碼請見receiveservlet.Java。
  最後一個文件是add-servlet.html,它用來調用Applet:

<html>
<head>
<title>sendApplet</title>
</head>
<body>
<hr>
<applet code=sendApplet width=400 height=300 ></applet>
<hr>
</body>
</html>

  是不是很簡單?

 


 

  如何應用應用Servlet進行圖象處理?

  我們在處理數據時,有時希望能用圖象直觀的表述,在這裏有一個巧方法,能方便快捷的實現一些簡單的圖形(不能稱之圖象),比如條形圖,我們不必去用Java來生成並顯示圖象,(Java生成圖象很慢),我們可以這樣來作,先用作圖工具作一個很小的你需要的圖片,再根據你所處理的數據量來實時的加長它,就可以得到所要表述的圖例。比如我們在數據庫中得到了一組數據,我們從中找出最大的那一個,按比列設定其<img>標籤的長度,其它的數據圖形則可與它相比,得到<img>的長度,這樣,一個簡簡單單的條形圖就出來。但有時一些簡單的圖形已經不能解決我們實際遇到的情況,比如曲線圖就不能用這種方法,這時我們需要生成Java圖象,也許大家都用過applet這樣的程序吧,若訪問量不大,而實時性又很特殊時(比如股票系統),必須這樣用它。但事實上,我們web程序大多有前後臺之分,前臺瀏覽,後臺維護。這樣我們可以在後臺用servlet實時動態定時地生成圖象文件,而前臺只是查看靜態圖片,這比你用applet來動態產生圖象的速度快了不知多少倍,因爲applet來動態產生圖象,有兩個地方很費時,一是數據庫查詢時間,二是applet本身生成圖象就很慢。下面以一個簡單的例子來說明一下怎樣生成並寫入圖象文件,本例注重的是怎樣寫入圖象文件,相信寫過applet的讀者會生成更加漂亮的圖象。

 

package test;
import Javax.servlet.*;
import Javax.servlet.http.*;
import Java.io.*;
import Java.util.*;
import Java.awt.image.BufferedImage;
import com.sun.image.codec.jpeg.*;
import Java.awt.image.*;
import Java.awt.*;

public class Servlet2 extends HttpServlet
{
public void init(ServletConfig config) throws ServletException {
super.init(config);
}

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
String sFileName = "e:/temp/name.jpg";
try{
FileOutputStream fos = new
FileOutputStream(sFileName);
BufferedImage myImage = new BufferedImage(225, 225,BufferedImage. TYPE_INT_RGB);
Graphics g = myImage.getGraphics();
g.setColor(Color.white);
g.fillRect(0,0,225,225);
g.setColor(Color.black);
g.drawString("Finance Balance Summary", 40, 15);
g.drawString("Primary", 90, 30);
g.setColor(Color.darkGray);
g.fillRect(15,193,7,7);
g.setColor(Color.black);
g.drawString("% Operating", 25, 200);
g.setColor(Color.yellow);
g.fillRect(130,193,7,7);
g.setColor(Color.black);
g.drawString("% Term", 140, 200);
g.setColor(Color.lightGray);
g.fillRect(15,213,7,7);
g.setColor(Color.black);
g.drawString("% Mortgage", 25, 220);
g.setColor(Color.green);
g.fillRect(130,213,7,7);
g.setColor(Color.black);
g.drawString("% Lease", 140, 220);
JPEGImageEncoder jpg = JPEGCodec.createJPEGEncoder(fos);
jpg.encode(myImage);
}catch (Exception e)
{
String exceptionThrown = e.toString();
String sourceOfException = " Method";
System.out.println("Origional Exception Thrown: " +exceptionThrown + '/r' + '/n');
System.out.println("Origional SourceOfException: " + sourceOfException +'/r' + '/n');
} // CatchStatementEnd
}
}

  如何通過Servlet調用JavaBean輸出結果集

  以此我們通過一個例子進行說明,該例演示瞭如何通過Servlet調用JavaBean輸出結果集,並打印的方法,共由兩個文件組成,一個是JavaBean,用於實現對數據庫的訪問,並獲得結果集;另一個是Servlet,主要負責JavaBean的調用,並將結果集發送到客戶端。

  在JavaBean中,我們將訪問DB2樣例數據庫(sample)中的STAFF表,至於如何實現對數據庫的訪問,讀者可以參考《JSP與JDBC》一章。此外,讀者可以通過修改部分參數,來實現對其他數據庫、表的訪問,達到舉一反三的效果。

  該JavaBean的核心是execute()函數:

public void execute()
{
try {
//裝載JDBC驅動程序
Class.forName("COM.ibm.db2.jdbc.app.DB2Driver").newInstance();
//建立對數據庫的連接
conn = DriverManager.getConnection("jdbc:db2:sample", "db2admin", "db2admin");
stmt = conn.createStatement();
String sql = "SELECT * FROM STAFF WHERE DEPT=20";
//執行查詢語句,返回結果集
ResultSet rs = stmt.executeQuery(sql);
setResult(rs);
} catch (SQLException e) {
} catch (IllegalAccessException e2) {
} catch (ClassNotFoundException e3) {
} catch (InstantiationException e4) {}
}

  JavaBean的具體源代碼請見Tbean.Java。

  知道數據是如何獲取之後,下面我們來看一下Servlet是如何來調用上述JavaBean的。

  同樣看service()方法即可(詳細源代碼請見Tservlet.Java):

public void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
try {
//實例化JavaBean
Demo.TBean Javabean = new Demo.TBean();
Javabean.execute();
ResultSet rs1 = Javabean.getResult();
PrintWriter out = res.getWriter();
res.setContentType("text/html");
out.println("<table border=1>"=;
out.println("<H1>Hello World</H1>"=;
out.println("<td>ID</td><td>NAME</td><td>DEPT</td><td>JOB</td><td>YEARS</td><td>SALARY</td><td>COMM</td>"=;
while (rs1.next())
{
out.println("<tr>"=;
for (int i = 1; i <= 7; i++=
out.println("<td>" + rs1.getString(i) + "</td>"=;
out.println("</tr>"=;
}
out.println("</table>"=;
Javabean.Sqlclose();
} catch (SQLException e) {}
}
//運行:在VisualAge for Java 的IBM Websphere Test Environment的環境下:
//http://localhost:8080/servlet/Demo.TServlet

Hello Print ID NAME DEPT JOB YEARS SALARY COMM
10 Sanders 20 Mgr 7 18357.50 null
20 Pernal 20 Sales 8 18171.25 612.45
80 James 20 Clerk null 13504.60 128.20
190 Sneider 20 Clerk 8 14252.75 126.50

 


 

  如何用Servlet來中斷涉及的多線程

  現在我們已經知道,當服務器要卸載一個Servlet時,它會在所有的service都已經完成後再調用destroy()方法。如果程序的操作運行需要很長時間,destroy()被調用時就可能還有其他線程在運行。Servlet程序員必須保證所有的線程都已經完成。

  長時間運行響應客戶端請求的那些Servlet應當保留當前有多少方法在運行的記錄。它的long-running方法應當週期性地輪流詢問以確保它們能夠繼續運行下去。如果Servlet被destroy()方法調用,那麼這個long-running方法必須停止工作或清除。

  舉例,變量serviceCounter用來統計有多少service方法在運行,變量shuttingDown顯示這個Servlet是否被destroy。每個變量有它自己的獲取方法:

public ShutdownExample extends HttpServlet
{
private int serviceCounter = 0;
private Boolean shuttingDown;

//serviceCounter
protected synchronized void enteringServiceMethod()
{
serviceCounter++;
}
protected synchronized void leavingServiceMethod()
{
serviceCounter--;
}
protected synchronized int numServices()
{
return serviceCounter;
}
//shuttingDown
protected setShuttingDown(Boolean flag)
{
shuttingDown = flag;
}
protected Boolean isShuttingDown()
{
return shuttingDown;
}
這個service方法每次在它進入時要增加,而在它返回退出時要減少:
protected void service(HttpServletRequest req , HttpServletResponse resp)
throws ServletException IOException
{
enteringServiceMethod();
try{
super.service(req , resp);
}
finally {leavingServiceMethod();}
}

  destroy方法應當檢查serviceCounter,如果存在長時間方式運行的話,設置變量shuttingDown。這個變量將會讓那個正在處理請求的線程知道該結束了。destroy方法應當等待這幾個service方法完成,這樣就是一個清楚的關閉過程了。

public void destroy()
{
//檢查是否有線程在運行,如果有,告訴它們停止
if (numServices() > 0)
{
setShuttingDown(true);
}
//等待它們停止
while(numService() > 0)
{
try{
thisThread.sleep(interval);
}catch(InterruptedException e) {}
}
}
long-running方法如必要應當檢查這個變量,並且解釋它們的工作:
public void doPost(…)
{

for(i = 0; ((i < lotsOfStuffToDo) && !isShuttingDown()); i++)
{
try{
partOfLongRunningOperation(i);
}catch (InterruptedException e) {}
}
}

 


 

  附錄:深入理解servlet

  Servlets(Java小服務器程序)是Java中新增加的一個全新功能。一般來說,Servlets是由服務器端調用和執行的任何Java類,瀏覽器端運行的Java程序叫Applet,Web服務器端運行的Java程序叫做Servlet。自從有了Servlet後,Java的電子商務才真正的開始,之後的JSP又是在Servlet上有了更進一步的發展。瞭解Servlet的機制也就掌握到了到JSP的實現原理。
  在編寫Servlet的時候不需要關心一個Servlet是如何被裝載到服務器環境中,只需要調用Java Servlet API編程接口就行了。在使用Servlet API的時候,程序員完全可以不必瞭解內部運行方式,服務器頭、Cookies、會話都可以通過Servlet來處理。但當我們需要一些特殊功能的時候,就需要了解它的一些實現機制了。
  先從Servlet的生命週期說起。一般情況下,可以歸納爲幾點:
  1.裝載Servlets。這項操作一般是動態執行的。有些Server提供了相應的管理功能,可以在啓動的時候就裝載Servlet;
  2.Server創建一個Servlet實例;
  3.Server調用Servlet的init()方法;
  4.一個客戶端的請求到達Server;
  5.server創建一個請求對象;
  6.Server創建一個響應對象;
  7.Server激活Servlet的Service()方法,並傳遞請求和響應對象;
  8.service()方法獲得關於請求對象的的信息、處理請求、訪問其他資源、獲得需要的信息;
  9.service()方法使用響應對象的方法,將響應傳回Server,最終到達客戶端。Service()方法可能機或其他方法已處理請求,如doGet()或doPost()或程序員自己開發的方法;
  10.對於更多的客戶端請求,Server創建新的請求和響應對象,仍然激活此Servlet的service()方法,將這兩個對象作爲參數傳遞給它。如此重複以上的循環,但無需再次調用init()方法。也就是說,Servlet()只初始化一次。
  11.當Server不再需要Servlet時(一般是當Server關閉的時候),Server調用Servlets的destroy()方法。
  從前面我們還可以更進一步得出以下幾點:
  Servlet運行時是多線程的,在開發的時候由Servlet API實現,開發人員不需要做特別的處理。Servlet的init()、destroy()的使用就像是一個C++編寫的結構、析構函數,要注意前後的一致。比如在init()打開了一個數據庫,就要在destroy()中關閉。
  對一些底層的處理,如要控制消息響應的方式,我們就要重載server()方法,並重新編寫。
  爲便於解釋Servlet的實現機制,圍繞HttpServlet,用下圖描述Javax.Http包中常用到的類之間的關係(除掉了JSP部分),限於篇幅,沒有給出每個接口、類的屬性和方法。通過圖示,我們很容易開發一些較低層次的代碼。

  從下圖,非常明顯的可以看出HttpServlet是從GenericServlet類繼承下來的。而事實上,GenericServlet類主要是爲了起到三個接口的聯合(這個詞用得可能不科學):Servlet,ServletConfig,Serializable,以獲得系統的支持;除了init()外幾乎沒有實現方法操作。真正調用系統所支持的各項功能是在HttpServlet類中。

  就HttpServlet的service()方法,一般來說,當它接收到一個OPTIONS請求時,會調用doOptions()方法,當接收到一個TRACE請求時調用doTrace()。doOptions()缺省執行方式是自動決定什麼樣的HTTP被選擇並且返回哪個信息。相關源代碼如下:

public void service(ServletRequest req,ServletResponse res)
throws ServletException,IOException
{
HttpServletRequest request;
    HttpServletResponse response;
    try
    {
    request = (HttpServletRequest)req;
      response = (HttpServletResponse)res;
  } catch(ClassCastException _ex)
  {
      throw new ServletException("non-HTTP request or response");
}
service(request, response);
  }
  protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
  {
      String method = req.getMethod();
      if(method.equals("GET"))
      {
        long lastModified = getLastModified(req);
        if(lastModified == -1L)
        {
          doGet(req, resp);
        }
        else
        {
          long ifModifiedSince = req.getDateHeader("If-Modified-Since");
          if(ifModifiedSince < (lastModified / 1000L) * 1000L)
          {
            maybeSetLastModified(resp, lastModified); doGet(req, resp);
          }
          else
          {
            resp.setStatus(304);
          }
        }
      }
      else if(method.equals("HEAD"))
      {
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);
      }
      else
      if(method.equals("POST"))
        doPost(req, resp);
      else
      if(method.equals("PUT"))
        doPut(req, resp);
      else
      if(method.equals("DELETE"))
        doDelete(req, resp);
      else
      if(method.equals("OPTIONS"))
        doOptions(req, resp);
      else
      if(method.equals("TRACE")) {
        doTrace(req, resp);
      }
      else
      {
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object errArgs[] = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);
        resp.sendError(501, errMsg);
      }
    }
  protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
    {
      Method methods[] = getAllDeclaredMethods(getClass());
      boolean ALLOW_GET = false; boolean ALLOW_HEAD = false;
      boolean ALLOW_POST = false; boolean ALLOW_PUT = false;
      boolean ALLOW_DELETE = false; boolean ALLOW_TRACE = true;
      boolean ALLOW_OPTIONS = true;
      for(int i = 0; i < methods.length; i++)
      {
        Method m = methods[i];
        if(m.getName().equals("doGet"))
        {
          ALLOW_GET = true;
          ALLOW_HEAD = true;
        }
        if(m.getName().equals("doPost"))
        ALLOW_POST = true;
        if(m.getName().equals("doPut"))
        ALLOW_PUT = true;
        if(m.getName().equals("doDelete"))
        ALLOW_DELETE = true;
        }
        String allow = null;
        if(ALLOW_GET && allow == null)
          allow = "GET";
        if(ALLOW_HEAD)
          if(allow == null)
           allow = "HEAD";
          else allow = allow + ", HEAD";
        if(ALLOW_POST)
          if(allow == null)
          allow = "POST";
          else allow = allow + ", POST";
        if(ALLOW_PUT)
          if(allow == null)
          allow = "PUT";
          else allow = allow + ", PUT";
        if(ALLOW_DELETE)
          if(allow == null)
        allow = "DELETE";
        else allow = allow + ", DELETE";
      if(ALLOW_TRACE)
        if(allow == null)
        allow = "TRACE";
        else allow = allow + ", TRACE";
      if(ALLOW_OPTIONS)
        if(allow == null)
        allow = "OPTIONS";
        else allow = allow + ", OPTIONS";
      resp.setHeader("Allow", allow);
      }
    protected void doTrace(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException
    {
      String CRLF = "/r/n";
     String responseString = "TRACE " + req.getRequestURI() + " " + req.getProtocol();
      for(Enumeration reqHeaderEnum = req.getHeaderNames();
          reqHeaderEnum.hasMoreElements();)
    {
          String headerName = (String)reqHeaderEnum.nextElement();
     responseString = responseString + CRLF + headerName + ": " + req.getHeader(headerName);
   }
responseString = responseString + CRLF; int responseLength = responseString.length();
 resp.setContentType("message/http");
resp.setContentLength(responseLength);
 ServletOutputStream out = resp.getOutputStream();
out.print(responseString);
out.close();
}

  如果我們改寫了上面的代碼,將可以寫出一些針對性更強的特殊領域中的應用。限於篇幅,這裏將不在舉例。另外,在HttpServlet類(或子類)中,可以對一些系統底層支持的功能進行一些操作,比如Session、Cookie等。其中HttpSession接口在Sun公司提供的包中沒有找到它的類,應該是由Server引擎來實現、管理它的相關功能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章