第2段. 用戶接口
介紹
有一個現實:你盡管可以架構最純粹、最優雅和最強壯的Web應用,但是如果用戶不喜歡其接口的樣子,你便注定要失敗。有些Java 開發人員考慮到這些問題,并使用普通的HTML 和JavaScript技術來解決這些問題。不管你是否喜歡,這些技術,特別是HTML的知識會使得一切在涉及表現和可用性的時候變得完全不同。如果你不知道如何使用它們,你的應用便面臨如何被用戶去接受的風險和挑戰。
這一部分將介紹一些有用的技巧和招數來解決大多數應用表現的難題。這里并不是說就不需要優秀的圖形設計和用戶接口設計人員。然而,通過Struts的動態能力來利用HTML的優勢畢竟還有一大段距離。另外,這一部分將提供一種基于補充技術的解決方案,比如使用JSTL。
一些情形,比如使用HTML表單也非常麻煩。比如,Checkboxes 因為其對unchecked 控件狀態的處理讓人無休止的頭疼。這部分就包括了一個專門處理這個問題的技術。表單處理中另一個通用的問題就是如何處理日期字段。有很多方式,但都有其優缺點。本部分也包含有一個對這些方式的比較。
其中保包括如何設置表單中的tab 次序,產生用于JavaScript中的URL,以及使用框架幀(frame)。總之,如果你對Struts的UI有問題,這里就是解決他們的一個好地方。
Java 平臺持久性機制的比較
訪問持久性數據是所有應用都需要考慮的一個重要方面。Java 語言嚴格的遵循了持久性數據和暫態性數據的分離,將持久性管理留給了應用或者分層機制。SUN使用一種嚴格的比較機制測試和比較了Java平臺上的各種革新的持久性機制。
通過選擇,選出了一個有代表意義的幾個系統來進行評價和比較。這些機制都通過OO7J的基準測試進行量化分析。
所有的測試環境都具有足夠的內存,使得測試數據庫完全可以在內存中駐留,排除磁盤訪問等硬件的影響,也是今天主要服務環境的通用內存容量配置。OO7J的虛擬機變體設置性能基線,每一種機制都和它進行比較。OPJ的PJama實現得到了總體最高分。它只需要最小的源代碼修改,甚至對OO7J的主體基本不需要,雖然它僅是一個原型實現,卻取得了最好的性能。但是它的確需要一個定制的虛擬機。
EJB 框架看起來最糟,需要最多的源代碼修改和最壞的性能和可伸縮性。
JOS 和 JBP,雖然因為它們缺乏增量訪問從而使得在讀寫數據的時候性能也很糟糕,但是,他也僅需要很少的代碼修改并能在數據在內存中的情況下全速運行。
JDBC 對源代碼修改也有較大影響,但是性能卻是在JVM和數據庫的邊緣交叉處的大量交互體現的很好。兩個JDO 的變體則展現出JDO模型對不同外部數據存儲的多能性。特別是JDO-TP 和其事務對象模型對關系數據庫工作的很好,同時允許源代碼對基線保持最小的變更。并且, JDO 的性能也排在前列,一旦數據在內存中,這是合情合理的,比JDBC 和 EJB而言也是如此。
雖然EJB CMP 的性能要優于EJB BMP,其性能也差于JDO-TP很多。EJB性能差的原因還不清楚,也應該注意到,一些應用服務企業有很差的性能,可能是容器實現的原因。
作為結論,很顯然,持久性對應用代碼的影響是很寬的,也對結果系統的性能影響很大。. 如果在虛擬機一級得到支持,OPJ可望提供最有競爭力的性能。在另一個極端,EJB似乎很難達到可接受的性能水平,除非應用對象模型發生戲劇性的變化,從而避免在映射到EJB時產生的細粒度對象。雖然這些方法已經反映在標準的EJB設計模式中了,但是其暗示著在應用程序的各個方面均要附加更多額外的努力。相反,JDO 試圖在對應用設計的影響最小的情況下達到合理的性能,同時保持對外部數據源的中立。因此,當前,JDO 似乎能夠提供面向對象應用所需的最佳的總體持久性機制。但是,目前,SUN建議了一個最新的實體Bean持久性規范 [Sun04],提交給 EJB3.0,它可能更加接近于JDO 模型。
報告的原文可以在以下地址下載:http://research.sun.com/techrep/2004/smli_tr-2004-136.pdf
同時,這也是SUN首次公開披露EJB的性能問題。這也解釋了為什么EJB一直得不到很好的廣泛使用的原因,淡然還有它的復雜性和部署的高成本性(需要昂貴的EJB容器)。作為J2EE規范中的一個重要的核心組件,SUN的賭注押在了EJB3.0上面。EJB3.0相關的持久性機制(JSR220)吸收了JDO和oracle的TopLink的優點,并且可望提供向后兼容性和遷移API。
雖然,EJB從其規范和目標來說看上去很美。但是EJB3.0至少要今年晚些時候才能出場,目前我們能夠使用什么呢?考察目前市面上的持久性框架,結合你的應用要求,也不難得出選擇。畢竟,應用來說,性能并不是第一位的。
對于持久性框架的選擇,從應用和功能上講,主要考慮以下方面:
- 對O-R mapping框架的使用經驗。
- 數據源(Data Source),必須支持不同的數據源,包括關系數據庫和JCA體系。
- 最好提供圖形化的映射工具來進行對象和數據庫表之間的映射,自動生成XML配置文件。
- 數據庫支持,可以利用數據庫的優勢,如存儲過程,觸發器,支持高級數據類型和數據庫安全。
- 查詢支持,應該支持通過Java代碼或者OO查詢機制,如EJB QL,按例查詢,標準SQL 來編寫自己的查詢。
- 鎖定。必須支持不同應用訪問同一數據庫時候的鎖定機制。
- 緩存。提供有效的緩存基址,減少網絡和查詢的開銷。 并支持不同節點的集群。
- 支持到EJB 3.0的遷移。
目前,JBOSS已經在AS 4.0中提供了EJB3.0的早期實現。可以參考。http://www.jboss.org/products/ejb3
如果你不怕在Oracle數據庫平臺上鎖定,你打可使用Toplink,這也是目前性能最好的POJO的持久性框架。Oracle也是EJB3.0的專家組,將來肯定會提供遷移的手段。 你可以閱讀這篇文章:Preparing for EJB3.0. 地址:http://www.oracle.com/technology/tech/java/newsletter/articles/toplink/preparing_for_ejb_3.html
注:
JOS
JOS,稱為Java對象序列化,是JDK 1.1 引入的 [Sun99],它是一種支持幾乎所有對象到流的編碼和從流解碼的機制。他不是基于屬性表的編碼,而是基于文本的,對象序列使用標準的二進制編碼格式。JOS 是Java平臺默認的持久性機制,也被用來作為 JAVA遠程方法調用RMI的參數編組協議。在最低層次上,JOS 構成了對基礎類型的編碼和解碼的平臺支持。但是,序列化遇到了類演化的嚴重問題,這也使得在Java1.4中引入了“JavaBean的長期持久化”的新系統。
JDBC
JDK 1.1 也引入了Java數據庫連接(JDBC) API [FEB03]作為使用標準的SQL語言在Java和關系數據庫之間的通信機制。JDBC為Java 向關系數據庫提供了一個強有力的跳板,并且被平臺上的很多API實現證明很成功。設計者很成功地將Java 編程語言和SQL進行了嫁接,而且JDBC 對直接的數據庫訪問非常容易使用。然而,如果應用需要底層數據的對象視圖的時候,比如,將主鍵轉換成相等的對象間的引用關系,程序將便得非常復雜和容易出錯。為了解決這個問題,有許多系統開發出來試圖自動化這個流程,這通常稱為是對象關系映射(O-R Mapping)。大多數開發環境都提供對這一機制的不同程度的支持。 JDBC API 也在發展,近來支持直接將Java語言中的一個類映射到SQL的面向對象擴展中的相等類型。
JDO
在JDBC 開發的同時,對象數據庫管理組 (ODMG)定義了一個它們的對象模型到Java語言的綁定[Ced96],而許多對象數據庫廠商則提供了這種綁定的實現。因為對象數據庫并不像關系數據庫那樣部署普遍,因此反映在這個綁定上面也是不怎么成功。Java社區流程(JCP)出現后,這一努力被一個更廣泛的建議所替代,即Java數據對象 (JDO) [JR03]。JDO 的目標是針對各種各樣的底層數據存儲,包括關系數據庫,并向開發人員呈現一個類似于LJS所定義的對象模型,但附加限制更少。而許多對象數據庫廠商也支持JDO。
OPJ
綜合這些努力,SUN和 Glasgow 大學合作,提出了一個Java平臺的正交持久化機制 (OPJ) [JA00]。這一方法以極高的透明性支持變成語言的各個方面。從本質上講,OPJ 添加了對JVM穩定內存的支持,也意味著這一對JVM 實現的要求根本上限制了其可接受性。
第2.8式. 有選擇地禁止Action
問題
你想要是使用一個定制屬性來禁止(disable)一個,并且該屬性能夠在struts-config.xml文件的action元素中進行設置;轉發到該disabled action 的任何請求都會得到"under construction" 頁面。
動作要領
創建一個定制的ActionMapping擴展(如Example 2-16) ,它可以提供一個boolean 類型的屬性來指示action 是否被禁止。
Example 2-16. 定制ActionMapping
import org.apache.struts.action.ActionMapping;


