摘要:本文主要介紹了如何利用Frails框架進(jìn)行快速開發(fā)。在本文中我們的目標(biāo)是架構(gòu)起一個Spring+JSF+Hibernate的項目,并且實現(xiàn)后臺代碼。您會發(fā)現(xiàn),原本僅僅實現(xiàn)一個Spring框架就要話很長時間的“痛苦”過程,在Frails框架和模板的幫助下,幾乎喝杯咖啡的功夫就可以搞定了。您甚至?xí)l(fā)現(xiàn),不需要寫任何SQL或者數(shù)據(jù)庫相關(guān)代碼就能夠?qū)崿F(xiàn)幾乎可以覆蓋全部的數(shù)據(jù)庫操作需求。通過本文,您一定會喜歡上在Frails框架幫助下快速開發(fā)的感覺。


Frails簡介
Frails是SourceForge.net上的一個開源項目,其項目管理開發(fā)是由華中科技大學(xué)IBM俱樂部JAVA組與軟件學(xué)院學(xué)生合作完成。Frails框架誕生的目的就是幫助開發(fā)者快速開發(fā)J2EE項目。更多基本信息您可以查看http://www.sourceforge.net/projects/frails(英文),http://frails.hexiao.cn/(中文)。

我們現(xiàn)在假設(shè)您對現(xiàn)在主流的J2EE框架Spring,JSF,Hibernate有所了解。那么您會發(fā)現(xiàn)這些框架的配置與實現(xiàn)是相當(dāng)復(fù)雜的,然而相當(dāng)多的情況下我們不需要用到其中高靈活性的配置。就拿JSF來說,JSF的頁面導(dǎo)航配置實際上是一種沒有必要的靈活,實際上我們更喜歡的將A頁面導(dǎo)航到B頁面的規(guī)則簡單的約定,而不是每個都要手動配置;驗證過程也是很麻煩的,相對于將驗證代碼零星的嵌入到頁面邏輯中,我們更希望看到的是有統(tǒng)一的驗證中心,這樣更符合軟件工程的原則;對于數(shù)據(jù)庫操作來說,通常我們的操作都是增刪查改四種操作,每個操作都會有很多重復(fù)的“垃圾”代碼,這也阻礙了快速開發(fā)的目標(biāo);Spring更方便的注射,也能夠提高開發(fā)速度。上述問題在Frails中有良好的解決,并且保持了與原始框架一樣的高靈活可配置性,甚至提高了靈活性,或者是提供了一種更優(yōu)秀的解決方案。這里要強調(diào)的是,F(xiàn)rails不是其他框架的一個簡化版本,它只是通過開發(fā)者和框架之間的一種約定或者是“默契”,代辦了其他框架累贅、重復(fù)的工作。如果你想要高靈活的配置,F(xiàn)rails同樣適合。

動手實踐
下面我們將展示一個建立項目的過程。這里的項目需求是實現(xiàn)一個簡單的留言簿,目的就是通過最簡單的例子最大化的覆蓋到Frails的特性。

搭建項目
首先要有個IDE比較好,我使用的是Eclipse與WTP插件,這樣開發(fā)起來會很方便,當(dāng)然你要是不想用IDE或者用別的也一樣可以,F(xiàn)rails是和IDE無關(guān)的。

最方便的是Frails為一般的項目提供了一個模板FrailsTemplate(這個模板在2.0包里的Samples下可以找到)。這個模板已經(jīng)包含了Spring+Hibernate+JSF的全部配置。我們僅僅需要對這個模板進(jìn)行做些適合自己的修改就可以快速的搭建起一個項目來了。

我們新建一個WEB項目,將FrailsTemplate的WEB-INF拷貝到自己的項目中來,并且在scr中建立四個包,分別為actions,domain,dao,domain.entities。這樣基本上一個項目的結(jié)構(gòu)就我們接下來介紹這些行為的意義:

WEB-INF/lib目錄下是整個項目需要的所有JAR包。其中Frails需要的JAR就是frails4jsf1.1.jar。如果你沒有別的什么特殊需求,例如要使用Icefaces的JSF組件,那么這個lib的JAR文件足夠你開發(fā)了。你看看里面還包括了tomahawk-1.1.3.jar呢。但是里面沒有包含facelet,如果你想用facelet的話直接添加進(jìn)來就可以了。如果你僅僅想急著些個JSF頁面完成老板的任務(wù),那么你這些東西都不用管了,直接考過來就是了。

