第1部分)
一、 什麼是Struts
架構(Framework)是可重用的,半完成的應用程式,可以用來產生專門的定製程式。
您只要細心地研究真實的應用程式,就會發現程式大致上由兩類性質不同的組件組成,一類與程式要處理的具體事務密切相關,我們不妨把它們叫做業務組件;另一類是應用服務。比如說:一個稅務征管系統和一個圖書管理系統會在處理它們的業務方面存在很大的差異,這些直接處理業務的組件由於業務性質的不同不大可能在不同的系統中重用,而另一些組件如決定程式流向的控制、輸入的校正、錯誤處理及標籤庫等這些只與程式相關的組件在不同的系統中可以很好地得到重用。人們自然會想要是把這些在不同應用程式中有共性的一些東西抽取出來,做成一個半成品程式,這樣的半成品就是所謂的程式架構,再做一個新的東西時就不必白手起家,而是可以在這個基礎上開始搭建。實際上,有些大型軟體企業選擇自己搭建這樣的架構。但大多數中小型軟體企業或者其他組織,沒有條件自己建立架構。
Struts作為一個開放原代碼的應用程式框架,在最近幾年得到了飛速的發展,在JSP Web應用開發中應用得非常廣泛,有的文獻上說它已經成為JSP Web應用程式框架的事實上的標準。那麼,究竟什麼是Struts呢?
要回答這個問題還得從JSP Web應用的兩種基本的結構模式:Model 1和Model 2說起,為了給讀者一些實實在在的協助,并力圖讓學習曲線變得平坦一些,我想採用執行個體驅動的方法來逐步深入地回答有關問題,因為,學一門技術的最好方法莫過於在實踐中學習、在實踐中體會,逐步加深對其精神實質的理解和把握,而不是一上來就引入一大堆新概念讓大家覺得無所適從,或者死記硬背一大堆概念而面對一個真正的實際需求束手無策。正如,一個人即使在書本上學成了遊泳博士,只要他不下水,我想他也是不大可能真正會遊泳的。
Model 1結構1所示:
圖1
mode1 1是一個以JSP檔案為中心的模式,在這種模式中JSP頁面不僅負責表現邏輯,也負責控制邏輯。專業書籍上稱之為邏輯耦合在頁面中,這種處理方式,對一些規模很小的項目如:一個簡單的留言簿,也沒什麼太大的壞處,實際上,人們開始接觸一些對自己來說是新的東西的時候,比如,用JSP訪問資料庫時,往往喜歡別人能提供一個包含這一切的單個JSP頁面,因為這樣在一個頁面上他就可以把握全域,便於理解。但是,用Model 1模式開發大型時,程式流向由一些互相能夠感知的頁面決定,當頁面很多時要清楚地把握其流向將是很複雜的事情,當您修改一頁時可能會影響相關的很多頁面,大有牽一髮而動全身的感覺,使得程式的修改與維護變得異常困難;還有一個問題就是程式邏輯開發與頁面設計糾纏在一起,既不便於分工合作也不利於代碼的重用,這樣的程式其健壯性和延展性都不好。
Grady Booch等人在UML使用者指南一書中,強調建模的重要性時,打了一個製作狗窩、私人住宅、和大廈的形象比喻來說明人們處理不同規模的事物時應該採用的合理方法一樣,人們對不同規模的應用程式也應該採用不同的模式。
為了克服Model 1的缺陷,人們引入了Model 2,2所示:
圖2
它引入了"控制器"這個概念,控制器一般由Servlet來擔任,用戶端的請求不再直接送給一個處理商務邏輯的JSP頁面,而是送給這個控制器,再由控制器根據具體的請求調用不同的事務邏輯,並將處理結果返回到合適的頁面。因此,這個servlet控制器為應用程式提供了一個進行前-後端處理的中樞。一方面為輸入資料的驗證、身份認證、日誌及實現國際化編程提供了一個合適的切入點;另一方面也提供了將商務邏輯從JSP檔案剝離的可能。商務邏輯從JSP頁面分離後,JSP檔案蛻變成一個單純完成顯示任務的東西,這就是常說的View。而獨立出來的事務邏輯變成人們常說的Model,再加上控制器Control本身,就構成了MVC模式。實踐證明,MVC模式為大型程式的開發及維護提供了巨大的便利。
其實,MVC開始並不是為Web應用程式提出的模式,傳統的MVC要求M將其狀態變化通報給V,但由於Web瀏覽器工作在典型的拉模式而非推模式,很難做到這一點。因此有些人又將用於Web應用的MVC稱之為MVC2。正如上面所提到的MVC是一種模式,當然可以有各種不同的具體實現,包括您自己就可以實現一個體現MVC思想的程式架構,Struts就是一種具體實現MVC2的程式架構。它的大致結構三所示:
圖三
圖三基本勾勒出了一個基於Struts的應用程式的結構,從左至右,分別是其展示層(view)、控制層(controller)、和模型層(Model)。其展示層使用Struts標籤庫構建。來自客戶的所有需要通過架構的請求統一由叫ActionServlet的servlet接收(ActionServlet Struts已經為我們寫好了,只要您應用沒有什麼特別的要求,它基本上都能滿足您的要求),根據接收的請求參數和Struts配置(struts-config.XML)中ActionMapping,將請求送給合適的Action去處理,解決由誰做的問題,它們共同構成Struts的控制器。Action則是Struts應用中真正幹活的組件,開發人員一般都要在這裡耗費大量的時間,它解決的是做什麼的問題,它通過調用需要的業務組件(模型)來完成應用的業務,業務組件解決的是如何做的問題,並將執行的結果返回一個代表所需的描繪響應的JSP(或Action)的ActionForward對象給ActionServlet以將響應呈現給客戶。
過程四所示:
圖四
這裡要特別說明一下的是:就是Action這個類,上面已經說到了它是Struts中真正幹活的地方,也是值得我們高度關注的地方。可是,關於它到底是屬於控制層還是屬於模型層,存在兩種不同的意見,一種認為它屬於模型層,如:《JSP Web編程指南》;另一些則認為它屬於控制層如:《Programming Jakarta Struts》、《Mastering Jakarta Struts》和《Struts Kick Start》等認為它是控制器的一部分,還有其他一些書如《Struts in Action》也建議要避免將商務邏輯放在Action類中,也就是說,圖3中Action後的括弧中的內容應該從中移出,但實際中確有一些系統將比較簡單的且不打算重用的商務邏輯放在Action中,所以在圖中還是這樣表示。顯然,將業務對象從Action分離出來後有利於它的重用,同時也增強了應用程式的健壯性和設計的靈活性。因此,它實際上可以看作是Controller與Model的適配器,如果硬要把它歸於那一部分,筆者更傾向於後一種看法,即它是Controller的一部分,換句話說,它不應該包含過多的商務邏輯,而應該只是簡單地收集業務方法所需要的資料並傳遞給業務對象。實際上,它的主要職責是:
校正前提條件或者聲明
調用需要的商務邏輯方法
檢測或處理其他錯誤
路由控制到相關視圖上面這樣簡單的描述,初學者可能會感到有些難以接受,下面舉個比較具體的例子來進一步協助我們理解。如:假設,我們做的是個電子商務程式,現在程式要完成的操作任務是提交定單並返回定單號給客戶,這就是關於做什麼的問題,應該由Action類完成,但具體怎麼獲得資料庫連接,插入定單資料到資料庫表中,又怎麼從資料庫表中取得這個定單號(一般是自增資料列的資料),這一系列複雜的問題,這都是解決怎麼做的問題,則應該由一個(假設名為orderBo)業務對象即Model來完成。orderBo可能用一個返回整型值的名為submitOrder的方法來做這件事,Action則是先校正定單資料是否正確,以免常說的垃圾進垃圾出;如果正確則簡單地調用orderBo的submitOrder方法來得到定單號;它還要處理在調用過程中可能出現任何錯誤;最後根據不同的情況返回不同的結果給客戶。
二、為什麼要使用Struts架構
既然本文的開始就說了,自己可以建這種架構,為什麼要使用Struts呢?我想下面列舉的這些理由是顯而易見的:首先,它是建立在MVC這種公認的好的模式上的,Struts在M、V和C上都有涉及,但它主要是提供一個好的控制器和一套定製的標籤庫上,也就是說它的著力點在C和V上,因此,它天生就有MVC所帶來的一系列優點,如:結構層次分明,高可重用性,增加了程式的健壯性和延展性,便於開發與設計分工,提供集中統一的許可權控制、校正、國際化、日誌等等;其次,它是個開源項目得到了包括它的發明者Craig R.McClanahan在內的一些程式大師和高手持續而細心的呵護,並且經受了實戰的檢驗,使其功能越來越強大,體系也日臻完善;最後,是它對其他技術和架構顯示出很好的融合性。如,現在,它已經與tiles融為一體,可以展望,它很快就會與JSF等融會在一起。當然,和其他任何技術一樣,它也不是十全十美的,如:它對類和一些屬性、參數的命名顯得有些隨意,給使用帶來一些不便;還有如Action類execute方法的只能接收一個ActionForm參數等。但瑕不掩瑜,這些沒有影響它被廣泛使用。
三、Struts的安裝與基本配置
我們主要針對Struts1.1版本進行講解,這裡假定讀者已經配置好Java運行環境和相應的Web容器,本文例子所使用的是j2sdk和Tomcat4.1.27。下面,將採用類似於step by step的方式介紹其基礎部分。
安裝Struts
到http://jakarta.apache.org/ 下載Struts的安裝檔案,本文例子使用的是1.1版。
接下來您要進行如下幾個步驟來完成安裝:
1、解壓下載的安裝檔案到您的本地硬碟
2、產生一個新的Web應用,假設我們產生的應用程式的根目錄在/Webapps/mystruts目錄。在server.xml檔案中為該應用建立一個別名如/mystruts
3、從第1步解壓的檔案中拷貝下列jar檔案到/Webapps/mystruts/WEB-INF/lib目錄,主要檔案有如下一些。
struts.jarcommons-beanutils.jarcommons-collections.jarcommons-dbcp.jarcommons-digester.jarcommons-logging.jarcommons-pool.jarcommons-services.jarcommons-validator.jar
4、建立一個web.xml檔案,這是一個基於servlet的Web應用程式都需要的部署描述檔案,一個Struts Web應用,在本質上也是一個基於servlet的Web應用,它也不能例外。
Struts有兩個組件要在該檔案中進行配置,它們是:ActionServlet和標籤庫。下面是一個配置清單:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"><web-app> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <init-param> <param-name>debug</param-name> <param-value>2</param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <taglib> <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri> <taglib-location>/WEB-INF/struts-bean.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri> <taglib-location>/WEB-INF/struts-html.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri> <taglib-location>/WEB-INF/struts-logic.tld</taglib-location> </taglib></web-app>
上面我們在web.xml中完成了對servlet和標籤庫的基本配置,而更多的架構組件要在struts-config.xml中進行配置:
5、建立一個基本的struts-config.xml檔案,並把它放在/Webapps/mystruts/WEB-INF/目錄中,該檔案是基於Struts應用程式的配置描述檔案,它將MVC結構中的各組件結合在一起,開發的過程中會不斷對它進行充實和更改。在Struts1.0時,一個應用只能有一個這樣的檔案,給分工開發帶來了一些不便,在Struts1.1時,可以有多個這樣的檔案,將上述缺點克服了。需在該檔案中配置的組件有:data-sources
global-execptionsform-beans
global-forwards
action-mappings
controller
message-resources
plug-in
配置清單如下:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"><struts-config> <message-resources parameter="ApplicationResources" /></struts-config>
到此為止,我們已經具備了完成一個最簡單Struts應用的所需的各種組件。前面已經提到,在開發過程中我們會不斷充實和修改上面兩個配置描述檔案。下面我們將實際做一個非常簡單的應用程式來體驗一下Struts應用開發的真實過程,以期對其有一個真實的認識。在完成基礎部分的介紹後,筆者會給出一些在實際開發中經常用到而又讓初學者感到有些難度的執行個體。最後,會介紹Struts與其他架構的關係及結合它們產生應用程式的例子.
下面,我們就從一個最簡單的登入例子入手,以對Struts的主要部分有一些直觀而清晰的認識。這個例子功能非常簡單,假設有一個名為lhb的使用者,其密碼是awave,程式要完成的任務是,呈現一個登入介面給使用者,如果使用者輸入的名稱和密碼都正確返回一個歡迎頁面給使用者,否則,就返回登入頁面要求使用者重新登入並顯示相應的出錯資訊。這個例子在我們講述Struts的基礎部分時會反覆用到。之所以選用這個簡單的程式作為例子是因為不想讓過於複雜的商務邏輯來沖淡我們的主題。
因為Struts是建立在MVC設計模式上的架構,你可以遵從標準的開發步驟來開發你的Struts Web應用程式,這些步驟大致可以描述如下:
1定義並產生所有代表應用程式的使用者介面的Views,同時產生這些Views所用到的所有ActionForms並將它們添加到struts-config.xml檔案中。
2在ApplicationResource.properties檔案中添加必要的MessageResources項目
3產生應用程式的控制器。
4在struts-config.xml檔案中定義Views與 Controller的關係。
5產生應用程式所需要的model組件
6編譯、運行你的應用程式.
(第2部分)
(第三部分)
一、JDBC的工作原理
Struts在本質上是java程式,要在Struts應用程式中訪問資料庫,首先,必須搞清楚Java Database Connectivity API(JDBC)的工作原理。正如其名字揭示的,JDBC庫提供了一個底層API,用來支援獨立於任何特定SQL實現的基本SQL功能。提供資料庫訪問的準系統。它是將各種資料庫訪問的公用概念抽取出來組成的類和介面。JDBC API包括兩個包:java.sql(稱之為JDBC核心API)和javax.sql(稱之為JDBC標準擴充)。它們合在一起,包含了用Java開發資料庫應用程式所需的類。這些類或介面主要有:
Java.sql.DriverManager
Java.sql.Driver
Java.sql.Connection
Java.sql.Statement
Java.sql.PreparedStatement
Java.sql.ResultSet等
這使得從Java程式發送SQL語句到資料庫變得比較容易,並且適合所有SQL方言。也就是說為一種資料庫如Oracle寫好了java應用程式後,沒有必要再為MS SQL Server再重新寫一遍。而是可以針對各種資料庫系統都使用同一個java應用程式。這樣表述大家可能有些難以接受,我們這裡可以打一個比方:聯合國開會時,聯合國的成員國的與會者(相當我們這裡的具體的資料庫管理系統)往往都有自己的語言(方言)。大會發言人(相當於我們這裡的java應用程式)不可能用各種語言來發言。你只需要使用一種語言(相當於我們這裡的JDBC)來發言就行了。那麼怎麼保證各成員國的與會者都聽懂發言呢,這就要依靠同聲翻譯(相當於我們這裡的JDBC驅動程式)。實際上是驅動程式將java程式中的SQL語句翻譯成具體的資料庫能執行的語句,再交由相應的資料庫管理系統去執行。因此,使用JDBC API訪問資料庫時,我們要針對不同的資料庫採用不同的驅動程式,驅動程式實際上是適合特定的資料庫JDBC介面的具體實現,它們一般具有如下三種功能:
UTF-8 編碼字元理論上可以最多到 6 個位元組長, 然而 16 位 BMP 字元最多隻用到 3 位元組長。
位元組 0xFE 和 0xFF 在 UTF-8 編碼中從未用到。
通過,UTF-8這種形式,Unicode終於可以廣泛的在各種情況下使用了。在討論struts的國際化編程之前,我們先來看看我們以前在jsp編程中是怎樣處理中文問題以及我們經常遇到的:
二、中文字元亂碼的原因及解決辦法
java的核心是Unicode的,也就是說,在程式處理字元時是用Unicode來表示字元的,但是檔案和流的儲存方式是使用位元組流的。在java的基礎資料型別 (Elementary Data Type)中,char是Unicode的,而byte是位元組,因此,在不同的環節java要對位元組流和char進行轉換。這種轉換髮生時如果字元集的編碼選擇不當,就會出現亂碼問題。
我們常見的亂碼大致有如下幾種情形:
1、漢字變成了問號"?"
2、有的漢字顯示正確,有的則顯示錯誤
3、顯示亂碼(有些是漢字但並不是你預期的)
4、讀寫資料庫出現亂碼
下面我們逐一對它們出現的原因做一些解釋:
首先,我們討論漢字變成問號的問題。
Java中byte與char相互轉換的方法在sun.io包中。其中,byte到char的常用轉換方法是:
public static ByteToCharConverter getConverter(String encoding);