public class DisablingActionMapping extends ActionMapping
{

private String disabled;
private boolean actionDisabled = false;

public String getDisabled( )
{
return disabled;
}


public void setDisabled(String disabled)
{
this.disabled = disabled;
actionDisabled = new Boolean(disabled).booleanValue( );
}

public boolean isActionDisabled( )
{
return actionDisabled;
}
}

這個action mapping 就可以在struts-config.xml文件中指定。如果你想要一個action被禁止,你可以設置disabled屬性為True :
<action-mappings type="com.oreilly.strutsckbk.DisablingActionMapping">

<!-- Edit mail subscription -->
<action path="/editSubscription"
type="org.apache.struts.webapp.example.EditSubscriptionAction"
attribute="subscriptionForm"
scope="request"
validate="false">
<set-property property="disabled" value="true"/>
<forward name="failure" path="/mainMenu.jsp"/>
<forward name="success" path="/subscription.jsp"/>
</action>

然后創建一個定制的RequestProcessor,比如Example 2-17中的那個,它可以處理DisablingActionMapping.
Example 2-17. 處理對被禁止的actions的請求
import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.RequestProcessor;


public class CustomRequestProcessor extends RequestProcessor
{
protected ActionForward processActionPerform(HttpServletRequest request,
HttpServletResponse response, Action action,ActionForm form,

ActionMapping mapping) throws IOException, ServletException
{
ActionForward forward = null;

if (!(mapping instanceof DisablingActionMapping))
{
forward = super.processActionPerform( request, response,
action, form, mapping);
}

else
{
DisablingActionMapping customMapping =
(DisablingActionMapping) mapping;

if (customMapping.isActionDisabled( ))
{
forward = customMapping.findForward("underConstruction");
}

else
{
forward = super.processActionPerform( request, response,
action, form, mapping);
}
}
return forward;
}
}