WEB-INF下的幾個配置文件可能需要改動:

Jdbc.properties
Jdbc.properties里面包含的是你的數(shù)據(jù)庫配置。實際上applicationContext.xml里數(shù)據(jù)庫有關(guān)的配置都關(guān)聯(lián)到此。

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=GBK
jdbc.username=root
jdbc.password=password

jdbc.driverClassName對應(yīng)的是數(shù)據(jù)庫驅(qū)動。正對不同的數(shù)據(jù)庫填寫不同的驅(qū)動就可以了。這里的是MySQL。
Jdbc.url是數(shù)據(jù)庫鏈接地址,一般不需要更改。但是你要用別的數(shù)據(jù)庫還是得改成相應(yīng)的數(shù)據(jù)庫URL。最后的characterEncoding是數(shù)據(jù)庫編碼,這里選用的是GBK,你也可以根據(jù)你的需要設(shè)置為UTF-8或者別的。
Jdbc.username是數(shù)據(jù)庫用戶名,需要根據(jù)你自己的設(shè)置更改。
Jdbc.password是數(shù)據(jù)庫密碼,需要根據(jù)你的設(shè)置更改。

applicationContext.xml
基本的應(yīng)用上下文配置,一般不需要修改。

faces-config.xml
由于Frails框架的原因,原本復(fù)雜的faces-config.xml你現(xiàn)在可以一個字不用寫。

frails-config.xml
Frails框架配置。簡單的來說你可以不做任何修改。Frails將根據(jù)這里的配置(如果沒有的話就是默認(rèn)配置),識別項目中的ManagedBeans,Validators,Converters,頁面,資源目錄等。具體說明可以在Frails項目包中的Frails User Guide中的Configrations找到相應(yīng)的說明。這里簡單的認(rèn)為什么都不寫就好了,我們等下按照一個默認(rèn)配置的規(guī)范來繼續(xù)我們的項目開發(fā)就OK了。

hibernate.cfg.xml
Hibernate配置。這個配置是必須修改的。至少我們在每寫一個Hibernate實體的時候,就應(yīng)該在SessionFactory里添加一條<mapping class="domain.entities.ClassName"/> class里面對應(yīng)了一個實體的類名。我們等下也會提到。
如果你并不是使用MySQL數(shù)據(jù)庫的話,需要把數(shù)據(jù)庫的別名修改成你數(shù)據(jù)庫的相應(yīng)設(shè)置:<propertyname="dialect">org.hibernate.dialect.MySQLDialect</property>。
<property name="hibernate.hbm2ddl.auto">update</property>是說每次啟動Hibernate都會自動檢查,如果原來數(shù)據(jù)庫中沒有表則建立新的表,如果有了則更新原有表。

spring-beans.xml
Spring框架配置。這個配置是必須修改的。這里主要是配置所有的dao,Spring按照這個配置進(jìn)行依賴注入。不要擔(dān)心,我們的配置也之是寫點點東西,等下詳細(xì)說明。

現(xiàn)在項目的基本配置就完成了,雖然還有些要根據(jù)實際的設(shè)計實現(xiàn)增加些配置,但是大體的工作已經(jīng)完成了。您看,根本就沒有花很多時間。下面讓我們快速進(jìn)入到實際的開發(fā)實現(xiàn)過程中來。


實現(xiàn)一個項目

現(xiàn)在該輪到解釋剛才建立的四個包了(當(dāng)然了,你也完全可以不這么做,包名只是對類的一種功能上的分類,完全可以根據(jù)自己的需要更改為其他的名字):actions包里面包含的是所有的ManagedBeans;dao里面包含所有的數(shù)據(jù)庫相關(guān)業(yè)務(wù)接口;domain包里是數(shù)據(jù)庫業(yè)務(wù)實現(xiàn);domain.entities包里是所有的實體類。下面結(jié)合我們的例子來介紹。
例子:我們要實現(xiàn)一個留言簿,notebook.jsp頁面顯示出留言列表,并在下方顯示一個用戶名和內(nèi)容輸入框,點擊提交后將留言儲存到數(shù)據(jù)庫里。

后臺(業(yè)務(wù)邏輯)代碼設(shè)計與實現(xiàn)

