posts - 495,  comments - 11,  trackbacks - 0

          4.9 Struts與Hibernate的整合策略

          前面介紹了Hibernate的一些相關(guān)知識(shí)點(diǎn),距離Hibernate進(jìn)入實(shí)際開發(fā)還有一段路要走。Hibernate作為持久層解決方案,必須與其他表現(xiàn)層技術(shù)組合在一起才可形成一個(gè)J2EE開發(fā)框架。經(jīng)??吹骄W(wǎng)上一些朋友給出的Hibernate入門示例,居然在JSP頁面中訪問Hibernate Configuratioin對(duì)象。甚至看到某些所謂的精通J2EE書籍,也居然在JSP頁面中訪問Hibernate的Configuration對(duì)象——這種現(xiàn)狀非常讓人擔(dān)憂,Hibernate并不是萬金油,并不是說項(xiàng)目中使用Hibernate就怎么了不起了,而是通過使用Hibernate,可以讓J2EE應(yīng)用架構(gòu)更科學(xué),可以讓開發(fā)者以更好的面向?qū)ο蟮姆绞竭M(jìn)行項(xiàng)目開發(fā)。

          反過來說,即使不使用Hibernate,而使用普通的JDBC持久化解決方案,也不應(yīng)該在JSP(表現(xiàn)層)訪問到JDBC API(持久層API)。下面介紹如何讓Hibernate和Struts進(jìn)行整合,整合Spring部分將在后面章節(jié)介紹。

          4.9.1 工廠模式介紹

          工廠模式是指當(dāng)應(yīng)用程序中A組件需要B組件協(xié)助時(shí),并不是直接創(chuàng)建B組件的實(shí)例,而是通過B組件的工廠——該工廠可以生成某一個(gè)類型組件的實(shí)例。在這種模式下,A組件無須與B組件以硬編碼方式耦合在一起,而只需要與B組件的工廠耦合。

          對(duì)于A組件而言,它只關(guān)心工廠生產(chǎn)的實(shí)例是否滿足某種規(guī)范,即實(shí)現(xiàn)了某個(gè)接口(滿足接口規(guī)范,即可供自己正常調(diào)用)。這種模式提供了對(duì)象之間清晰的角色劃分,降低了程序的耦合。

          接口產(chǎn)生的全部實(shí)例通常實(shí)現(xiàn)相同接口,接口里定義全部實(shí)例共同擁有的方法,這些方法在不同的實(shí)現(xiàn)類中實(shí)現(xiàn)方式不同。程序調(diào)用者無須關(guān)心方法的具體實(shí)現(xiàn),從而降低了系統(tǒng)異構(gòu)的代價(jià)。

          下面是工廠模式的示例代碼:

          //Person接口定義

          public interface Person

          {??

          ??? /**

          ??? * @param name 對(duì)name打招呼

          ??? * @return 打招呼的字符串

          ??? */

          ??? public String sayHello(String name);

          ??? /**

          ??? * @param name 對(duì)name告別

          ??? * @return 告別的字符串

          ??? */

          ??? public String sayGoodBye(String name);

          }

          該接口定義Person的規(guī)范,該接口必須擁有兩個(gè)方法:能打招呼、能告別。規(guī)范要求實(shí)現(xiàn)該接口的類必須具有這兩個(gè)方法:

          //American類實(shí)現(xiàn)Person接口

          public class American implements Person

          {

          ??? /**

          ??? * @param name 對(duì)name打招呼

          ??? * @return 打招呼的字符串

          ??? */

          ??? public String sayHello(String name)

          ??? {

          ??????? return name + ",Hello";

          ??? }

          ??? /**

          ??? * @param name 對(duì)name告別

          ??? * @return 告別的字符串

          ??? */

          ??? public String sayGoodBye(String name)

          ??? {

          ??????? return name + ",Good Bye";

          ??? }

          }

          下面是實(shí)現(xiàn)Person接口的另一個(gè)實(shí)現(xiàn)類Chinese

          public class Chinese implements Person

          {

          ??? /**

          ??? * @param name 對(duì)name打招呼

          ??? * @return 打招呼的字符串

          ??? */

          ??? public String sayHello(String name)

          ??? {

          ??????? return name + ",您好";

          ??? }

          ??? /**

          ??? * @param name 對(duì)name告別

          ??? * @return 告別的字符串

          ??? */

          ??? public String sayGoodBye(String name)

          ??? {

          ??????? return name + ",下次再見";

          ??? }

          }

          然后看Person工廠的代碼:

          public class PersonFactory

          {

          ??? /**

          ??? * 獲得Person實(shí)例的工廠方法

          ??? * @ param ethnic 調(diào)用該實(shí)例工廠方法傳入的參數(shù)

          ??? * @ return返回Person實(shí)例

          ??? */

          ??? public Person getPerson(String ethnic)

          ??? {

          ??????? //根據(jù)參數(shù)返回Person接口的實(shí)例

          ??????? if (ethnic.equalsIgnoreCase("chin"))

          ??????? {

          ??????????? return new Chinese();

          ??????? }

          ??????? else

          ??????? {

          ??????????? return new American();

          ??????? }

          ??? }

          }

          最簡(jiǎn)單的工廠模式的框架基本如上所示。

          主程序部分僅僅需要與工廠耦合,而無須與具體的實(shí)現(xiàn)類耦合在一起。下面是主程序部分:

          public class FactroyTest

          {

          ??? public static void main(String[] args)

          ??? {

          ??????? //創(chuàng)建PersonFactory的實(shí)例,獲得工廠實(shí)例

          ??????? PersonFactory pf = new PersonFactory();

          ??????? //定義接口Person的實(shí)例,面向接口編程

          ??????? Person p = null;

          ??????? //使用工廠獲得Person的實(shí)例

          ??????? p = pf.getPerson("chin");

          ??????? //下面調(diào)用Person接口的方法

          ??????? System.out.println(p.sayHello("wawa"));

          ??????? System.out.println(p.sayGoodBye("wawa"));

          ??????? //使用工廠獲得Person的另一個(gè)實(shí)例

          ??????? p = pf.getPerson("ame");

          ??????? //再次調(diào)用Person接口的方法

          ??????? System.out.println(p.sayHello("wawa"));

          ??????? System.out.println(p.sayGoodBye("wawa"));

          ??? }

          }

          主程序從Person接口的具體類中解耦出來,而且程序調(diào)用者無須關(guān)心Person的實(shí)例化過程,角色劃分清晰。主程序僅僅與工廠服務(wù)定位結(jié)合在一起,獲得工廠的引用,程序?qū)⒖色@得所有工廠產(chǎn)生的實(shí)例。具體類的變化,重要接口不發(fā)生任何改變,調(diào)用者程序代碼部分幾乎無須發(fā)生任何改動(dòng)。

          4.9.2 使用DAO模式

          第1章介紹了J2EE應(yīng)用的架構(gòu),最上面的表現(xiàn)層,表現(xiàn)層與MVC框架的控制器交互,控制器負(fù)責(zé)調(diào)用業(yè)務(wù)邏輯組件的業(yè)務(wù)邏輯方法來處理用戶請(qǐng)求,而業(yè)務(wù)邏輯組件則依賴于DAO組件提供的數(shù)據(jù)庫原子操作,這種模式也被稱為DAO模式。

          由上面關(guān)于J2EE應(yīng)用架構(gòu)的介紹可見,控制器總是依賴于業(yè)務(wù)邏輯組件,而業(yè)務(wù)邏輯組件總是依賴于DAO組件。也就是說,控制器需要調(diào)用業(yè)務(wù)邏輯組件的方法,而業(yè)務(wù)邏輯組件需要調(diào)用DAO組件的方法。

          DAO模式的分層非常清晰,持久層訪問被封裝在DAO層下,而決不會(huì)擴(kuò)散到業(yè)務(wù)邏輯層,更不會(huì)在JSP頁面(表現(xiàn)層)中進(jìn)行持久層訪問。

          注意:即使在早期的Model 1(使用JSP + JavaBean創(chuàng)建應(yīng)用的模式,沒有使用MVC設(shè)計(jì)模式)模式下,持久層訪問也被封裝在JavaBean中完成,而不是直接在JSP頁面中進(jìn)行數(shù)據(jù)庫訪問。對(duì)于直接在JSP中訪問持久層API的做法,可以說根本不了解J2EE開發(fā)。

          那么控制器采用怎樣的方式訪問業(yè)務(wù)邏輯組件呢?應(yīng)該采用工廠模式,讓控制器與業(yè)務(wù)邏輯組件的實(shí)現(xiàn)類分離,僅與業(yè)務(wù)邏輯工廠耦合;同樣,業(yè)務(wù)邏輯組件也應(yīng)該采用工廠模式訪問DAO模式,而不是直接與DAO實(shí)現(xiàn)類耦合。

          后面的案例部分會(huì)介紹更實(shí)際的整合策略,此處僅僅介紹DAO模式下兩個(gè)工廠模式策略。

          4.9.3 DAO組件的工廠模式

          在J2EE應(yīng)用開發(fā)中,可擴(kuò)展性是一個(gè)隨時(shí)需要關(guān)注的問題。而DAO組件是經(jīng)常需要增加的項(xiàng)目組件,如果每次需要增加一個(gè)DAO組件都需要修改代碼是相當(dāng)讓人沮喪的事情。為了避免這種情況,采用XML配置文件來管理所有的DAO組件,這種DAO組件配置文件的代碼如下:

          <?xml version="1.0" encoding="GBK"?>

          <daoContext>

          ??? <!-- 配置應(yīng)用需要的sonDao組件 -->

          ??? <dao id="sonDao" class="org.yeeku.dao.impl.SonDaoImpl"/>

          ??? <!-- 配置應(yīng)用需要的personDao組件 -->

          ??? <dao id="personDao" class="org.yeeku.dao.impl.PersonDaoImpl"/>

          </daoContext>

          查看上面的配置文件可以看出,應(yīng)用中有配置了兩個(gè)DAO組件,因?yàn)槊總€(gè)DAO組件在J2EE應(yīng)用中僅需要一個(gè)實(shí)例就足夠了,因此DAO工廠類提供了一個(gè)緩存池來緩存每個(gè)DAO實(shí)例,并負(fù)責(zé)在應(yīng)用啟動(dòng)時(shí)創(chuàng)建所有的DAO。

          下面是DAO工廠類的代碼:

          public class DaoFactory

          {

          ??? //用于緩存DAO實(shí)例的Map對(duì)象

          ??? private Map<String, Dao> daoMap = new HashMap<String , Dao>();

          ??? //將DAO工廠寫成單態(tài)模式

          ??? private static DaoFactory df;

          ??? //DAO工廠的構(gòu)造器

          ??? private DaoFactory()throws Exception

          ??? {

          ??????? //使用SAXReader來負(fù)責(zé)解析daoContext.xml配置文檔

          ??? ??? Document doc = new SAXReader().read(new File(ConstantsUtil.realPath

          ??????? + "\\daoContext.xml"));

          ??????? //獲取文檔的根文檔

          ??? ??? Element root = doc.getRootElement();

          ??????? //獲取daoContext根元素的所有子元素

          ??? ??? List el = root.elements();

          ??? ??? for (Iterator it = el.iterator();it.hasNext() ; )

          ??? ??? {

          ??????????? //每個(gè)子元素對(duì)應(yīng)一個(gè)DAO組件

          ??????? ??? Element em = (Element)it.next();

          ??????? ??? String id = em.attributeValue("id");

          ??? ??????? //獲取實(shí)現(xiàn)類

          ??????? ??? String impl = em.attributeValue("class");

          ??????????? //通過反射,根據(jù)類名創(chuàng)建DAO組件的實(shí)例

          ??????? ??? Class implClazz = Class.forName(impl);

          ??????? ??? Dao d = (Dao)implClazz.newInstance();

          ??????????? //將創(chuàng)建的DAO組件放入緩存池中

          ??????? ??? daoMap.put(id, d);

          ??? ??? }

          ??? }

          ??? //單態(tài)模式必須提供一個(gè)入口方法來創(chuàng)建DAO工廠的方法

          ??? public static DaoFactory instance()throws Exception

          ??? {

          ??????? //如果DAO工廠還未創(chuàng)建

          ??? ??? if (df == null)

          ??? ??? {

          ??????? ??? df = new DaoFactory();

          ??? ??? }

          ??? ??? return df;

          ??? }

          ??? //下面的方法用于根據(jù)DAO組件ID獲取DAO組件

          ??? public Dao getDao(String id)

          ??? {

          ??? ??? return daoMap.get(id);

          ??? }

          }

          通過上面的工廠類代碼可以看出,DAO工廠負(fù)責(zé)初始化所有的DAO組件。系統(tǒng)每增加一個(gè)DAO組件,無須再修改任何代碼,僅僅需要在daoContext.xml配置文件中增加配置即可。

          注意:這種整合策略非常優(yōu)秀??蓴U(kuò)展性很好,如果應(yīng)用需要增加一個(gè)DAO組件,只需要修改配置文件,并提供相應(yīng)的DAO組件實(shí)現(xiàn)即可。而且,如果有一天需要重構(gòu)DAO組件,只須提供修改過的DAO組件實(shí)現(xiàn)類,而業(yè)務(wù)邏輯組件無須任何改變。

          業(yè)務(wù)邏輯組件代碼無須與DAO實(shí)現(xiàn)類耦合,業(yè)務(wù)邏輯組件的代碼面向DAO組件的接口編程,將業(yè)務(wù)邏輯組件和DAO組件的耦合降低到接口層次。

          4.9.4 業(yè)務(wù)邏輯組件的工廠模式

          與此類似的是,業(yè)務(wù)邏輯組件完全可以采用這種編程模式,業(yè)務(wù)邏輯組件的配置文件代碼如下:

          <?xml version="1.0" encoding="GBK"?>

          <appContext>

          ??? <!-- 配置應(yīng)用需要的業(yè)務(wù)邏輯組件,每個(gè)業(yè)務(wù)邏輯組件對(duì)應(yīng)一個(gè)app元素 -->

          ??? <app id="wawa" class="org.yeeku.service.impl.WawaServiceImpl"/>

          </appContext>

          業(yè)務(wù)邏輯組件工廠同樣可根據(jù)該配置文件來初始化所有業(yè)務(wù)邏輯組件,并將業(yè)務(wù)邏輯組件放入緩存池中,讓控制器與業(yè)務(wù)邏輯組件的耦合降低到接口層次。業(yè)務(wù)邏輯組件的工廠類代碼如下:

          public class AppFactory

          {

          ??? private Map<String , Object> appMap = new HashMap<String , Object>();

          ??? //業(yè)務(wù)邏輯組件工廠采用單態(tài)模式

          ??? private static AppFactory df;

          ??? //業(yè)務(wù)邏輯組件工廠的私有構(gòu)造器

          ??? private AppFactory()throws Exception

          ??? {

          ??????? //使用SAXReader來負(fù)責(zé)解析appContext.xml配置文檔

          ??????? Document doc = new SAXReader().read(new File(ConstantsUtil.realPath

          ??????? + "\\appContext.xml"));

          ??????? //獲取文檔的根文檔

          ??????? Element root = doc.getRootElement();

          ??????? //獲取appContext根元素的所有子元素

          ??????? List el = root.elements();

          ??????? for (Iterator it = el.iterator();it.hasNext() ; )

          ??????? {

          ??????????? //每個(gè)app元素對(duì)應(yīng)一個(gè)業(yè)務(wù)邏輯組件

          ??????????? Element em = (Element)it.next();

          ??????????? String id = em.attributeValue("id");

          ??????????? //根據(jù)配置文件指定的業(yè)務(wù)邏輯組件實(shí)現(xiàn)類來創(chuàng)建業(yè)務(wù)邏輯組件實(shí)例

          ??????????? String impl = em.attributeValue("class");

          ??????????? Class implClazz = Class.forName(impl);

          ??????????? Object d = implClazz.newInstance();

          ??????????? //將業(yè)務(wù)邏輯組件放入緩存池中

          ??????????? appMap.put(id , d);

          ??????? }

          ??? }

          ??? //單態(tài)模式必須提供入口方法,用于創(chuàng)建業(yè)務(wù)邏輯組件工廠

          ??? public static AppFactory instance()throws Exception

          ??? {

          ??????? //如果業(yè)務(wù)邏輯組件工廠為空

          ??????? if (df == null)

          ??????? {

          ??????????? df = new AppFactory();

          ??????? }

          ??????? return df;

          ??? }

          ??? //根據(jù)業(yè)務(wù)邏輯組件的id屬性獲取業(yè)務(wù)邏輯組件

          ??? public Object getApp(String id)

          ??? {

          ??????? //直接從緩存池中取出業(yè)務(wù)邏輯組件,并返回

          ??????? return appMap.get(id);

          ??? }

          }

          從某種程度上來講,這種方式與后來Spring的控制反轉(zhuǎn)(Inversion of Control,IoC)容器有異曲同工之妙,但Spring的IoC容器則提供了更多的功能。

          上面的兩個(gè)類中都用到了一個(gè)ConstantsUtil,它僅用于保存一個(gè)全局變量,有一個(gè)public static的realPath屬性,該屬性用于保存應(yīng)用在服務(wù)器中的路徑。

          posted on 2009-07-19 10:08 jadmin 閱讀(70) 評(píng)論(0)  編輯  收藏

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 油尖旺区| 鸡西市| 亚东县| 枞阳县| 旬阳县| 班戈县| 平定县| 铜山县| 凌云县| 柯坪县| 宁波市| 盐边县| 息烽县| 潼南县| 株洲县| 陆河县| 蒙自县| 玉林市| 德保县| 婺源县| 红桥区| 巍山| 都匀市| 云和县| 石渠县| 福州市| 舟曲县| 安陆市| 凤城市| 东兰县| 台中县| 广南县| 务川| 偃师市| 叙永县| 淳化县| 新疆| 新干县| 冕宁县| 昭觉县| 小金县|