動作變化
Struts 通過兩種機制來對action提供定制屬性的能力。
首先,每個Struts action 都可以通過一個通用參數parameter值來傳遞:
<action path="/editRegistration"
type="org.apache.struts.webapp.example.EditRegistrationAction"
attribute="registrationForm"
scope="request"
validate="false"
parameter="disabled">
<forward name="success" path="/registration.jsp"/>
</action>

其次,在Action的實現中,parameter的值可以通過下面的代碼來訪問:
String parameterValue = mapping.getParameter( );
然而,某些Struts所提供的子類,比如DispatchAction 已經使用了parameter屬性。因為你只可以指定一個parameter屬性,所以,如果你使用這些預定義的Action子類,便不能再將parameter用作定制屬性值。
對于完整的擴展,你可以擴展ActionMapping類,可選地為你所選擇的定制屬性提供accessor 和 mutator :
package com.oreilly.strutsckbk;

import org.apache.struts.ActionMapping


public class MyCustomActionMapping extends ActionMapping
{
private String customValue;

public String getCustomValue( )
{ return customValue; }

public String setCustomValue(String s)
{ customValue = s; }
}


你可以在struts-config.xml文件中引用這個擴展。如果定制action mapping 將被用于所有action,請將action-mappings元素的type屬性設置為定制擴展的全限定類名:
<action-mappings type="com.oreilly.strutsckbk.MyCustomActionMapping">
否則,為定制action mapping所需的action元素設置className屬性。這兩種情況下,set-property元素都可以用來針對特定的action元素為定制擴展中的JavaBean 屬性設置值:
<action path="/someAction"
type="com.oreilly.strutsckbk.SomeAction"
className="com.oreilly.strutsckbk.MyCustomActionMapping">
<set-property property="customValue" value="some value"/>
</action>

