葛 萌,歐陽宏基
(咸陽師范學(xué)院信息工程學(xué)院,咸陽712000)
Struts作為Apache組織的一個(gè)開源項(xiàng)目,是一種基于MVC設(shè)計(jì)模式的Java Web開發(fā)框架[1],其第一代產(chǎn)品Struts1在Java Web開發(fā)領(lǐng)域具有非常高的市場(chǎng)占有率。隨著時(shí)間的推移,Struts1的一些弊端逐漸顯露出來,例如表現(xiàn)層所支持的技術(shù)單一、Action組件與Servlet API緊密耦合并且是單例實(shí)現(xiàn)、存在線程安全性問題、侵入式框架、嚴(yán)重依賴于Struts1 API等。因此Struts2在Struts1和WebWork的技術(shù)基礎(chǔ)上進(jìn)行了整合。它以WebWork為核心,采用攔截器的機(jī)制來處理用戶請(qǐng)求,目的是為了使業(yè)務(wù)邏輯控制器能夠與Servlet API完全脫離開,便于開發(fā)與測(cè)試[2]。分析了Struts2的體系結(jié)構(gòu),詳細(xì)描述了Struts2各組件在響應(yīng)客戶端請(qǐng)求時(shí)的調(diào)用過程。按照MVC設(shè)計(jì)模式,依據(jù)一個(gè)Web站點(diǎn)的信息發(fā)布系統(tǒng),詳細(xì)描述了表現(xiàn)層、控制層和模型層的實(shí)現(xiàn)過程,對(duì)于利用Struts2開發(fā)Java Web應(yīng)用具有一定的借鑒意義。
Struts2的體系結(jié)構(gòu)(如圖1所示)與Struts1差別非常大,因?yàn)镾truts2使用了WebWork的設(shè)計(jì)核心,而不是Struts1的設(shè)計(jì)核心。Struts2框架的基本執(zhí)行流程如下描述:
(1)客戶端向Servlet容器(例如 Tomcat)發(fā)送HttpServletRequest請(qǐng)求。
(2)該請(qǐng)求經(jīng)過一系列的過濾器(Filter)進(jìn)行過濾,其中ActionContextCleanUp是一個(gè)可選過濾器,該過濾器對(duì)于Struts2和其他框架的集成起作用(例如:SiteMesh Plugin)。
(3)FilterDispatcher是Struts2的核心過濾器,擔(dān)當(dāng)MVC模式中控制層的核心功能。FilterDispatcher根據(jù)ActionMapper來決定當(dāng)前用戶請(qǐng)求是否需要調(diào)用某個(gè)Action。
(4)如果 ActionMapper決定需要調(diào)用某個(gè)Action,F(xiàn)ilterDispatcher把請(qǐng)求的處理交給 Action-Proxy。
(5)ActionProxy通過Configuration Manager讀取框架的配置文件(struts.xml),找到需要調(diào)用的Action類,并創(chuàng)建一個(gè)ActionInvocation實(shí)例。
(6)ActionInvocation實(shí)例使用命名模式來調(diào)用,在調(diào)用 Action的過程前后,涉及到相關(guān)攔截器(Intercepter)的調(diào)用。
(7)默認(rèn)情況下,Action調(diào)用execute()方法執(zhí)行相應(yīng)的業(yè)務(wù)邏輯(也可以是Action中其它的方法,但這些方法在調(diào)用時(shí)需要顯示聲明)。
(8)當(dāng) Action執(zhí)行完畢,ActionInvocation實(shí)例根據(jù)配置文件(查找響應(yīng)的是什么信息如:SUCCESS、ERROR,INPUT等)找到對(duì)應(yīng)的返回結(jié)果。返回結(jié)果通常是(也可能是另外的一個(gè)Action)JSP頁面或者FreeMarker的模版來響應(yīng)用戶請(qǐng)求,在表示過程中可以使用Struts2提供的標(biāo)簽。
圖1 Struts2體系結(jié)構(gòu)
目前幾乎所有Web站點(diǎn)都具備信息的動(dòng)態(tài)維護(hù)功能。管理人員合法登錄到站點(diǎn)的后臺(tái)模塊就可以對(duì)最新信息、文檔、圖片等資源進(jìn)行添加、上傳等操作,使得前臺(tái)頁面顯示的內(nèi)容呈現(xiàn)出動(dòng)態(tài)變化的過程。本節(jié)以咸陽師范學(xué)院國(guó)際交流學(xué)院的Web站點(diǎn)為例,依據(jù)MVC模式結(jié)合Struts2和FCKEditor框架,詳細(xì)描述信息發(fā)布系統(tǒng)的實(shí)現(xiàn)過程。
表示層主要由JSP頁面、FCKEditor和Struts2標(biāo)簽構(gòu)成。FCKeditor是目前最優(yōu)秀的可見即可得網(wǎng)頁文檔編輯器之一,它采用JavaScript編寫,具備功能強(qiáng)大、配置容易、跨瀏覽器、支持多種編程語言(可以和 PHP、JavaScript、ASP、ASP.NET、Java 等不同的編程語言相結(jié)合)、開源等特點(diǎn)[3]。本系統(tǒng)選用的是Java版本的FCKEditor2.6,首先將fckeditorjava-core-2.6.jar包導(dǎo)入到 WEB -INF/lib目錄中,然后在需要FCKEditor的JSP頁面頭部加入以下聲明代碼。
< %@taglib uri="http://java.fckeditor.net"prefix="FCK"%>
< %@page import="net.fckeditor.*"% >在頁面需要顯示文檔編輯器的位置,加入如下Java代碼段:
//以JSP內(nèi)置對(duì)象request為實(shí)參創(chuàng)建FCKEditor對(duì)象并命名
FCKeditor fckEditor=new FCKeditor(request,"EditorDefault");
//FCKEditor一些簡(jiǎn)單屬性的設(shè)置,并通過out內(nèi)置對(duì)象將文檔編輯器顯示在JSP頁面上fckEditor.setValue("");
fckEditor.setHeight("500");
out.println(fckEditor);
Struts2提供了功能強(qiáng)大的標(biāo)簽庫,這些標(biāo)簽不依賴于任何的表現(xiàn)層技術(shù),同時(shí)支持用戶自定義標(biāo)簽以滿足頁面復(fù)雜多變的需求[4]。Struts2提供的標(biāo)簽主要包括:UI類標(biāo)簽、控制類標(biāo)簽和支持Ajax的標(biāo)簽等。使用Struts2標(biāo)簽前需要添加導(dǎo)入標(biāo)簽庫的語句:<%@taglib uri="/struts-tags"prefix="s"%>
下面是前臺(tái)JSP頁面顯示最新信息標(biāo)題和發(fā)布日期的Struts2控制類標(biāo)簽-iterator的使用代碼。
<s:iterator value="list_news"var="infos">
<tr width="100%"height="20">
<td width="85%">
<a class="t1"href="content.jsp?id= < s:property value="#infos.id"/>"target="blank"> <s:property value="#infos.title"/> < /a>
</td>
<td width="15%"><s:property value="#in-fos.publishDate"/>
</td>
</tr>
</s:iterator>
其中value屬性的值list_news為相應(yīng)Action中的一個(gè)集合,這個(gè)集合里封裝的是與數(shù)據(jù)庫表對(duì)應(yīng)的信息實(shí)體的JavaBean對(duì)象,再通過property標(biāo)簽將需要的屬性顯示在頁面中。
控制層主要完成Action和配置文件的定義。Struts2框架有兩個(gè)核心配置文件,其中struts.xml是整個(gè)Struts2應(yīng)用程序的核心配置文件(存放在項(xiàng)目的src目錄下),其中主要包括包配置、命名空間配置、攔截器配置、Action映射及Action處理結(jié)果和物理資源之間的映射配置及異常處理配置等。另一個(gè)struts.properties配置文件中主要定義了Struts2框架的大量常量屬性。但通常推薦也是在struts.xml文件中來配置這些常量屬性。以下是本系統(tǒng)中struts.xml配置文件的部分代碼。
<struts>
<constant name="struts.i18n.encoding"value="UTF-8"/>
< constant name="struts.multipart.maxSize"value="10701096"/>
<package name="lhy-default"namespace="/bgm"extends="struts-default">
<global-results>
<result name="error" > /bgm/error.jsp < /result>
< /global-results>
<global-exception-mappings>
<exception-mappingresult="error"exception="java.lang.Exception" > < /exception -mapping>
</global-exception-mappings>
</package>
<package name="backgroud"namespace="/bgm"extends="lhy-default">
<action name="InformationManage"class="lhy.action.InformationAction" >
<result name="success">${destinationPage}</result>
<resultname ="error" >/bgm/Information Manage-Result.jsp < /result>
</action>
</package>
</struts>
其中constant標(biāo)簽用于配置相應(yīng)常量屬性。package標(biāo)簽用于定義包配置,name為必填屬性,用來指定包的名字,extends為可選屬性,用來指定該包繼承其他包。如果繼承其它包,可以繼承其它包中的Action、攔截器等。namespace為可選屬性,用來指定該包的命名空間。global-results和globalexception-mapping兩個(gè)標(biāo)簽結(jié)合起來完成異常的聲明式處理方式,exception屬性指明了處理異常的類型,result屬性指明了產(chǎn)生該異常時(shí)的返回類型,從而在global-results標(biāo)簽中找到對(duì)應(yīng)的顯示頁面。由于異常的聲明式處理方式配置在一個(gè)package標(biāo)簽中,那么其他的package可以進(jìn)行繼承,從而共享這個(gè)異常處理方式。action標(biāo)簽用來配置Action對(duì)象,name為必填屬性(在一個(gè) package中Action的name值唯一),class為必填屬性用來指定Action字節(jié)碼的位置。result為action的子標(biāo)簽,name為可選屬性,其值默認(rèn)是SUCCESS(可選值為SUCCESS、NONE、ERROR、INPUT、LOGIN)表示Action執(zhí)行的結(jié)果,不同的結(jié)果定位到不同的資源。
除了配置文件外,控制層最主要的任務(wù)是根據(jù)用戶請(qǐng)求定義相應(yīng)的Action來進(jìn)行處理。Struts2的Action采用了低侵入式設(shè)計(jì),即Action可以是一個(gè)POJO,不與任何Struts2的基類和接口打交道。但實(shí)際上為了方便實(shí)現(xiàn)Action,大多數(shù)情況下都會(huì)繼承com.opensymphony.xwork2.ActionSupport類,并重寫此類的execute()方法。因?yàn)榇祟愔袑?shí)現(xiàn)了很多的實(shí)用接口,提供了很多默認(rèn)方法,這些默認(rèn)方法包括獲取國(guó)際化信息的方法、數(shù)據(jù)校驗(yàn)的方法、默認(rèn)的處理用戶請(qǐng)求的方法等,這樣可以大大簡(jiǎn)化Action的開發(fā)。以下是信息發(fā)布業(yè)務(wù)所對(duì)應(yīng)的Action核心代碼。
import com.opensymphony.xwork2.ActionSupport;
public class InformationAction extends ActionSupport{
privateInformationBean infoBean;//HTTP 向Action傳遞參數(shù)采取域模型的方式
public String execute(){
String tag=ERROR;
int opResult=0;
if(opType.equals("add"))//當(dāng)前執(zhí)行添加操作
{
//調(diào)用模型層組件,執(zhí)行持久化操作
InfoDAO infoDAO=DAOFactory.getDAOInstance(InfoBean.class);
opResult=infoDAO.addInformation(infoBean);
if(opResult==1)
{
tag=SUCCESS;
destinationPagePath="/lhy/bgm/ShowInfo.jsp";
}
}
r eturn tag;
}}
由于Struts2的Action直接封裝了HTTP請(qǐng)求參數(shù),與Servlet API實(shí)現(xiàn)了松耦合。Struts2的Action接收HTTP請(qǐng)求參數(shù)一共有三種方式:使用Action的屬性、域模型、實(shí)現(xiàn)ModelDriver泛型接口。本系統(tǒng)采用域模型方式來接收請(qǐng)求參數(shù),此方式的優(yōu)點(diǎn)在于避免Action內(nèi)部出現(xiàn)大量與實(shí)體相關(guān)的屬性和實(shí)現(xiàn)過多的接口。例如上述代碼中的infoBean,它是一個(gè)JavaBean對(duì)象,內(nèi)部封裝了與信息實(shí)體相關(guān)的屬性。Action類中要提供infoBean的get和set方法。
和Struts1一樣,Struts2也沒有在模型方面提供現(xiàn)成的組件,為了達(dá)到Java EE分層模型之間的松散耦合,開發(fā)人員需要選取JavaBean或者EJB組件來完成業(yè)務(wù)邏輯和持久化邏輯。本系統(tǒng)選取JavaBan來實(shí)現(xiàn)業(yè)務(wù)邏輯,利用泛型DAO模式結(jié)合JDBC來實(shí)現(xiàn)持久化邏輯。DAO模式是Java EE數(shù)據(jù)持久層所使用的最廣泛的模式,它的核心思想是向外部提供一個(gè)訪問數(shù)據(jù)源的統(tǒng)一接口,對(duì)外隱藏操作數(shù)據(jù)源的實(shí)現(xiàn)細(xì)節(jié),以至于將來更換數(shù)據(jù)持久化方式時(shí)不影響上層的調(diào)用者。由于一個(gè)系統(tǒng)中可能會(huì)存在多個(gè)需要持久化操作的實(shí)體類,這些實(shí)體類對(duì)數(shù)據(jù)源的CRUD操作都具有相同點(diǎn),因此在DAO模式的基礎(chǔ)上加入泛型機(jī)制可以避免大量重復(fù)的CRUD代碼并且能夠解決強(qiáng)制類型轉(zhuǎn)換所可能產(chǎn)生的異常。
控制層通過Action調(diào)用持久化層邏輯的過程如圖2所示。DAOFactory通過工廠模式向上層調(diào)用者隱藏了具體DAO實(shí)現(xiàn)類的創(chuàng)建過程。實(shí)體類通常與數(shù)據(jù)庫表對(duì)應(yīng),每個(gè)實(shí)體類都有相應(yīng)的DAO實(shí)現(xiàn),由于不同的實(shí)體類基本上都具有相同的CRUD操作,所以定義了泛型DAO接口提取了所有實(shí)體類共有的持久化方法,并在泛型實(shí)現(xiàn)類中通過模板模式進(jìn)行抽象,將不同實(shí)體類屬性與表中字段的映射留給具體泛型實(shí)現(xiàn)類去完成。JdbcUtil是采用單例模式設(shè)計(jì)的一個(gè)JDBC工具類,提供了通過DataSource獲取Connection對(duì)象的方法以及釋放相關(guān)JDBC資源的方法。為了實(shí)現(xiàn)對(duì)Connection對(duì)象的復(fù)用以提高數(shù)據(jù)庫訪問的性能,在Tomcat中配置了DBCP連接池。Tomcat Servlet容器將DBCP數(shù)據(jù)源作為一個(gè)JND I數(shù)據(jù)源,通過配置參數(shù)來使用DBCP 連接池[5]。在 DBPool.config 配置文件中定義了連接池的相關(guān)參數(shù)(初始連接數(shù)、最大活動(dòng)連接數(shù)、最小空閑連接數(shù)、最大空閑連接數(shù)等)。
圖2 Action調(diào)用持久化層邏輯
論文詳細(xì)分析了Struts2框架的組成部分以及響應(yīng)用戶請(qǐng)求的執(zhí)行過程,按照MVC模式的視圖、控制、模型三個(gè)部分結(jié)合信息發(fā)布系統(tǒng)詳細(xì)描述了Struts2框架的實(shí)現(xiàn)過程。實(shí)踐表明:Struts2框架能夠簡(jiǎn)化Web應(yīng)用的開發(fā)過程,使得程序具有清晰的結(jié)構(gòu)框架,并且開發(fā)人員能夠分工協(xié)作,從而提高開發(fā)效率和系統(tǒng)的可擴(kuò)展性和可維護(hù)性。
[1]甘文麗,劉為超.基于Struts2和Ajax的企業(yè)級(jí)Web應(yīng)用開發(fā)[J].工礦自動(dòng)化,2013,39(2):24 -25.
[2]丁波,晁愛農(nóng).基于Struts2框架的 AJAX開發(fā)研究[J].計(jì)算機(jī)工程與設(shè)計(jì),2009,30(16):3910 -3911.
[3]董海燕,王衛(wèi)東.基于JSF、Spring和Hibernate的技術(shù)資料綜合管理系統(tǒng)設(shè)計(jì)[J].計(jì)算機(jī)應(yīng)用與軟件,2012,29(5):214 -214.
[4]陸舟.Struts2技術(shù)內(nèi)幕:深入解析Struts架構(gòu)設(shè)計(jì)與實(shí)現(xiàn)原理[M].機(jī)械工業(yè)出版社,2012,185-187.
[5]陳潔.DBCP數(shù)據(jù)庫連接池的自優(yōu)化配置[J].計(jì)算機(jī)與現(xiàn)代化,2010(12):112-113.