1 建立實體
我們設(shè)計是每條留言記錄對應(yīng)一個實體:在包domain.entitis下建立Note.java,并且按照Hibernate的Annotation規(guī)范編寫,使得Note.java這個類會按照Hibernate的方式映射到數(shù)據(jù)庫中。(關(guān)于Hibernate相關(guān)內(nèi)容請查看Hibernate相關(guān)文檔,這里不再贅述),代碼如下:

package ?domain.entities;

import ?javax.persistence.Entity;
import ?javax.persistence.GeneratedValue;
import ?javax.persistence.GenerationType;
import ?javax.persistence.Id;

@Entity
public ? class ?Note? {
????
????
/**
?????*?要數(shù)據(jù)庫為每個留言建立一個ID
?????
*/

????@Id
????@GeneratedValue(strategy
= GenerationType.AUTO)
????
long ?id;
????
????
/**
?????*?留言者姓名
?????
*/

????String?poster;
????
????
/**
?????*?留言內(nèi)容
?????
*/

????String?content;

????
public ?String?getContent()? {
????????
return ?content;
????}


????
public ? void ?setContent(String?content)? {
????????
this .content? = ?content;
????}


????
public ? long ?getId()? {
????????
return ?id;
????}


????
public ? void ?setId( long ?id)? {
????????
this .id? = ?id;
????}


????
public ?String?getPoster()? {
????????
return ?poster;
????}


????
public ? void ?setPoster(String?poster)? {
????????
this .poster? = ?poster;
????}


}

同時,不要忘了,你應(yīng)該在hibernate.cfg.xml的<session-factory>里加上對本實體映射聲明<mapping class="domain.entities.Note"/>。

2實體的數(shù)據(jù)庫操作接口

在dao包內(nèi)設(shè)計出所有實體對飲的數(shù)據(jù)庫業(yè)務(wù)接口,最佳實踐是,每個實體對應(yīng)一個接口,并且使用ClassnameDao的形式命名。所以我們這里的為NoteDao。所則上我們應(yīng)該在本接口中聲明所有的和Note類相關(guān)的數(shù)據(jù)庫方法,但是就像前面提到過了的,不同實體的數(shù)據(jù)庫業(yè)務(wù)基本上都是增、刪、查、該,無非是在操作的對象上有不同而已。所以在實現(xiàn)的階段,我們要實現(xiàn)每個接口,將是非常累贅的事情——反復(fù)的寫例如開關(guān)數(shù)據(jù)庫的“垃圾”代碼。好在我們有了Java 5的泛型,以及Frails的GenericDao<T>,我們可以省了好多事情。GenericDao基本上已經(jīng)幫我們聲明了所有可能要用到的數(shù)據(jù)庫方法,一般情況下我們僅僅之需要讓實體的Dao繼承自此就可以了,不用寫任何代碼。記得繼承的時候要聲明泛型的類型。所以NoteDao接口代碼如下:

package?dao;

import?net.sf.frails.hibernate.GenericDao;
import?domain.entities.Note;

public?interface?NoteDao?extends?GenericDao<Note>?{

}

3 實現(xiàn)實體數(shù)據(jù)庫操作接口

下面是實現(xiàn)每個dao了。你可能會說,盡管本例子中只有一個dao,但實際的項目中實體的數(shù)目將會很多,并且每個實體內(nèi)都有不少的方法要實現(xiàn),這樣一個個的寫代碼將是多大的負(fù)擔(dān)啊!幸運的是,再一次Frails解決了這個問題,同樣,我們讓所有的實現(xiàn)類繼承自一個支持泛型的GenericDaoSupport<T>。

GenericDaoSupport實現(xiàn)了GenericDao<T>接口,已經(jīng)實現(xiàn)了幾乎所有可能要用到的數(shù)據(jù)庫方法。NoteDaoImp代碼如下,我們還是什么都沒有寫:

package?dao;

import?java.util.List;
import?net.sf.frails.hibernate.GenericDaoSupport;
import?domain.entities.Note;

public?class?NoteDaoImp?extends?GenericDaoSupport<Note>?implements?NoteDao{
}

這個時候我們就要修改spring-beans.xml里的配置了。在<Beans>里為每個daoImp加入一個聲明<bean id="noteDao" parent="dao" class="dao.NoteDaoImpl"></bean>