這種方案使用一個定制的RequestProcessor來處理定制ActionMapping的disabled 屬性。如果你對特定的action使用定制的ActionMapping,你可以在Action.execute()訪法中直接訪問定值ActionMapping的屬性:
boolean disabled = ((DisablingActionMapping) mapping).isActionDisabled( );
if (disabled) return mapping.findForward("underConstruction");

相關招式
你也可以使用授權(authorization) servlet 過濾器來解決這個問題。那是第11.8式的動作。
Mule 是一個基于ESB架構理念的消息平臺。Mule 的核心是一個基于SEDA的服務容器,該容器管理被稱為通用消息對象(Universal Message Objects /UMO)的服務對象,而這些對象都是POJO。所有UMO和其他應用之間的通信都是通過消息端點(message endpoint)來進行的。這些端點為眾多的分立的技術,比如Jms, Smtp, Jdbc, Tcp, Http, Xmpp, file等等,提供了簡單和一致的接口。
Mule 應用通常是由網絡中的許多Mule 實例組成。每一個實例都是一個駐留一個或者多個UMO組件的輕量級容器。每一個UMO 組件都有一個或者多個通過它(們)發送和接收事件的端點。

容器通過UMO組件提供各種各樣的服務,比如事務管理、事件轉換,路由,事件關聯、日志、審計和管理等等。Mule將對象構造從管理手段中分離出來,通常流行框架和IoC/DI 容器,如Spring, PicoContainer 或者 Plexus 可用這種管理手段來構建你的UMO 組件。
很多人認為, "Mule是一個Jms 實現"。實際上,Mule 不是一個Jms server,但是可以配置來使用任何你覺得非常漂亮的Jms server。Mule 的理念是,如果已經有了穩定和廣泛接受的實現,就不會直接實現任何傳輸。例如,Mule 就重用了Axis 和 GLUE 的SOAP棧而不是重新實現一個。Mule 提供了一個一致的服務集來管理任何類型的連接的事件流、關聯、事務、安全和審計。
下面是Mule Server 組件的簡單圖示:


Mule Manager是Mule server 實例的中心(也稱為一個節點戶或者Mule Node)。其主要的角色是管理各種對象,比如Mule實例的連接器、端點和轉換器。這些對象然后被用來控制進出你的服務組件的消息流,并且為Model和它所管理的組件提供服務。
Model
model 是管理和執行組件的容器。它控制進出組件的消息流,管理線程、生命周期和緩充池。默認的MuleModel是基于SEDA的,它使用一個有效的基于事件的隊列模型來獲取的最大的性能和直通性。
UMO Components
UMO代表Universal Message Object;它是一個可以接收來自于任何地方的消息的對象。UMO 組件就是你的業務對象。它們是執行引入的事件之上的具體業務邏輯的組件。這些組件是標準的JavaBean,組件中并沒有任何Mule特定的代碼。Mule 基于你的組件的配置處理所有進出組件的事件的路由和轉換。
Endpoints
Endpoint是Mule的通信能力的基礎。一個Endpoint定義了兩個或者多個組建應用或者存儲庫之間的通信渠道。并且提供了一個強大的機制來允許你的對象在一個統一的方式上再多種協議之上進行交談。端點可以通過消息過濾器、安全攔截器和事務信息進行配置來控制什么消息,在何時,以及怎樣通過端點進行發送和接收。
External Applications
外部應用可以使任何應用,從應用服務器到遺留的傳統應用,主機程序,或者C/S系統。基本上是任何可以產生和操縱數據的應用。因為Mule通過endpoints執行所有通信,UMO 組件并不打算在其中包含應用產生數據,以及應用主流,以及使用傳輸協議的部分。
關鍵特性
- 基于J2EE 1.4的企業消息總線( Enterprise Service Bus (ESB))和消息代理(broker)
- 可插入性連接,比如Jms (1.0.2b 和 1.1), vm (嵌入), jdbc, tcp, udp, multicast, http, servlet, smtp, pop3, file, xmpp等
- 支持任何傳輸之上的異步,同步和請求響應事件處理機制
- 支持Axis或者Glue的Web Service.
- 靈活的部署結構[Topologies]包括Client/Server, P2P, ESB 和Enterprise Service Network.
- 支持聲明性和編程性事務,包括XA 支持
- 對事件的路由、傳輸和轉換的斷到端支持
- Spring 框架集成。可用作ESB 容器,而Mule c也可以很容易的嵌入到Spring 應用中。
- 使用基于SEDA處理模型的高度可伸縮的企業服務器
- 支持REST API 來提供技術獨立和語言中立的基于web的對Mule 事件的訪問
- 強大的基于EIP模式的事件路由機制
- 動態、聲明性的,基于內容和基于規則的路由選項
- 非入侵式的方式。任何對象都可以通過ESB 容器管理
- 強大的應用集成框架
- 完整的可擴展的開發模式
何時使用
一般在這些情形下使用Mule -
- 集成兩個或者多個需要互相通信的或者多個現有的系統.
- 需要完全和周圍環境去耦合的應用,或者需要在系統中伸縮不止一個組件的系統
- 開發人員不知道未來是否會將其應用分發或者伸縮需求的單VM 應用。
項目地址:http://mule.codehaus.org/
第2.7式. 訪問來自于數據庫中的消息資源
問題
你想要將所有標注、消息和其他靜態文本都保存在數據庫中而不知一個屬性文件中,并且仍然可以通過bean:message標簽來進行訪問。
動作要領
- 下載OJBMessageResources分發包,地址是:http://prdownloads.sourceforge.net/struts/ojb-message-resources.zip?download.
- 將ZIP 文件解包到一個本地目錄;
- 將ojb-msg-res.jar文件從ojb-message-resources/dist文件夾拷貝到你的應用的WEB-INF/lib文件夾。
- 將屬性文件、XML 和DTD 文件從ojb-message-resources/config文件夾拷貝到你的應用的src文件夾。當你build 你的應用的時候是,這些文件必須被拷貝到WEB-INF/classes文件夾中。
- 創建數據庫表來保存對象關系橋(OJB) 的元數據。OJB 使用表來保持內部的映射關系。Example 2-12 列出了在MYSQL中創建這些表的SQL。對于其他數據庫的語句,包含在OJB 分發包中。
Example 2-12. OJB 元數據DDL (MySQL)
CREATE TABLE ojb_dlist (
ID int NOT NULL default '0',
SIZE_ int default NULL,
PRIMARY KEY (ID)
) TYPE=MyISAM;

CREATE TABLE ojb_dlist_entries (
ID int NOT NULL default '0',
DLIST_ID int NOT NULL default '0',
POSITION_ int default NULL,
OID_ longblob,
PRIMARY KEY (ID)
) TYPE=MyISAM;

CREATE TABLE ojb_dmap (
ID int NOT NULL default '0',
SIZE_ int default NULL,
PRIMARY KEY (ID)
) TYPE=MyISAM;

CREATE TABLE ojb_dmap_entries (
ID int NOT NULL default '0',
DMAP_ID int NOT NULL default '0',
KEY_OID longblob,
VALUE_OID longblob,
PRIMARY KEY (ID)
) TYPE=MyISAM;

CREATE TABLE ojb_dset (
ID int NOT NULL default '0',
SIZE_ int default NULL,
PRIMARY KEY (ID)
) TYPE=MyISAM;

