路由控制到相關視圖
上面這樣簡單的描述,初學者可能會感到有些難以接受,下面舉個比較具體的例子來進一步幫助我們理解。如:假設,我們做的是個電子商務程序,現(xiàn)在程序要完成的操作任務是提交定單并返回定單號給客戶,這就是關于做什么的問題,應該由Action類完成,但具體怎么獲得數(shù)據(jù)庫連接,插入定單數(shù)據(jù)到數(shù)據(jù)庫表中,又怎么從數(shù)據(jù)庫表中取得這個定單號(一般是自增數(shù)據(jù)列的數(shù)據(jù)),這一系列復雜的問題,這都是解決怎么做的問題,則應該由一個(假設名為orderBo)業(yè)務對象即Model來完成。orderBo可能用一個返回整型值的名為submitOrder的方法來做這件事,Action則是先校驗定單數(shù)據(jù)是否正確,以免常說的垃圾進垃圾出;如果正確則簡單地調用orderBo的submitOrder方法來得到定單號;它還要處理在調用過程中可能出現(xiàn)任何錯誤;最后根據(jù)不同的情況返回不同的結果給客戶。
二、為什么要使用Struts框架
既然本文的開始就說了,自己可以建這種框架,為什么要使用Struts呢?我想下面列舉的這些理由是顯而易見的:首先,它是建立在MVC這種公認的好的模式上的,Struts在M、V和C上都有涉及,但它主要是提供一個好的控制器和一套定制的標簽庫上,也就是說它的著力點在C和V上,因此,它天生就有MVC所帶來的一系列優(yōu)點,如:結構層次分明,高可重用性,增加了程序的健壯性和可伸縮性,便于開發(fā)與設計分工,提供集中統(tǒng)一的權限控制、校驗、國際化、日志等等;其次,它是個開源項目得到了包括它的發(fā)明者Craig R.McClanahan在內的一些程序大師和高手持續(xù)而細心的呵護,并且經受了實戰(zhàn)的檢驗,使其功能越來越強大,體系也日臻完善;最后,是它對其他技術和框架顯示出很好的融合性。如,現(xiàn)在,它已經與tiles融為一體,可以展望,它很快就會與JSF等融會在一起。當然,和其他任何技術一樣,它也不是十全十美的,如:它對類和一些屬性、參數(shù)的命名顯得有些隨意,給使用帶來一些不便;還有如Action類execute方法的只能接收一個ActionForm參數(shù)等。但瑕不掩瑜,這些沒有影響它被廣泛使用。
三、Struts的安裝與基本配置
我們主要針對Struts1.1版本進行講解,這里假定讀者已經配置好java運行環(huán)境和相應的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目錄,主要文件有如下一些。
truts.jar
commons-beanutils.jar
commons-collections.jar
commons-dbcp.jar
commons-digester.jar
commons-logging.jar
commons-pool.jar
commons-services.jar
commons-validator.jar |
4、創(chuàng)建一個web.xml文件,這是一個基于servlet的Web應用程序都需要的部署描述文件,一個Struts Web應用,在本質上也是一個基于servlet的Web應用,它也不能例外。
Struts有兩個組件要在該文件中進行配置,它們是:ActionServlet和標簽庫。下面是一個配置清單:
lt;?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、創(chuàng)建一個基本的struts-config.xml文件,并把它放在Webapps/mystruts/WEB-INF/目錄中,該文件是基于Struts應用程序的配置描述文件,它將MVC結構中的各組件結合在一起,開發(fā)的過程中會不斷對它進行充實和更改。在Struts1.0時,一個應用只能有一個這樣的文件,給分工開發(fā)帶來了一些不便,在Struts1.1時,可以有多個這樣的文件,將上述缺點克服了。需在該文件中配置的組件有:data-sources
lobal-execptions
form-beans
global-forwards
action-mappings
controller
message-resources
plug-in |
配置清單如下:
lt;?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應用的所需的各種組件。前面已經提到,在開發(fā)過程中我們會不斷充實和修改上面兩個配置描述文件。下面我們將實際做一個非常簡單的應用程序來體驗一下Struts應用開發(fā)的真實過程,以期對其有一個真實的認識。在完成基礎部分的介紹后,筆者會給出一些在實際開發(fā)中經常用到而又讓初學者感到有些難度的實例。最后,會介紹Struts與其他框架的關系及結合它們生成應用程序的例子.
下面,我們就從一個最簡單的登錄例子入手,以對Struts的主要部分有一些直觀而清晰的認識。這個例子功能非常簡單,假設有一個名為lhb的用戶,其密碼是awave,程序要完成的任務是,呈現(xiàn)一個登錄界面給用戶,如果用戶輸入的名稱和密碼都正確返回一個歡迎頁面給用戶,否則,就返回登錄頁面要求用戶重新登錄并顯示相應的出錯信息。這個例子在我們講述Struts的基礎部分時會反復用到。之所以選用這個簡單的程序作為例子是因為不想讓過于復雜的業(yè)務邏輯來沖淡我們的主題。
因為Struts是建立在MVC設計模式上的框架,你可以遵從標準的開發(fā)步驟來開發(fā)你的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應用程序中訪問數(shù)據(jù)庫,首先,必須搞清楚Java Database Connectivity API(JDBC)的工作原理。正如其名字揭示的,JDBC庫提供了一個底層API,用來支持獨立于任何特定SQL實現(xiàn)的基本SQL功能。提供數(shù)據(jù)庫訪問的基本功能。它是將各種數(shù)據(jù)庫訪問的公共概念抽取出來組成的類和接口。JDBC API包括兩個包:java.sql(稱之為JDBC內核API)和javax.sql(稱之為JDBC標準擴展)。它們合在一起,包含了用Java開發(fā)數(shù)據(jù)庫應用程序所需的類。這些類或接口主要有:
Java.sql.DriverManager
Java.sql.Driver
Java.sql.Connection
Java.sql.Statement
Java.sql.PreparedStatement
Java.sql.ResultSet等
這使得從Java程序發(fā)送SQL語句到數(shù)據(jù)庫變得比較容易,并且適合所有SQL方言。也就是說為一種數(shù)據(jù)庫如Oracle寫好了java應用程序后,沒有必要再為MS SQL Server再重新寫一遍。而是可以針對各種數(shù)據(jù)庫系統(tǒng)都使用同一個java應用程序。這樣表述大家可能有些難以接受,我們這里可以打一個比方:聯(lián)合國開會時,聯(lián)合國的成員國的與會者(相當我們這里的具體的數(shù)據(jù)庫管理系統(tǒng))往往都有自己的語言(方言)。大會發(fā)言人(相當于我們這里的java應用程序)不可能用各種語言來發(fā)言。你只需要使用一種語言(相當于我們這里的JDBC)來發(fā)言就行了。那么怎么保證各成員國的與會者都聽懂發(fā)言呢,這就要依靠同聲翻譯(相當于我們這里的JDBC驅動程序)。實際上是驅動程序將java程序中的SQL語句翻譯成具體的數(shù)據(jù)庫能執(zhí)行的語句,再交由相應的數(shù)據(jù)庫管理系統(tǒng)去執(zhí)行。因此,使用JDBC API訪問數(shù)據(jù)庫時,我們要針對不同的數(shù)據(jù)庫采用不同的驅動程序,驅動程序實際上是適合特定的數(shù)據(jù)庫JDBC接口的具體實現(xiàn),它們一般具有如下三種功能:
UTF-8 編碼字符理論上可以最多到 6 個字節(jié)長, 然而 16 位 BMP 字符最多只用到 3 字節(jié)長。
字節(jié) 0xFE 和 0xFF 在 UTF-8 編碼中從未用到。
通過,UTF-8這種形式,Unicode終于可以廣泛的在各種情況下使用了。在討論struts的國際化編程之前,我們先來看看我們以前在jsp編程中是怎樣處理中文問題以及我們經常遇到的:
二、中文字符亂碼的原因及解決辦法
java的內核是Unicode的,也就是說,在程序處理字符時是用Unicode來表示字符的,但是文件和流的保存方式是使用字節(jié)流的。在java的基本數(shù)據(jù)類型中,char是Unicode的,而byte是字節(jié),因此,在不同的環(huán)節(jié)java要對字節(jié)流和char進行轉換。這種轉換發(fā)生時如果字符集的編碼選擇不當,就會出現(xiàn)亂碼問題。
我們常見的亂碼大致有如下幾種情形:
1、漢字變成了問號"?"
2、有的漢字顯示正確,有的則顯示錯誤
3、顯示亂碼(有些是漢字但并不是你預期的)
4、讀寫數(shù)據(jù)庫出現(xiàn)亂碼
下面我們逐一對它們出現(xiàn)的原因做一些解釋:
首先,我們討論漢字變成問號的問題。
Java中byte與char相互轉換的方法在sun.io包中。其中,byte到char的常用轉換方法是:
public static ByteToCharConverter getConverter(String encoding);
為了便于大家理解,我們先來做一個小實驗:比如,漢字"你"的GBK編碼為0xc4e3,其Unicode編碼是\u4f60。我們的實驗是這樣的,先有一個頁面比如名為a_gbk.jsp輸入漢字"你",提交給頁面b_gbk.jsp。在b_gbk.jsp文件中以某種編碼方式得到"你"的字節(jié)數(shù)組,再將該數(shù)組以某種編碼方式轉換成char,如果得到的char值是0x4f60則轉換是正確的。
a_gbk.jsp的代碼如下:
/FONT>參考文獻:
UTF-8 and Unicode FAQ
《JSP動態(tài)網站技術入門與提高》太陽工作室 孫曉龍 趙莉編著
第5部分
一個支持i18n的應用程序應該有如下一些特征:
1增加支持的語言時要求不更改程序代碼
2字符元素、消息、和圖象保存在原代碼之外
3依賴于不同文化的數(shù)據(jù)如:日期時間、小數(shù)、及現(xiàn)金符號等數(shù)據(jù)對用戶的語言和地理位置應該有正確的格式
4應用程序能迅速地適應新語言和/或新地區(qū)
Struts主要采用兩個i18n組件來實現(xiàn)國際化編程:
第一個組件是一個被應用程序控制器管理的消息類,它引用包含地區(qū)相關信息串的資源包。第二個組件是一個JSP定制標簽,,它用于在View層呈現(xiàn)被控制器管理的實際的字符串。在我們前面的登錄例子中這兩方面的內容都出現(xiàn)過。
用Struts實現(xiàn)國際化編程的標準做法是:生成一個java屬性文件集。每個文件包含您的應用程序要顯示的所有消息的鍵/值對。
這些文件的命名要遵守如下規(guī)則,代表英文消息的文件可作為缺省的文件,它的名稱是ApplicationResources.properties;其他語種的文件在文件名中都要帶上相應的地區(qū)和語言編碼串,如代表中文的文件名應為ApplicationResources_zh_CN.properties。并且其他語種的文件與ApplicationResources.properties文件要放在同一目錄中。
ApplicationResources.properties文件的鍵/值都是英文的,而其他語種文件的鍵是英文的,值則是對應的語言。如在我們前面的登錄例子中的鍵/值對:logon.jsp.prompt.username=Username:在中文文件中就是:logon.jsp.prompt.username=用戶名:當然,在實際應用時要把中文轉換為AscII碼。
有了上一篇文章和以上介紹的一些基礎知識后。我們就可以將我們的登錄程序進行國際化編程了。
首先,我們所有jsp頁面文件的字符集都設置為UTF-8。即在頁面文件的開始寫如下指令行:
,在我們的登錄例子中已經這樣做了,這里不需要再改動。
其次,將所有的request的字符集也設置為UTF-8。雖然,我們可以在每個文件中加入這樣的句子:request.setCharacterEncoding("UTF-8");來解決,但這樣顯得很麻煩。一種更簡單的解決方法是使用filter。具體步驟如下:
在mystruts\WEB-INF\classes目錄下再新建一個名為filters的目錄,新建一個名為:SetCharacterEncodingFilter的類,并保存在該目錄下。其實,這個類并不要您親自來寫,可以借用tomcat中的例子。現(xiàn)將該例子的程序節(jié)選如下:
/FONT>
lt;validator name="required"
<!--①-->
classname="org.apache.struts.validator.FieldChecks"
method="validateRequired"
methodParams="java.lang.Object,
org.apache.commons.validator.ValidatorAction,
org.apache.commons.validator.Field,
org.apache.struts.action.ActionErrors,
javax.servlet.http.HttpServletRequest"
<!--②-->
msg="errors.required">
<!--③-->
<javascript><![CDATA[
function validateRequired(form) {
var isValid = true;
var focusField = null;
var i = 0;
var fields = new Array();
oRequired = new required();
for (x in oRequired) {
??????var field = form[oRequired[x][0]];
??????
if (field.type == 'text' ||
field.type == 'textarea' ||
field.type == 'file' ||
field.type == 'select-one' ||
field.type == 'radio' ||
field.type == 'password') {
var value = '';
????????????????????????// get field's value
????????????????????????if (field.type == "select-one") {
????????????????????????var si = field.selectedIndex;
????????????????????????if (si >= 0) {
????????????????????????value = field.options[si].value;
??????????????????????????????????????????}
????????????????????????????????????} else {
??????????????????????????????????????????value = field.value;
????????????????????????????????????}
if (trim(value).length == 0) {
?????? if (i == 0) {
?????? focusField = field;
?????? }
?????? fields[i++] = oRequired[x][1];
?????? isValid = false;
}
}
}
if (fields.length > 0) {
focusField.focus();
alert(fields.join('\n'));
}
return isValid;
}
// Trim whitespace from left and right sides of s.
function trim(s) {
return s.replace( /^\s*/, "" ).replace( /\s*$/, "" );
}
]]>
</javascript>
</validator> |
????① 節(jié)的代碼是引用一個服務器邊的驗證器,其對應的代碼清單如下:
ublic static boolean validateRequired(Object bean,
ValidatorAction va, Field field,
ActionErrors errors,
HttpServletRequest request) {
String value = null;
if (isString(bean)) {
value = (String) bean;
} else {
value = ValidatorUtil.getValueAsString(bean, field.getProperty());
}
if (GenericValidator.isBlankOrNull(value)) {
errors.add(field.getKey(), Resources.getActionError(request, va, field));
return false;
} else {
return true;
}
} |
????② 節(jié)是驗證失敗后的出錯信息,要將對應這些鍵值的信息寫入到ApplicationResources.properity文件中,常見的錯誤信息如下:
Standard error messages for validator framework checks
errors.required={0} is required.
errors.minlength={0} can not be less than {1} characters.
errors.maxlength={0} can not be greater than {1} characters.
errors.invalid={0} is invalid.
errors.byte={0} must be a byte.
errors.short={0} must be a short.
errors.integer={0} must be an integer.
errors.long={0} must be a long.
errors.float={0} must be a float.
errors.double={0} must be a double.
errors.date={0} is not a date.
errors.range={0} is not in the range {1} through {2}.
errors.creditcard={0} is an invalid credit card number.
errors.email={0} is an invalid e-mail address. |
????③ 節(jié)的代碼用于客戶邊的JavaScript驗證
其次,在validation.xml文件中配置要驗證的form極其相應的字段,下面是該文件中的代碼:
lt;?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE form-validation PUBLIC "-//Apache Software Foundation
//DTD Commons Validator Rules Configuration 1.0//EN"
"http://jakarta.apache.org/commons/dtds/validator_1_0.dtd">
<form-validation>
<formset>
<form name="userInfoForm">
<field property="username"
depends="required,mask,minlength,maxlength">
<arg0 key="logon.jsp.prompt.username" resource="true"/>
<arg1 name="minlength" key="${var:minlength}" resource="false"/>
<arg1 name="maxlength" key="${var:maxlength}" resource="false"/>
<var>
<var-name>mask</var-name>
<var-value>^\w</var-value>
</var>
<var>
<var-name>minlength</var-name>
<var-value>2</var-value>
</var>
<var>
<var-name>maxlength</var-name>
<var-value>16</var-value>
</var>
</field>
<field property="password"
depends="required,minlength,maxlength">
<arg0 key="logon.jsp.prompt.password" resource="true"/>
<arg1 name="minlength" key="${var:minlength}" resource="false"/>
<arg1 name="maxlength" key="${var:maxlength}" resource="false"/>
<var>
<var-name>minlength</var-name>
<var-value>2</var-value>
</var>
<var>
<var-name>maxlength</var-name>
<var-value>16</var-value>
</var>
</field>
</form>
</formset>
</form-validation> |
????這里要注意的是:該文中的的鍵值都是取自資源綁定中的。前面還講到了出錯信息也是寫入ApplicationResources.properity文件中,因此,這就為國際化提供了一個很好的基礎。
????再次,為了使服務器邊的驗證能夠進行,將用到的formBean從ActionForm的子類改為ValidatorForm的子類,即:
????將public class UserInfoForm extends ActionForm改為:public class UserInfoForm extends ValidatorForm
????到此,進行服務器邊的驗證工作已經一切準備得差不多了,此時,只要完成最后步驟就可以實驗服務器邊的驗證了。但大多數(shù)情況下,人們總希望把這些基本的簡單驗證放在客戶邊進行。
????為了能進行客戶邊的驗證,我們還要對logon.jsp文件做適當?shù)男薷摹?
????將
lt;html:form action="/logonAction.do" focus="username"> |
改為
????
lt;html:form action="/logonAction.do" focus="username" onsubmit="return validateUserInfoForm(this)"> |
????在標簽后加上:
????
lt;html:javascript dynamicJavascript="true" staticJavascript="true" formName="userInfoForm"/> |
????最后,對struts的配置文件struts-config.xml作適當?shù)男薷模?
????1、將
lt;action input="/logon.jsp" name="userInfoForm"
path="/logonAction" scope="session" type="action.LogonAction" validate="false" > |
改為
lt;action input="/logon.jsp" name="userInfoForm"
path="/logonAction" scope="session" type="action.LogonAction" validate="true" > |
其作用是要求進行校驗
????2、將下列代碼放在struts-config.xml文件中的標簽前。其作用是將用于校驗的各個組件結合在一起。
lt;plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property property="pathnames"
??????value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml" />
</plug-in> |
????到此為止,我們的一切工作準備就緒,您可以享受自己的勞動成果了,試著輸入各種組合的用戶名和口令,看看它們的驗證效果。仔細體會你會發(fā)現(xiàn),服務器邊的驗證要更全面一些,比如對password的字符長度的驗證。
????參考文獻:
《Struts in Action》Ted Husted Cedric Dumoulin George Franciscus David Winterfeldt著
《Programming Jakarta Struts》Chuck Cavaness著
第7部分
上一篇文章中介紹校驗時提到客戶邊的校驗用到了JavaScript,實際上用Struts配合JavaScript還可以實現(xiàn)許多有用的功能,比如,級聯(lián)下拉菜單的實現(xiàn)就是一個典型的例子:
本例假設要實現(xiàn)的是一個文章發(fā)布系統(tǒng),我們要發(fā)布的文章分為新聞類和技術類,其中新聞類又分為時事新聞和行業(yè)動態(tài);技術類又分為操作系統(tǒng)、數(shù)據(jù)庫、和編程語言等,為了便于添加新的條目,所有這些都保存在數(shù)據(jù)庫表中。
為此,我們建立一個名為articleClass的表和一個名為articleSubClass的表。
rticleClass表的結構如下:
articleClassID字段:char類型,長度為2,主鍵
articleClassName字段:varchar類型,長度為20
articleSubClass表的結構如下:
articleClassID字段:char類型,長度為2
articleSubClassID字段:char類型,長度為2與articleClassID一起構成主鍵
articleSubClassName字段:varchar類型,長度為20 |
表建好后,在articleClass表中錄入如下數(shù)據(jù):如,01、新聞類;02、技術類
在articleSubClass表中錄入:01、01、時事新聞;01、02、行業(yè)動態(tài);02、01、操作系統(tǒng)等記錄。到這里,數(shù)據(jù)庫方面的準備工作就已做好。
有了前面做登錄例子的基礎,理解下面要進行的工作就沒有什么難點了,我們現(xiàn)在的工作也在原來mystruts項目中進行。首先,建立需要用到的formbean即ArticleClassForm,其代碼如下:
ackage entity;
import org.apache.struts.action.*;
import javax.servlet.http.*;
import java.util.Collection;
public class ArticleClassForm extends ActionForm {
//為select的option做準備
private Collection beanCollection;
private String singleSelect = "";
private String[] beanCollectionSelect = { "" };
private String articleClassID;
private String articleClassName;
private String subI;//子類所在行數(shù)
private String subJ;//子類所在列數(shù)
private String articleSubClassID;
private String articleSubClassName;
public Collection getBeanCollection(){
return beanCollection;
}
public void setBeanCollection(Collection beanCollection){
this.beanCollection=beanCollection;
}
public String getSingleSelect() {
return (this.singleSelect);
}
public void setSingleSelect(String singleSelect) {
this.singleSelect = singleSelect;
}
public String[] getBeanCollectionSelect() {
return (this.beanCollectionSelect);
}
public void setBeanCollectionSelect(String beanCollectionSelect[]) {
this.beanCollectionSelect = beanCollectionSelect;
}
public String getArticleClassID() {
return articleClassID;
}
public void setArticleClassID(String articleClassID) {
this.articleClassID = articleClassID;
}
public String getArticleClassName() {
return articleClassName;
}
public void setArticleClassName(String articleClassName) {
this.articleClassName = articleClassName;
}
public String getSubI() {
return subI;
}
public void setSubI(String subI) {
this.subI = subI;
}
public String getSubJ() {
return subJ;
}
public void setSubJ(String subJ) {
this.subJ = subJ;
}
public String getArticleSubClassID() {
return articleSubClassID;
}
public void setArticleSubClassID(String articleSubClassID) {
this.articleSubClassID = articleSubClassID;
}
public String getArticleSubClassName() {
return articleSubClassName;
}
public void setArticleSubClassName(String articleSubClassName) {
this.articleSubClassName = articleSubClassName;
}
} |
將它放在包entity中。其次,我們的系統(tǒng)要訪問數(shù)據(jù)庫,因此也要建立相應的數(shù)據(jù)庫訪問對象ArticleClassDao,其代碼如下:
ackage db;
import entity.ArticleClassForm;
import db.*;
import java.sql.*;
import java.util.Collection;
import java.util.ArrayList;
import org.apache.struts.util.LabelValueBean;
public class ArticleClassDao {
private Connection con;
public ArticleClassDao(Connection con) {
this.con=con;
}
public Collection findInUseForSelect(){
PreparedStatement ps=null;
ResultSet rs=null;
ArrayList list=new ArrayList();
String sql="select * from articleClass order by articleClassID";
try{
if(con.isClosed()){
throw new IllegalStateException("error.unexpected");
}
ps=con.prepareStatement(sql);
rs=ps.executeQuery();
while(rs.next()){
String value=rs.getString("articleClassID");
String label=rs.getString("articleClassName");
list.add(new LabelValueBean(label,value));
}
return list;
}
catch(SQLException e){
e.printStackTrace();
throw new RuntimeException("error.unexpected");
}
finally{
try{
if(ps!=null)
ps.close();
if(rs!=null)
rs.close();
}
catch(SQLException e){
e.printStackTrace();
throw new RuntimeException("error.unexpected");
}
}
}
public Collection findInUseForSubSelect(){
PreparedStatement ps=null;
ResultSet rs=null;
PreparedStatement psSub=null;
ResultSet rsSub=null;
int i=0;//大類記數(shù)器
int j=0;//小類記數(shù)器
String classID="";
String subClassID="";
String subClassName="";
ArrayList list=new ArrayList();
ArticleClassForm articleClassForm;
String sql="select * from articleClass order by articleClassID";
try{
if(con.isClosed()){
throw new IllegalStateException("error.unexpected");
}
ps=con.prepareStatement(sql);
rs=ps.executeQuery();
while(rs.next()){
i++;
classID=rs.getString("articleClassID");
String sqlSub="select * from articleSubClass where articleClassID=?
????????????order by articleSubClassID";
psSub=con.prepareStatement(sqlSub);
psSub.setString(1,classID);
rsSub=psSub.executeQuery();
articleClassForm=new ArticleClassForm();
articleClassForm.setSubI(""+i);
articleClassForm.setSubJ(""+j);
articleClassForm.setArticleSubClassID("請輸入一個小類");
articleClassForm.setArticleSubClassName("請輸入一個小類");
list.add(articleClassForm);
while(rsSub.next()){
subClassID=rsSub.getString("articleSubClassID");
subClassName=rsSub.getString("articleSubClassName");
j++;
//optionStr="articleSubClassGroup[" + i + "][" + j + "]=
new Option('"+ subClassName +"','"+ subClassID+ "')";
articleClassForm=new ArticleClassForm();
articleClassForm.setSubI(""+i);
articleClassForm.setSubJ(""+j);
articleClassForm.setArticleSubClassID(subClassID);
articleClassForm.setArticleSubClassName(subClassName);
list.add(articleClassForm);
}
j=0;
}
return list;
}
catch(SQLException e){
e.printStackTrace();
throw new RuntimeException("error.unexpected");
}
finally{
try{
if(ps!=null)
ps.close();
if(rs!=null)
rs.close();
}
catch(SQLException e){
e.printStackTrace();
throw new RuntimeException("error.unexpected");
}
}
}
} |
將它保存在db目錄中。它們的目的是將文章的類和子類信息從數(shù)據(jù)庫表中讀出,以一定的格式保存在集合對象中以供頁面顯示。
再次,我們要建立相應的jsp文件,文件名為selectArticleClass.jsp,代碼如下:
lt;%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<html>
<head>
<title>
選擇文件類別
</title>
</head>
<body bgcolor="#ffffff">
<h3>
選擇文件所屬類型
</h3>
<html:errors/>
<table width="500" border="0" cellspacing="0" cellpadding="0">
<tr>
<td><html:form name="articleClassForm" type="entity.ArticleClassForm"
??????action="selectArticleClassAction.do">
<table width="500" border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="right">文章大類*</td>
<td>
<html:select property="articleClassID" styleClass="word"
?????????????????? onchange="articleClassFormredirect(this.options.selectedIndex)">
<html:option value="">請選擇一個大類</html:option>
<html:optionsCollection name="articleClassForm" property="beanCollection" styleClass="word"/>
</html:select>
</td>
</tr>
<tr>
<td align="right">文章小類*</td>
<td>
<select name="articleSubClassID" Class="word" >
<option value="">請選擇一個小類</option>
</select>
<SCRIPT language=JavaScript>
<!--
var articleSubClassGroups=document.articleClassForm.articleClassID.
?????????????????? options.length
var articleSubClassGroup=new Array(articleSubClassGroups)
for (i=0; i<articleSubClassGroups; i++)
articleSubClassGroup[i]=new Array()
<logic:iterate name="articleSubClassList" id="articleClassForm"
?????????????????? scope="request" type="entity.ArticleClassForm">
articleSubClassGroup[<bean:write name="articleClassForm"
????????????????????????property="subI"/>][<bean:write name="articleClassForm"
????????????????????????property="subJ"/>]=new Option("<bean:write name="articleClassForm"
??????????????????property="articleSubClassName"/>","<bean:write name="articleClassForm"
??????????????????property="articleSubClassID"/>")
</logic:iterate>
var articleSubClassTemp=document.articleClassForm.articleSubClassID
function articleClassFormredirect(x){
for (m=articleSubClassTemp.options.length-1;m>0;m--)
articleSubClassTemp.options[m]=null
for (i=0;i<articleSubClassGroup[x].length;i++){
articleSubClassTemp.options[i]=new
????????????Option(articleSubClassGroup[x][i].text,
articleSubClassGroup[x][i].value)
}
articleSubClassTemp.options[0].selected=true
}
//-->
</SCRIPT>
</td>
</tr>
</table>
</html:form>
</td>
</tr>
</table>
</body>
</html> |
這里值得重點關注的是其中的JavaScript代碼,有興趣的可以仔細分析一下它們是怎樣配合集合中的元素來實現(xiàn)級聯(lián)選擇的。
最后,為了例子的完整。我們將涉及到action代碼和必要的配置代碼在下面列出:其中,action的文件名為SelectArticleClassAction.java,代碼如下:
ackage action;
import entity.*;
import org.apache.struts.action.*;
import javax.servlet.http.*;
import javax.sql.DataSource;
import java.sql.Connection;
import db.ArticleClassDao;
import java.util.Collection;
import java.sql.SQLException;
public class SelectArticleClassAction extends Action {
public ActionForward execute(ActionMapping actionMapping, ActionForm actionForm,
HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
/**@todo: complete the business logic here, this is just a skeleton.*/
ArticleClassForm articleClassForm = (ArticleClassForm) actionForm;
DataSource dataSource;
Connection cnn=null;
ActionErrors errors=new ActionErrors();
try{
dataSource = getDataSource(httpServletRequest,"A");
cnn = dataSource.getConnection();
ArticleClassDao articleClassDao=new ArticleClassDao(cnn);
Collection col=articleClassDao.findInUseForSelect();
articleClassForm.setBeanCollection(col);
httpServletRequest.setAttribute("articleClassList",col);
//處理子類選項
Collection subCol=articleClassDao.findInUseForSubSelect();
httpServletRequest.setAttribute("articleSubClassList",subCol);
return actionMapping.findForward("success");
}
catch(Throwable e){
e.printStackTrace();
//throw new RuntimeException("未能與數(shù)據(jù)庫連接");
ActionError error=new ActionError(e.getMessage());
errors.add(ActionErrors.GLOBAL_ERROR,error);
}
finally{
try{
if(cnn!=null)
cnn.close();
}
catch(SQLException e){
throw new RuntimeException(e.getMessage());
}
}
saveErrors(httpServletRequest,errors);
return actionMapping.findForward("fail");
}
} |
將其保存在action目錄中。
在struts-config.xml文件中做如下配置:
在中加入lt;form-bean name="articleClassForm" type="entity.ArticleClassForm" /> |
在中加入:
lt;action name="articleClassForm" path="/selectArticleClassAction" scope="session"
??????type="action.SelectArticleClassAction" validate="false">
<forward name="success" path="/selectArticleClass.jsp" />
<forward name="fail" path="/genericError.jsp" />
</action> |
為了對應配置中的lt;forward name="fail" path="/genericError.jsp" /> |
,我們還要提供一個顯示錯誤信息的jsp頁面,其代碼如下:
lt;%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
<head>
<title>
genericError
</title>
<link href="css/mycss.css" rel="stylesheet" type="text/css">
</head>
<body bgcolor="#ffffff">
<html:errors/>
</body>
</html> |
現(xiàn)在一切就緒,可以編譯執(zhí)行了。在瀏覽器中輸入:http://127.0.0.1:8080/mystruts/selectArticleClassAction.do就可以看到該例子的運行結果了。(T111)
ackage filters;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.UnavailableException;
/**
* <p>Example filter that sets the character encoding to be used in parsing the
* incoming request, either unconditionally or only if the client did not
* specify a character encoding. Configuration of this filter is based on
* the following initialization parameters:</p>
* <ul>
* <li><strong>encoding</strong> - The character encoding to be configured
* for this request, either conditionally or unconditionally based on
* the <code>ignore</code> initialization parameter. This parameter
* is required, so there is no default.</li>
* <li><strong>ignore</strong> - If set to "true", any character encoding
* specified by the client is ignored, and the value returned by the
* <code>selectEncoding()</code> method is set. If set to "false,
* <code>selectEncoding()</code> is called <strong>only</strong> if the
* client has not already specified an encoding. By default, this
* parameter is set to "true".</li>
* </ul>
*
* <p>Although this filter can be used unchanged, it is also easy to
* subclass it and make the <code>selectEncoding()</code> method more
* intelligent about what encoding to choose, based on characteristics of
* the incoming request (such as the values of the <code>Accept-Language</code>
* and <code>User-Agent</code> headers, or a value stashed in the current
* user's session.</p>
*
* @author Craig McClanahan
* @version $Revision: 1.2 $ $Date: 2001/10/17 22:53:19 $
*/
public class SetCharacterEncodingFilter implements Filter {
// ----------------------------------------------------- Instance Variables
/**
* The default character encoding to set for requests that pass through
* this filter.
*/
protected String encoding = null;
/**
* The filter configuration object we are associated with. If this value
* is null, this filter instance is not currently configured.
*/
protected FilterConfig filterConfig = null;
/**
* Should a character encoding specified by the client be ignored?
*/
protected boolean ignore = true;
// --------------------------------------------------------- Public Methods
/**
* Take this filter out of service.
*/
public void destroy() {
this.encoding = null;
this.filterConfig = null;
}
/**
* Select and set (if specified) the character encoding to be used to
* interpret request parameters for this request.
*
* @param request The servlet request we are processing
* @param result The servlet response we are creating
* @param chain The filter chain we are processing
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet error occurs
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
// Conditionally select and set the character encoding to be used
if (ignore || (request.getCharacterEncoding() == null)) {
String encoding = selectEncoding(request);
if (encoding != null)
request.setCharacterEncoding(encoding);
}
// Pass control on to the next filter
chain.doFilter(request, response);
}
/**
* Place this filter into service.
*
* @param filterConfig The filter configuration object
*/
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
this.encoding = filterConfig.getInitParameter("encoding");
String value = filterConfig.getInitParameter("ignore");
if (value == null)
this.ignore = true;
else if (value.equalsIgnoreCase("true"))
this.ignore = true;
else if (value.equalsIgnoreCase("yes"))
this.ignore = true;
else
this.ignore = false;
}
// ------------------------------------------------------ Protected Methods
/**
* Select an appropriate character encoding to be used, based on the
* characteristics of the current request and/or filter initialization
* parameters. If no character encoding should be set, return
* <code>null</code>.
* <p>
* The default implementation unconditionally returns the value configured
* by the <strong>encoding</strong> initialization parameter for this
* filter.
*
* @param request The servlet request we are processing
*/
protected String selectEncoding(ServletRequest request) {
return (this.encoding);
}
} |
其中,request.setCharacterEncoding(encoding);是一個關鍵句子。
為了讓該類工作,我們還要在web.xml文件中對它進行配置,配置代碼如下:
lt;filter>
<filter-name>Set Character Encoding</filter-name>
<filter-class>filters.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Set Character Encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> |
最后,就是準備資源包文件,我們以創(chuàng)建一個中文文件為例:
將ApplicationResources.properties文件打開,另存為ApplicationResources_zh.properties,這只是一個過渡性質的文件。將文件中鍵/值對的值都用中文表示。更改完后的代碼如下:
Application Resource for the logon.jsp
logon.jsp.title=登錄頁
logon.jsp.page.heading=歡迎 世界!
logon.jsp.prompt.username=用戶名:
logon.jsp.prompt.password=口令:
logon.jsp.prompt.submit=提交
logon.jsp.prompt.reset=復位
#Application Resource for the main.jsp
main.jsp.title=主頁
main.jsp.welcome=歡迎:
#Application Resource for the LogonAction.java
error.missing.username=<li><font color="red">沒有輸入用戶名</font></li>
error.missing.password=<li><font color="red">沒有輸入口令</font></li>
#Application Resource for the UserInfoBo.java
error.noMatch=<li><font color="red">沒有匹配的用戶</font></li>
#Application Resource for the UserInfoBo.java
error.logon.invalid=<li><font color="red">用戶名/口令是無效的</font></li>
error.removed.user=<li><font color="red">找不到該用戶</font></li>
error.unexpected=<li><font color="red">不可預期的錯誤</font></li> |
使用native2ascii工具將上面文件中的中文字符轉換為ascii碼,并生成一個最終使用的資源文件ApplicationResources_zh_CN.properties。
具體做法是打開一個dos窗口,到mystruts\WEB-INF\classes目錄下,運行如下語句:
native2ascii -encoding GBK ApplicationResources_zh.properties ApplicationResources_zh_CN.properties
生成的文件ApplicationResources_zh_CN.properties的內容如下:
Application Resource for the logon.jsp
logon.jsp.title=\u767b\u5f55\u9875
logon.jsp.page.heading=\u6b22\u8fce \u4e16\u754c!
logon.jsp.prompt.username=\u7528\u6237\u540d:
logon.jsp.prompt.password=\u53e3\u4ee4:
logon.jsp.prompt.submit=\u63d0\u4ea4
logon.jsp.prompt.reset=\u590d\u4f4d
#Application Resource for the main.jsp
main.jsp.title=\u4e3b\u9875
main.jsp.welcome=\u6b22\u8fce:
#Application Resource for the LogonAction.java
error.missing.username=<li><font color="red">\u6ca1\u6709\u8f93\u5165\u7528\u6237\u540d</font></li>
error.missing.password=<li><font color="red">\u6ca1\u6709\u8f93\u5165\u53e3\u4ee4</font></li>
#Application Resource for the UserInfoBo.java
error.noMatch=<li><font color="red">\u6ca1\u6709\u5339\u914d\u7684\u7528\u6237</font></li>
#Application Resource for the UserInfoBo.java
error.logon.invalid=<li><font color="red">\u7528\u6237\u540d/\u53e3\u4ee4\u662f\u65e0\u6548\u7684</font></li>
error.removed.user=<li><font color="red">\u627e\u4e0d\u5230\u8be5\u7528\u6237</font></li>
error.unexpected=<li><font color="red">\u4e0d\u53ef\u9884\u671f\u7684\u9519\u8bef</font></li> |
從這里可以看出,所有的中文字都轉換成了對應的Unicode碼。
現(xiàn)在,再運行登錄例子程序,您會發(fā)現(xiàn)它已經是顯示的中文了。在瀏覽器的"工具"--"Internet選項"的"語言首選項"對話框中,去掉"中文(中國)"加上英文,再試登錄程序,此時,又會顯示英文。這就是說不同國家(地區(qū))的客戶都可以看到自己語言的內容,這就實現(xiàn)了國際化編程的基本要求。如果還要顯示其他語言,可采用類似處理中文的方法進行,這里就不細講了。
本文中的例子程序所采用的數(shù)據(jù)庫仍然是MS SQLServer2000,數(shù)據(jù)庫字符集為gbk。實驗表明,對簡、繁體中文,英文及日文字符都能支持。
參考文獻:
《Programming Jakarta Struts》Chuck Cavaness著
《Mastering Jakarta Struts》James Goodwill著
第6部分
本文我們來討論一下Struts中的輸入校驗問題。我們知道,信息系統(tǒng)有垃圾進垃圾出的特點,為了避免垃圾數(shù)據(jù)的輸入,對輸入進行校驗是任何信息系統(tǒng)都要面對的問題。在傳統(tǒng)的編程實踐中,我們往往在需要進行校驗的地方分別對它們進行校驗,而實際上需要校驗的東西大多都很類似,如必需的字段、日期、范圍等等。因此,應用程序中往往到處充斥著這樣一些顯得冗余的代碼。而與此形成鮮明對照的是Struts采用Validator框架(Validator框架現(xiàn)在是Jakarta Commons項目的一部分)來解決校驗問題,它將校驗規(guī)則代碼集中到外部的且對具體的應用程序中立的.xml文件中,這樣,就將那些到處出現(xiàn)的校驗邏輯從應用程序中分離出來,任何一個Struts應用都可以使用這個文件,同時還為校驗規(guī)則的擴展提供了便利。更難能可貴的是由于Validator框架將校驗中要用到的一些消息等信息與資源綁定有機結合在一起,使得校驗部分的國際化編程變得十分的便捷和自然。
????Validator框架大致有如下幾個主要組件:
????Validators:是Validator框架調用的一個Java類,它處理那些基本的通用的校驗,包括required、mask(匹配正則表達式)、最小長度、最大長度、范圍、日期等
????.xml配置文件:主要包括兩個配置文件,一個是validator-rules.xml,另一個是validation.xml。前者的內容主要包含一些校驗規(guī)則,后者則包含需要校驗的一些form及其組件的集合。
????資源綁定:提供(本地化)標簽和消息,缺省地共享struts的資源綁定。即校驗所用到的一些標簽與消息都寫在ApplicationResources.properity文件中。
????Jsp tag:為給定的form或者action path生成JavaScript validations。
????ValidatorForm:它是ActionForm的一個子類。
????為了對Validator框架有一個比較直觀的認識,我們還是以前面的登陸例子的輸入來示范一下Validator框架的使用過程:
????首先,找一個validator-rules.xml文件放在mystruts\WEB-INF目錄下,下面是該文件中涉及到的required驗證部分代碼的清單:
lt;%@ page contentType="text/html; charset=GBK" language="java" import="java.sql.*" errorPage="" %>
<table width="611" border="0" align="center" cellpadding="0" cellspacing="0">
<tr>
<td> </td>
<td class="bigword"> </td>
<td> </td>
</tr>
<tr>
<td width="100"> </td>
<td class="bigword">Input</td>
<td width="100"> </td>
</tr>
<tr>
<td> </td>
<td class="bigword"> </td>
<td> </td>
</tr>
</table>
<table width="611" border="0" align="center" cellpadding="0" cellspacing="0">
<tr>
<td><form method="post" action="b_gbk.jsp">
<table width="611" border="0" cellpadding="0" cellspacing="0">
<tr>
<td width="100" align="right"></td>
<td><input name="ClsID" type="text" class="word" id="ClsID" maxlength="2" >
*</td>
</tr>
<tr>
<td width="100" align="right"> </td>
<td><input name="btn" type="submit" value="OK">
</td>
</tr>
</table>
</form></td>
</tr>
</table> |
b_gbk.jsp的代碼如下:
lt;%@ page contentType="text/html; charset=GBK" import="sun.io.*,java.util.*" %>
<%
String a=(String)request.getParameter("ClsID");
byte b[]=a.getBytes("ISO8859-1");
for(int j=0;j<b.length;j++){
out.println(Integer.toHexString(b[j])+"<br>");
}
ByteToCharConverter convertor=ByteToCharConverter.getConverter("GBK");
char[] c=convertor.convertAll(b);
out.println("b length:"+b.length+"<br>");
out.println("c length:"+c.length+"<br>");
for(int i=0;i<c.length;i++){
??????out.println(Integer.toHexString(c[i])+"<br>");
}
String a1=new String(a.getBytes("ISO8859-1"),"GBK");
%>
<%="a是:"+a%><br>
<%="a1是:"+a1%> |
在瀏覽器中打開a_gbk.jsp并輸入一個"你"字,點擊OK按鈕提交表單,則會出現(xiàn)如圖1所示的結果:

圖1
從圖1可以看出,在b_gbk.jsp中這樣將byte轉換為char是正確的,即得到的char是\u4f60。這里要注意的是:byte b[]=a.getBytes("ISO8859-1");中的編碼是ISO8859-1,這就是我們前面提到的有些web容器在您沒有指定request的字符集時它就采用缺省的ISO8859-1。
從圖1中我們還看到表達式中的a并沒有正確地顯示"你"而是變成"??"這是什么原因呢?這里的a是作為一個String被顯示的,我們來看看我們常用的String構造函數(shù):
String(byte[] bytes,String encoding);
在國標平臺上,該函數(shù)會認為bytes是按GBK編碼的,如果后一個參數(shù)省略,它也會認為是encoding是GBK。
對前一個參數(shù)就相當于將b_gbk.jsp文件的這句byte b[]=a.getBytes("ISO8859-1");中的ISO8859-1改為GBK,這樣顯然在GBK字符集中找不到相應的目的編碼,它給出的結果是0x3f、0x3f。因此,就會顯示為"??",這也就是造成亂碼的第一種現(xiàn)象的原因。我們的例子是演示的從byte到char的轉換過程,相反的過程也會造成同樣的問題,限于篇幅,就不在此討論了,大家自己可以做類似的實驗來驗證。
解決該問題的方法就是象例子中a1那樣,在獲取byte數(shù)組時,指定編碼為ISO8859-1。
接下來,我們討論有些漢字能正常顯示,有些不能正常顯示的問題。
如果我們將String a1=new String(a.getBytes("ISO8859-1"),"GBK");中的GBK改為GB2312則象朱镕基的"镕"字就不能正常顯示,這是因為該字是GBK中的字符而在GB2312中不存在。
解決上述兩種問題的方法就是象a1那樣構造String,也就是人們常說的同時也是常用的轉碼的方法。采用這種方法會在程序中到處出現(xiàn)這種語句,特別是在Struts中,Struts有一個回寫表單的功能,在回寫時也要做這種轉換,這樣的語句差不多要多一倍。因此,這是個比較笨拙的方法,有沒有簡捷一些的方法呢?其實是有的,只要在取得request的字符串前加上request.setCharacterEncoding("GBK");這句,指定request的字符集。則中的a就能正常顯示,a1反而不能正常顯示。此時要將byte b[]=a.getBytes("ISO8859-1");中的ISO8859-1變成GBK,從byte到char的轉換才是正確的,這就是此時a能正常顯示而a1反而不能正常顯示的原因。如果此時要a1正常顯示則必須將String a1=new String(a.getBytes("ISO8859-1"),"GBK");中的ISO8859-1改為GBK。
很顯然,使用request.setCharacterEncoding("GBK");只能解決GBK字符問題,要解決i18n問題則要使用UTF-8來取代GBK。我們接著做上述實驗,將a_gbk.jsp和b_gbk.jsp分別另存為a.jsp和b.jsp將文件中的GBK改為UTF-8,更改后的代碼分別如下:
a.jsp代碼:
lt;%@ page contentType="text/html; charset=UTF-8" language="java" import="java.sql.*" errorPage="" %>
<table width="611" border="0" align="center" cellpadding="0" cellspacing="0">
<tr>
<td> </td>
<td class="bigword"> </td>
<td> </td>
</tr>
<tr>
<td width="100"> </td>
<td class="bigword">Input</td>
<td width="100"> </td>
</tr>
<tr>
<td> </td>
<td class="bigword"> </td>
<td> </td>
</tr>
</table>
<table width="611" border="0" align="center" cellpadding="0" cellspacing="0">
<tr>
<td><form method="post" action="b.jsp">
<table width="611" border="0" cellpadding="0" cellspacing="0">
<tr>
<td width="100" align="right"></td>
<td><input name="ClsID" type="text" class="word" id="ClsID" maxlength="2" >
*</td>
</tr>
<tr>
<td width="100" align="right"> </td>
<td><input name="btn" type="submit" value="OK">
</td>
</tr>
</table>
</form></td>
</tr>
</table>
b.jsp代碼:
<ccid_nobr>
<table width="400" border="1" cellspacing="0" cellpadding="2"
bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center">
<tr>
<td bgcolor="e6e6e6" class="code" style="font-size:9pt">
<pre><ccid_code> <%@ page contentType="text/html; charset=UTF-8" import="sun.io.*,java.util.*" %>
<%
request.setCharacterEncoding("UTF-8");
String a=(String)request.getParameter("ClsID");
byte b[]=a.getBytes("UTF-8");
for(int j=0;j<b.length;j++){
out.println(Integer.toHexString(b[j])+"<br>");
}
ByteToCharConverter convertor=ByteToCharConverter.getConverter("UTF-8");
char[] c=convertor.convertAll(b);
out.println("b length:"+b.length+"<br>");
out.println("c length:"+c.length+"<br>");
for(int i=0;i<c.length;i++){
out.println(Integer.toHexString(c[i])+"<br>");
}
String a1=new String(a.getBytes("UTF-8"),"UTF-8");
%>
<%="a是:"+a%><br>
<%="a1是:"+a1%> |
再在a.jsp中輸入"你"字,你會發(fā)現(xiàn)顯示結果中,一個漢字是用三個byte表示的,它們的值分別是0xe4、0xbd、0xa0,也就是說用UTF-8來表示漢字,每個漢字要比GBK多占用一個byte,這也是使用UTF-8要多付出的一點代價吧。
現(xiàn)在,我們討論一下第三個問題,即顯示亂碼,有些莫名其妙的漢字并不是你預期的結果。
在上例中將String a1=new String(a.getBytes("UTF-8"),"UTF-8");改為String a1=new String(a.getBytes("UTF-8"),"GBK");再輸入"你"字,則a1會顯示成"浣?",您只要看一看"浣"的UTF-8碼和GBK碼就會知道其中的奧秘了。
下面,我們討論一下最后一個問題,就是讀寫數(shù)據(jù)庫時出現(xiàn)亂碼。
現(xiàn)在一些常用的數(shù)據(jù)庫都支持數(shù)據(jù)庫encoding,也就是說在創(chuàng)建數(shù)據(jù)庫時可以指定它自己的字符集設置,數(shù)據(jù)庫數(shù)據(jù)以指定的編碼形式存儲。當應用程序訪問數(shù)據(jù)庫時,在入口和出口處都會有encoding轉換。如果,在應用程序中字符本來已變成了亂碼,當然也就無法正確地轉換為數(shù)據(jù)庫的字符集了。數(shù)據(jù)庫的encoding可根據(jù)需要來設置,比如要支持簡、繁體中文、日、韓、英語選GBK,如果還要支持其他語言最好選UTF-8。
本篇文章對字符集及中文亂碼問題做了一下探討,為實現(xiàn)國際化編程的實踐打下一個基礎。下一篇文章,我們將介紹struts中實現(xiàn)國際化編程的具體步驟,并將我們前面介紹的登錄例子進行國際化。
下面,我們就一步步按照上面所說的步驟來完成我們的應用程序:
第一步,我們的應用程序的Views部分包含兩個.jsp頁面:一個是登錄頁面logon.jsp,另一個是用戶登錄成功后的用戶功能頁main.jsp,暫時這個頁面只是個簡單的歡迎頁面。
其中,logon.jsp的代碼清單如下:
lt;%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<HTML>
<HEAD>
<TITLE><bean:message key="logon.jsp.title"/></TITLE>
<html:base/>
</HEAD>
<BODY>
<h3><bean:message key="logon.jsp.page.heading"/></h3>
<html:errors/>
<html:form action="/logonAction.do" focus="username">
<TABLE border="0" width="100%">
<TR>
<TH align="right"><bean:message key="logon.jsp.prompt.username"/></TH>
<TD align="left"><html:text property="username"/></TD>
</TR>
<TR>
<TH align="right"><bean:message key="logon.jsp.prompt.password"/></TH>
<TD align="left"><html:password property="password"/></TD>
</TR>
<TR>
<TD align="right">
<html:submit><bean:message key="logon.jsp.prompt.submit"/></html:submit>
</TD>
<TD align="left">
<html:reset><bean:message key="logon.jsp.prompt.reset"/></html:reset>
</TD>
</TR>
</TABLE>
</html:form>
</BODY>
</HTML> |
main.jsp的代碼清單如下:
lt;%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<HTML>
<HEAD>
<TITLE><bean:message key="main.jsp.title"/></TITLE>
<html:base/>
</HEAD>
<BODY>
<logic:present name="userInfoForm">
<H3>
<bean:message key="main.jsp.welcome"/>
<bean:write name="userInfoForm" property="username"/>!
</H3>
</logic:present>
</BODY>
</HTML> |
首先,我們看一下logon.jsp文件,會發(fā)現(xiàn)它有這么兩個鮮明的特點:一是文件頭部有諸如:
這樣的指令代碼,他們的作用就是指示頁面要用到struts的自定義標簽,標簽庫uri是一個邏輯引用,標簽庫的描述符(tld)的位置在web.xml文件中給出,見上篇文章的配置部分。struts的標簽庫主要由四組標簽組成,它們分別是:
posted on 2006-07-13 11:06
SIMONE 閱讀(908)
評論(0) 編輯 收藏 所屬分類:
JAVA 、
JSP