這個地方parent屬性指的是在applicationContext.xml里幫助Dao對象得到hibernate的Session的抽象Bean 的名字。
并且在<bean id="servicesImp" class="domain.ServicesImp">內(nèi)為每個dao加入屬性:<property name="noteDao" ref="noteDao"></property>。這里ServicesImp就是下面要提到的業(yè)務(wù)邏輯實現(xiàn)。所以整個的<Beans>的結(jié)構(gòu)如下:

<beans>????
????
<bean?id="dao"?abstract="true">????
????????
<property?name="sessionFactory"?ref="sessionFactory"/>
????
</bean>
????
<bean?id="noteDao"?parent="dao"?class="dao.NoteDaoImp">
????
</bean>
????
<bean?id="services"?parent="transactionProxy">
????????
<property?name="target"?ref="servicesImp"/>
????????
<property?name="transactionAttributes">
????????????
<props>????????????????
????????????????
<prop?key="*">PROPAGATION_REQUIRED</prop>
????????????
</props>
????????
</property>
????
</bean>
????
<bean?id="servicesImp"?class="domain.ServicesImp">
????????
<property?name="noteDao"?ref="noteDao"></property>
????
</bean>
</beans>

4 提供統(tǒng)一的業(yè)務(wù)邏輯接口

接下來我們要將所有的業(yè)務(wù)邏輯包裝起來。在一個接口中聲明所有對外提供的業(yè)務(wù)邏輯方法,這里我們起名叫做Services。這樣做的目的是統(tǒng)一對外提供業(yè)務(wù)邏輯,當(dāng)我們設(shè)計頁面邏輯的時候就可以完全不用關(guān)心業(yè)務(wù)邏輯實現(xiàn)了,所以頁面設(shè)計人員和后臺設(shè)計人員可以分離,各自僅僅關(guān)心自己的領(lǐng)域顯然有助于高效的開發(fā)。再者,如果我們需要更改業(yè)務(wù)邏輯方法的話,僅僅修改這個接口和其實現(xiàn)就可以了,使得代碼有很高的維護(hù)性。代碼如下:

package?domain;
import?java.util.List;
import?domain.entities.Note;

public?interface?Services?{
????
????
public?void?saveNote(Note?note);
????
????
public?List<Note>?getAllNotes();

}

5 實現(xiàn)統(tǒng)一數(shù)業(yè)務(wù)邏輯接口

當(dāng)然了,我們現(xiàn)在就要實現(xiàn)的Services里聲明的所有數(shù)據(jù)庫方法:在domain包的ServiceImp,這里才是要真正寫代碼的地方(別急,每個方法不超過兩行就搞定了)。這個類有所有dao的引用,Spring會依照上面在spring-beans.xml的配置注入所有引用的實例,實際上我們調(diào)用的是GenericSupport的方法,而這些方法基本上覆蓋了所有的業(yè)務(wù)邏輯需求。通過繼承GenericDaoSupport<T>的方法如下,足夠你用了,萬一不行使用里面的HQL的查詢支持的方法吧。

我們這個例子的ServicesImp代碼如下,這樣看來我們?nèi)匀粵]有寫什么代碼啊:

package?domain;
import?java.util.List;
import?dao.NoteDao;
import?domain.entities.Note;

public?class?ServicesImp?implements?Services{
????NoteDao?noteDao;

????
public?List<Note>?getAllNotes()?{
????????
return?noteDao.listAll();
????}


????
public?void?saveNote(Note?note)?{
????????noteDao.save(note);
????}


}


這樣,我們的后臺代碼,也就是業(yè)務(wù)邏輯代碼已經(jīng)編寫完畢了。下面我們開始寫前臺。


前臺(頁面)邏輯代碼實現(xiàn)

1 頁面與ManagedBean

我們在actions包里添加一個叫做NotebookAction的類,這個類自動的將成為notebook.jsp(等下建立)頁面的ManagedBean,負(fù)責(zé)處理本頁面的邏輯。根據(jù)Frails的默認(rèn)配置,在actions包下的以頁面名字開頭(首字母大寫)加上Action結(jié)尾的類將自動成為該頁面的ManagedBean。如果你不喜歡這個后綴(Action),你也可以根據(jù)自己的需要該成別的,方法是在frails-config.xml的<frails-config>里加上:

<mbean-package>actions</mbean-package>這里是對應(yīng)的ManagedBean所在的包名稱。
<mbean-suffix>Action</mbean-suffix>這里是所有ManagedBean的后綴。

按照Frails 2.0的規(guī)范,我們還應(yīng)該在相應(yīng)的Bean上顯示的標(biāo)注些annotation。具體代碼如下:

package?action;

import?net.sf.frails.bean.annotations.DefMbean;
import?net.sf.frails.bean.annotations.ScopeType;

@DefMbean(scope
=ScopeType.REQUEST)
public?class?NotebookAction?{

}

您所要做的就是在類聲明的前面加上@DefMbean(scope=ScopeType.REQUEST)。其中scope是Bean的范圍,默認(rèn)的是REQUEST。每個頁面的ManagedBean都應(yīng)該盡量使用REQUEST范圍,這樣做是有很大好處的,我們將在最后一個部分具體介紹。在給這個Bean里添加頁面所需要的屬性和方法。我們讓所有的ManagedBean繼承自一個ServicesAction,在ServicesAction里包含了對Services的聲明。按照Frials框架,在Services聲明前注明@SpringBean后,Spring就可以自動注入了。

這樣繼承的好處是,我們在做頁面測試的時候,只需要把ServicesAction的引用換成一個假的Services實現(xiàn),只是模擬下數(shù)據(jù)庫的結(jié)果就省去大量的反復(fù)啟動數(shù)據(jù)庫的時間了。

package?action;

import?net.sf.frails.bean.annotations.SpringBean;
import?domain.Services;
import?domain.ServicesImp;

public?class?ServicesAction?{

????
/**
?????*?若果是測試頁面的話,去掉annotation換成模擬的實現(xiàn)就好了:
?????*??Services?services?=?new?MockServices();
?????*??這里MockServices是對Services接口的實現(xiàn),
?????*??但是邏輯代碼并不是真的操作數(shù)據(jù)庫,而是直接返回一些東西,方便頁面測試。
?????
*/

????@SpringBean
????Services?sevices;
}

?

package?action;

import?java.util.ArrayList;
import?java.util.List;

import?net.sf.frails.bean.annotations.DefMbean;
import?net.sf.frails.bean.annotations.Prop;
import?net.sf.frails.bean.annotations.ScopeType;
import?domain.entities.Note;

@DefMbean(scope
=ScopeType.REQUEST)
public?class?NotebookAction?extends?ServicesAction{
????
????
/**
?????*?存放已有的Note列表
?????
*/

????@Prop
????List
<Note>?noteList?=?new?ArrayList<Note>();

????
/**
?????*?存放一個新的Note
?????
*/

????@Prop
????Note?note?
=?new?Note();
????
????
/**
?????*?增加一個Note
?????*?
@param?newNote?要增加的Note
?????
*/

????
public?void?addNote(Note?newNote)
????
{
????????
????}

}

這個Bean里沒有setter和getter方法,這個并不是沒有給出完整的代碼,而是根本不用寫!Frials知道為每個屬性增加setter和getter是多么乏味的事情,雖然elipse能夠自動處理,但是Frials是和IDE無關(guān)的,并不假設(shè)快速開發(fā)一定要建立在功能強大的IDE上(但是實際上JSF是暗含有這樣的假設(shè)的)。只需要在屬性前面加上@Prop,這個屬性就相當(dāng)于自動加上了setter和getter方法。

更重要的是,我們在寫對應(yīng)的頁面的時候,EL表達(dá)式可以更加簡練:在引用對應(yīng)本頁面的ManagedBean的時候,我們不需要再寫這個Bean的名字,取而代之的是一個美元符號。所以,我們在寫datatable標(biāo)簽的時候,value的值寫成 #{$.noteList}這樣就好了。現(xiàn)在來寫JSF頁面吧,除了EL表達(dá)式簡化了外,和原來的JSF寫法沒有什么區(qū)別,記住名字的對應(yīng)就好了。

這里要說明的一點是Frails的EL表達(dá)式可以傳遞參數(shù),形式為#{$.method['arg1','arg2'... ]}。并且Frails提供了注入支持,我們甚至可以直接在頁面上通過EL表達(dá)式調(diào)用Services方法,比如我們這里要實現(xiàn)刪除一個Note,那么可以這樣寫dataTable:

<h:dataTable?value="#{$.noteList}"?var="note">
????????????
<h:column>
????????????????.
????????????
</h:column>
????????????
<h:column>
????????????????
<h:commandButton?action=#{$.Services.delete['note']}>
<!--?BUTTON?NAME?-->
</h:commandButton>
????????????
</h:column>
</h:dataTable>

另外頁面的導(dǎo)航也簡化成直接寫要導(dǎo)航到頁面的名字了。如action="a",則直接導(dǎo)航到a.jsp。

2 輸入驗證

假設(shè)我們需要驗證驗證是否輸入了用戶名,并且用戶名的長度在4-12之間。輸入內(nèi)容應(yīng)該大于12。當(dāng)然,我們完全可以按照J(rèn)SF原有的方式在頁面里嵌入驗證的組件。但是,驗證和頁面邏輯混雜在一起不是件好事情,隨著需要驗證的內(nèi)容加大,代碼變得難以維護(hù),并且驗證需求的變更——這個是可能的可會導(dǎo)致很大的損失。在Frails里,對屬性的驗證都在ManagedBean里屬性聲明時候處理。也就是說,我們聲明一個屬性的時候同時說明需要滿足什么條件的時候這個屬性才會接受值,否則我們就提示錯誤信息,或者轉(zhuǎn)到某個專門用來報錯的頁面。如果我們要在NotebookAction里驗證輸入的note,具體的做法有兩種:

一是使用annotation在當(dāng)前Bean里寫驗證方法,一般用于對一般類型的驗證。要做的就是在屬性前面加上@ValidateXXX(message="error message"),message里存放錯誤信息。
例如我們要驗證一個Email:

@Prop
????@ValidateEmail(message
="error.message")
????String?email;

或者要驗證字符串,要求不為空,最小長度為3:

@Prop
????@ValidateString(required?
=?true,?minLen?=?3)
????String?name;

還有@ValidateNumber,@ValidateDate。如果這些基本的還不能滿足你驗證需求,可以使用一個方法來抓們驗證屬性,例如我們要用一個叫validateMethod的方法驗證一個叫username的字符串,直接在Prop加上屬性(validator="Method"):

@Prop(validator="validate")
String?username;
public?void?validate(String?username)?{
if(username?==?null?||?username.length()?==?0)?{
//DO?SOMETHING?TO?REPROT?ERROR
}

}


二是使用驗證Bean,用于對類的屬性域的驗證。回到我們notebook的例子中來,我們要驗證一個note,可以在note屬性前加上@ValidateBean以及參數(shù):

@Prop
????@ValidateBean(fields
={
????@Field(name
="poster",required=true,
????????????validateNumber
=@ValidateNumber(max=12,min=8,message="Poster?Name?????too?long?or?to?short"),
????????????message
="You?must?input?your?name"),
????@Field(name
="content",required=true,
????????????validateString
=@ValidateString(minLen=8,message="You?have?????to?put?more?words?here"),
????????????message
="You?must?input?the?content")
????}
)
????Note?note?
=?new?Note();

其中每一個@Feild對應(yīng)了一個類中要驗證的屬性。Name對應(yīng)的是屬性的名稱,必須與你在類中屬性聲明一致;required代表是否不為空。后面還可以跟上若干validateXXX嵌套,每個層面上都可以有message來顯示錯誤。當(dāng)然了,其中的以部分這里也可以被標(biāo)注為使用方法來驗證。

也可以將驗證Bean單獨寫到一個專門的驗證類上,只需要在被驗證的屬性前加上@ValidateBean,并且在里面指明用來驗證類的別名就可以了:

@Prop
????@ValidateBean(name
="noteValidator")
????Note?note?
=?new?Note();

然后我們新建一個類來負(fù)責(zé)驗證note。在類前面加上@BeanValidator標(biāo)注,其fiels與恰面提到的一樣,只是注意在BeanValidator的name要和前面被驗證屬性上的匹配。代碼如下:

import?net.sf.frails.bean.validator.BeanValidator;

@BeanValidator(name?
=?"infoValidator",?fields?=?{
????????
//THE?SAME?WITH?VALIDATORBEAN
}
)
public?class?Validator?{

}