CREATE TABLE ojb_dset_entries (
ID int NOT NULL default '0',
DLIST_ID int NOT NULL default '0',
POSITION_ int default NULL,
OID_ longblob,
PRIMARY KEY (ID)
) TYPE=MyISAM;

CREATE TABLE ojb_hl_seq (
TABLENAME varchar(175) NOT NULL default '',
FIELDNAME varchar(70) NOT NULL default '',
MAX_KEY int default NULL,
GRAB_SIZE int default NULL,
PRIMARY KEY (TABLENAME,FIELDNAME)
) TYPE=MyISAM;

CREATE TABLE ojb_lockentry (
OID_ varchar(250) NOT NULL default '',
TX_ID varchar(50) NOT NULL default '',
TIMESTAMP_ decimal(10,0) default NULL,
ISOLATIONLEVEL int default NULL,
LOCKTYPE int default NULL,
PRIMARY KEY (OID_,TX_ID)
) TYPE=MyISAM;

CREATE TABLE ojb_nrm (
NAME varchar(250) NOT NULL default '',
OID_ longblob,
PRIMARY KEY (NAME)
) TYPE=MyISAM;

CREATE TABLE ojb_seq (
TABLENAME varchar(175) NOT NULL default '',
FIELDNAME varchar(70) NOT NULL default '',
LAST_NUM int default NULL,
PRIMARY KEY (TABLENAME,FIELDNAME)
) TYPE=MyISAM;

6. 使用Example 2-13中的SQL DDL來創建保存消息資源數據的表。
Example 2-13. MessageResources DDL
create table application_resources (
subApp varchar(100) not null,
bundleKey varchar(100) not null,
locale varchar(10) not null,
msgKey varchar(255) not null,
val varchar(255),
Primary Key(
subApp,
bundleKey,
locale,
msgKey
)
);

- 將消息資源裝入數據庫表格中。Example 2-14 展示了一個如何使用SQL語句組裝表格數據的簡便方法。
Example 2-14. SQL to load message resources table
insert into application_resources (
subApp, bundleKey, locale, msgKey, val )
values ('', '', '', 'label.index.title',
'Struts Cookbook');
insert into application_resources (
subApp, bundleKey, locale, msgKey, val )
values ('', '', 'fr', 'label.index.title',
'Struts Livre de Cuisine');

修改Struts 配置文件來使用OJBMessageResourcesfactory。
<message-resources
factory="org.apache.struts.util.OJBMessageResourcesFactory"
parameter="."/>

修改WEB-INF/classes文件夾中的repository.xml (第4步中拷貝的) 來使用專門針對你的數據庫的數據庫連接屬性。Example 2-15展示了針對MySQL 數據庫的配置信息。
Example 2-15. 針對MySQL的OJB 連接描述
<jdbc-connection-descriptor
platform="MySQL"
jdbc-level="2.0"
driver="com.mysql.jdbc.Driver"
protocol="jdbc"
subprotocol="mysql"
dbalias="http://localhost:3306/test"
username="user"
password="pass"
/>

動作變化
Struts MessageResources工具管理著諸如錯誤消息、字段標注、表格頭部以及窗口標題等靜態文本。通過這個工具,文本被保存在成為資源束的一個或者多個.properties文件的名值對中;名稱是一個邏輯關鍵字而值則是要顯示文本的值。如果你的應用需要針對某個特定國家和語言進行本地化,你將創建一個(套)新的屬性文件。你可以使用對屬性文件名稱添加一個由國家代碼和語言代碼組成的后綴來將一個文件關聯到一個特定的場所。比如,針對加拿大法語的MessageResources.properties文件可能是MessageResources_fr_CA.properties。本地化文件中的屬性包含有專門針對該場所的值。這種本地化的方式實際上是Java自身決定的。
這個工具對大多數中型和小型的應用工作的很不錯。但是,你可能想要使用一種更加具有可管理性的持久性手段,比如數據庫來保存它。雖然Struts 本身并不支持這種方式,但是可以通過其他擴展來支持。在幕后, Struts 使用MessageResourcesFactory的實現來創建在運行時保存在servlet context中的MessageResources對象。你可以提供定制的Struts MessageResourcesFactory實現,并且在Struts 配置文件中聲明你的實現:
<message-resources
factory="com.foo.CustomMessageResourcesFactory"
parameter="moduleName.bundleKey"/>

parameter 屬性指定消息資源工廠要針對創建的Struts module 名稱和bundle 關鍵字(bundle 名稱) 。
所以,你可以創建你自己的從數據庫中讀取資源的消息資源工廠。幸運的是,這個麻煩的工作已經有人做了。James Mitchell, 一個長期的Struts 志愿者,創建了一個OJBMessageResources實現。這些類利用了對象關系映射框架OJB來提供易用的數據庫驅動的MessageResources實現。
如果你不熟悉OJB,也不會阻止你練習這一招技術。你不需要完全了解OJB來能使用OJBMessageResources。OJB 中僅涉及到將關系數據映射到對象數據的部分內容。如果你使用本方案中指定的table schema ,對于映射數據不需要再作其他修改。然而,如果你想要使用一個不同的schema,你就需要對 OJB 基于XML的配置文件中的映射配置進行修改易適合你的需要。你不需要對實現MessageResourcesFactory的具體Java代碼做任何修改。OJBMessageResources的文檔也不錯,并且有一個逐步的安裝和配置指導的README文件。上面的動作要領就來自于這些指導。
為了最有效的使用OJBMessageResources ,理解數據庫schema 如何映射到對象數據是很有幫助的。首先,schema 金需要創建一個表格來保存消息資源(第6步)。使用一個表簡化了數據映射。Table 2-2 表述了組成該表的列,以及它們如何在Struts中使用。
Table 2-2. OJBMessageResources schema |
列 |
對應的Struts 概念 |
注釋和示例 |
subApp |
模塊前綴 |
非null。
使用一個空字符串("") 來標識默認模塊 |
bundleKey |
在使用多個資源集時,定位某一個消息資源集的關鍵字。這個值必須匹配Struts配置文件中的message-resources元素的key屬性的值。這個值也對應于Struts標簽中的bundle 屬性的值 (如, bean:message bundle="labels") |
非null。使用空字符串來表示默認關鍵字。否則,名稱是一個邏輯值,比如"labels," "headers," 和"errors." |
Locale |
標識消息場所的場所代碼。此值是兩個字母的語言代碼和兩個字母的國家代碼的組合。 |
非null。
使用空字符串來表示默認(服務器的)場所。比如"en_US" 標識美國英語,"fr"標識法語。 |
msgKey |
用于查找消息的消息的名稱。此值對所有場所都是一樣。這個值也是屬性文件中名值對中左邊的部分。此列的值對應于從MessageResources中獲取值的Struts標簽的key屬性的值。 |
非null, 并且應該永不為空。一個關鍵字可能看起來如: "title.hello.world". |
val |
對應于msgKey的值。是對應于屬性文件中名值對右邊部分的本地化值。這也是將由Struts標簽返回和顯示的值。 |
可以為null。這是將在頁面中顯示的文本。比如, "Hello, World!" |
記住,雖然OJBMessageResources 使用一個單表來保存消息資源,每一個資源集都仍然必須在struts-config.xml中通過message-resources元素進行配置。換句話說,對由bundleKey 和 subApp區分的每一個消息資源集都將需要一個message-resource元素。關于多個消息資源,參見第2.6式。
相關招式
第2.6式描述了如何配置Struts 來使用多個消息資源束。第13段的動作中會描述如何進行Struts 應用的國際化。
OJB 是Apache 大傘之下的一個項目。詳細信息清訪問呢:http://db.apache.org/ojb.
有人正在努力將Struts 消息資源重構到一些更加通用的類中。這就是Commons Resources 項目,可以訪問http://jakarta.apache.org/commons/sandbox/resources/獲取相關信息。它可以提供屬性文件支持的消息資源機制,或者其他持久性機制支持的消息資源機制,比如OBJ, Hibernate, Torque, 和直接JDBC。也許將來的Struts將使用這個通用的消息資源機制。