以上我們實現(xiàn)了統(tǒng)一的驗證過程。經(jīng)過實踐后您會發(fā)現(xiàn)將驗證代碼從頁面分離開后會帶來多么大的好處。這些請您親自去體驗吧。

3 ManagedBean范圍及設(shè)計模式

在實際的項目中,頁面肯定不會只有一個。頁面和頁面之間的需要傳遞的數(shù)據(jù),以前往往是放在Session里面,因為JSF內(nèi)建的導(dǎo)航機制都是POST請求。這就使得我們不得不將些ManagedBean設(shè)置為Session范圍,但是這樣做并不是那么必要,會造成一些麻煩。最好的設(shè)計是,每個頁面的ManagedBean都是Request范圍,他們通過簡單的GET請求互相傳遞一些數(shù)據(jù)的主鍵,而每個ManagedBean自己負(fù)責(zé)根據(jù)這些主鍵將相應(yīng)的信息提取出來。這樣頁面與頁面之間就通過類似接口一樣關(guān)聯(lián)起來,一旦他們之間傳遞的主鍵約定好了,頁面就可以相對分離開;同時,這些頁面也是支持瀏覽器收藏夾收藏的。這樣我們只需要少量的,基本上是一個Session范圍的Bean保存當(dāng)前用戶的信息,一個Application范圍的Bean保存應(yīng)用的某些屬性,其余的都是Request范圍的Bean來實現(xiàn)整個頁面架構(gòu)了。

有了Frails中@PreRender將一個方法標(biāo)注為在JSF組件樹渲染周期前調(diào)用,@Param將一個屬性標(biāo)注為GET請求參數(shù),通過這兩個annotation 的結(jié)合使用,我們能很輕松的實現(xiàn)GET請求。

假設(shè)我們要新增加一個頁面叫note.Jsp,用來顯示一條留言的詳細(xì)內(nèi)容。他的ManagedBean(當(dāng)然就是NoteAction了)接受一個long id作為GET請求參數(shù),并且在渲染出組件樹前事先從數(shù)據(jù)庫里根據(jù)id提取出note的內(nèi)容。

package?action;

import?net.sf.frails.bean.annotations.DefMbean;
import?net.sf.frails.bean.annotations.Param;
import?net.sf.frails.bean.annotations.PreRender;

@DefMbean
public?class?NoteAction?{

????@Param(name?
=?"noteID")
????
private?long?id;
????
????@PreRender
????
private?void?preRender()
????
{
????????
if(?id?!=?0?)
????????
{
????????????
//GET?INFO
????????}
else{
????????????
//HANDLE?WITH?NO?GET?PARAM
????????}

????}

????
????
//OTHERS
}

上面Param里的name屬性就是GET參數(shù)的名字,并且可以自動的將String參數(shù)類型轉(zhuǎn)換成其他的基本類型。GET參數(shù)的定義可以簡單的在頁面上使用<h:OutputLink>,這個標(biāo)簽對應(yīng)的是一個GET請求,在內(nèi)嵌入<f:param>就是請求的參數(shù)了。

<h:outputLink?value="note.jsp"?>
????????????????????
<h:outputText?value="查看詳細(xì)信息"></h:outputText>
????????????????????
<f:param?name="noteID"/>
????????
</h:outputLink>

請注意outputLink的屬性value就是要導(dǎo)航的頁面,這里Frials沒有對其進(jìn)行任何的簡化,必須填寫要導(dǎo)航到的頁面的全名。Param里的name屬性就是GET參數(shù)的名字,必須和noteAction里的@Param屬性一致。


總結(jié):

我們通過一個簡單的例子,基本上了解了Frails框架模板快速搭建一個Spring+Hibernate+JSF的項目過程。首先,我們按照Frails的默認(rèn)配置快速的完成了各種設(shè)置;接下來我們使用Frails對Dao的支持類在沒有寫任何Hibernate或者SQL代碼的情況下完成了數(shù)據(jù)庫業(yè)務(wù)的方法;然后我們了解了頁面上Frails簡化的EL表達(dá)式;還有相當(dāng)重要的驗證方法;最后我們了解了下通過實現(xiàn)GET請求來使得頁面之間相對松耦合的設(shè)計。希望這樣的介紹能帶您進(jìn)入到Frails框架的奇妙世界中來,我相信當(dāng)您親自體驗Frails的魅力后,一定愛不釋手!