熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java開源技術 >> 正文

用struts+spring+hibernate組裝web應用

2013-11-23 20:30:20  來源: Java開源技術 

  其實就算用Java建造一個不是很煩瑣的web應用程序也不是件輕松的事情當為一個應用程序建造一個構架時有許多事情需要考慮從高層來說開發者需要考慮怎樣建立用戶接口(user interfaces)?在哪裡處理業務邏輯?和怎樣持久化應用數據這三層每一層都有它們各自的問題需要回答 各個層次應該使用什麼技術?怎樣才能把應用程序設計得松耦合和能靈活改變?構架允許層的替換不會影響到其它層嗎?應用程序怎樣處理容器級的服務(container level services)比如事務處理(transactions)?

  當為你的web應用程序創建一個構架時需要涉及到相當多的問題幸運的是已經有不少開發者已經遇到過這類重復發生的問題並且建立了處理這類問題的框架一個好框架具備以下幾點 減輕開發者處理復雜的問題的負擔(不重復發明輪子內部定義為可擴展的有一個強大的用戶群支持框架通常能夠很好的解決一方面的問題然而你的應用程序有幾個層可能都需要它們各自的框架就如解決你的用戶接口(UI)問題時你就不應該把事務邏輯和持久化邏輯摻雜進來例如你不應該在控制器(controller)裡面寫jdbc代碼使它包含有業務邏輯這不是控制器應該提供的功能它應該是輕量級的代理來自用戶接口(UI)外的調用請求給其它服務於這些請求的應用層好的框架自然的形成代碼如何分布的指導更重要的是框架減輕開發者從頭開始寫像持久層這樣的代碼的痛苦使他們專注於對客戶來說很重要的應用邏輯

  這篇文章將討論怎樣組合幾個著名的框架去做到松耦合的目的怎樣建立你的構架怎樣讓你的各個應用層保持一致富於挑戰的是組合這些框架使得每一層都以一種松耦合的方式彼此溝通而與底層的技術無關這篇文章將使用3種流行的開源框架來討論組合框架的策略表現層我們將使用Struts();業務層我們將使用Spring();持久層使用Hibrenate()你也可以在你的應用程序中替換這些框架中的任何一種而得到同樣的效果圖1展示了當這些框架組合在一起時從高層看是什麼樣子

  
圖1用Struts Spring 和 Hibernate框架構建的概覽

  應用程序的分層 (Application Layering)

大多數不復雜的web應用都能被分成至少4個各負其責的層次這些層次是表現層(presentation)持久層(persistence)業務層(business)領域模型層(domain model)每層在應用程序中都有明確的責任不應該和其它層混淆功能每一應用層應該彼此獨立但要給他們之間放一個通訊接口讓我們從審視各個層開始討論這些層應該提供什麼和不應該提供什麼

  表現層 (The Presentation Layer)
  
  在一個典型的web應用的一端是表現層很多Java開發者也理解Struts所提供的然而太常見的是他們把像業務邏輯之類的耦合的代碼放進了一個orgapachestrutsAction所以讓我們在像Struts這樣一個框架應該提供什麼上取得一致意見這兒是Struts負責的

  為用戶管理請求和響應
提供一個控制器(controller)代理調用業務邏輯和其它上層處理
處理從其它層擲出給一個Struts Action的異常
為顯示提供一個模型
執行用戶接口(UI)驗證

這兒是一些經常用Struts編寫的但是卻不應該和Struts表現層相伴的項目
直接和數據庫通訊比如JDBC調用
業務邏輯和與你的應用程序相關的驗證
事務管理
在表現層中引入這種代碼將導致典型耦合(type coupling)和討厭的維護

持久層 (The Persistence Layer )

  在典型web應用的另一端是持久層這通常是使事情迅速失控的地方開發者低估了構建他們自己的持久層框架的挑戰性一般來說機構內部自己寫的持久層不僅需要大量的開發時間而且還經常缺少功能和變得難以控制有幾個開源的對象-關系映射(ORM)框架非常解決問題尤其是Hibernate框架為java提供了"對象-關系持久化"(objecttorelational persistence)機制和查詢服務Hibernate對那些已經熟悉了SQL和JDBC API的Java開發者有一個適中的學習曲線Hibernate持久對象是基於簡單舊式Java對象(POJO)和Java集合(Java collections)此外使用Hibernate並不妨礙你正在使用的IDE下面的列表包含了你該寫在一個持久層框架裡的代碼類型

  查詢相關的信息成為對象Hibernate通過一種叫作HQL的面向對象(OO)的查詢語言或者使用條件表達式API(expressive criteria API)來做這個事情 HQL非常類似於SQL 只是把SQL裡的table和columns用Object和它的fields代替有一些新的專用的HQL語言成分要學不過它們容易理解而且文檔做得好HQL是一種使用來查詢對象的自然語言花很小的代價就能學習它

  保存更新刪除儲存在數據庫中的信息

  像Hibernate這樣的高級對象-關系映射(objecttorelational mapping)框架提供對大多數主流SQL數據庫的支持它們支持父/子(parent/child)關系事務處理繼承和多態

  這兒是一些應該在持久層裡被避免的項目

  業務邏輯應該在你的應用的一個高一些的層次裡持久層裡僅僅允許數據存取操作

  你不應該把持久層邏輯(persistence logic)和你的表現層邏輯(presentation logic)攪在一起避免像JSPs或基於servlet的類這些表現層組件裡的邏輯和數據存取直接通訊通過把持久層邏輯隔離進它自己的層應用程序變得易於修改而不會影響在其它層的代碼例如Hebernate能夠被其它持久層框架或者API代替而不會修改在其它任何層的代碼

  業務層(The Business Layer)

  在一個典型的web應用程序的中間的組件是業務層或服務層從編碼的視角來看這個服務層是最容易被忽視的一層不難在用戶接口(UI)層或者持久層裡找到散布在其中的這種類型的代碼這不是正確的地方因為這導致了應用程序的緊耦合這樣一來隨著時間推移代碼將很難維護幸好針對這一問題有好幾種Frameworks存在在這個領域兩個最流行的框架是Spring和PicoContainer它們叫作微容器(microcontainers)你可以不費力不費神的把你的對象連在一起所有這些框架都工作在一個簡單的叫作依賴注入(dependency injection)(也通稱控制反轉(inversion of control))的概念上這篇文章將著眼於Spring的為指定的配置參數通過bean屬性的setter注入(setter injection)的使用Spring也提供了一個構建器注入(constructor injection)的復雜形式作為setter注入的一個替代對象們被一個簡單的XML文件連在一起這個XML文件含有到像事務管理器(transaction management handler)對象工廠(object factories)包含業務邏輯的服務對象(service objects)和數據存取對象(DAO)這些對象的引用(references)

  這篇文章的後面將用例子來把Spring使用這些概念的方法說得更清楚一些業務層應該負責下面這些事情
處理應用程序的業務邏輯和業務驗證
管理事務
預留和其它層交互的接口
管理業務層對象之間的依賴
增加在表現層和持久層之間的靈活性使它們互不直接通訊
從表現層中提供一個上下文(context)給業務層獲得業務服務(business services )
管理從業務邏輯到持久層的實現

  領域模型層 (The Domain Model Layer )
  最後因為我們討論的是一個不是很復雜的基於web的應用程序我們需要一組能在不同的層之間移動的對象領域對象層由那些代表現實世界中的業務對象的對象們組成比如一份訂單(Order)訂單項(OrderLineItem)產品(Product)等等這個層讓開發者停止建立和維護不必要的數據傳輸對象(或者叫作DTOs)來匹配他們的領域對象例如Hibernate允許你把數據庫信息讀進領域對象(domain objects)的一個對象圖這樣你可以在連接斷開的情況下把這些數據顯示到UI層那些對象也能被更新和送回到持久層並在數據庫裡更新而且你不必把對象轉化成DTOs因為DTOs在不同的應用層間移動可能在轉換中丟失這個模型使得Java開發者自然地以一種面向對象的風格和對象打交道沒有附加的編碼

  結合一個簡單的例子

  既然我們已經從一個高的層次上理解了這些組件 現在就讓我們開始實踐吧在這個例子中我們還是將合並StrutsSpringHibernate框架每一個這些框架在一篇文章中都有太多的細節覆蓋到這篇文章將用一個簡單的例子代碼展示怎樣把它們結合在一起而不是進入每個框架的許多細節示例應用程序將示范一個請求怎樣跨越每一層被服務的這個示例應用程序的一個用戶能保存一個訂單到數據庫中和查看一個在數據庫中存在的訂單進一步的增強可以使用戶更新或刪除一個存在的訂單  

  你可以下載這個應用的源碼()

  因為領域對象(domain objects)將和每一層交互我們將首先創建它們這些對象將使我們定義什麼應該被持久化什麼業務邏輯應該被提供和哪種表現接口應該被設計然後我們將配置持久層和用Hibernate為我們的領域對象(domain objects)定義對象關系映射(objecttorelational mappings)然後我們將定義和配置我們的業務對象(business objects)在有了這些組件後我們就能討論用Spring把這些層連在一起最後我們將提供一個表現層(presentation layer)它知道怎樣和業務服務層(business service layer)交流和知道怎樣處理從其它層產生的異常(exceptions)

  領域對象層(Domain Object Layer)

  因為這些對象將和所有層交互這也許是一個開始編碼的好地方這個簡單的領域模型將包括一個代表一份訂單(order)的對象和一個代表一個訂單項(line item for an order)的對象訂單(order)對象將和一組訂單項(a collection of line item)對象有一對多(onetomany)的關系例子代碼在領域層有兩個簡單的對象
agleboOrderjava: 包括一份訂單(oder)的概要信息
agleboOrderLineItemjava: 包括一份訂單(order)的詳細信息
考慮一下為你的對象選擇包名它將反映你的應用程序是怎樣分層的例如簡單應用的領域對象(domain objects)可以放進aglebo包[譯者注bobusiness object?]更多專門的領域對象將放入在aglebo下面的子包裡業務邏輯在agleservice包裡開始打包DAO對象放進agleservicedaohibernate包對於forms和actions的表現類(presentation classes)分別放入agleaction 和 agleforms包准確的包命名為你的類提供的功能提供一個清楚的區分使當故障維護時更易於維護和當給應用程序增加新的類或包時提供一致性

  持久層配置(Persistence Layer Configuration)
 
  用Hibernate設置持久層涉及到幾個步驟第一步是進行配置持久化我們的領域業務對象(domain business objects )因為我們用於領域對象(domain objects )持久化的Hibernate和POJOs一起工作( 此句原文Since Hibernate works with POJOs we will use our domain objects for persistence因此訂單和訂單項對象包括的所有的字段的都需要提供getter和setter方法訂單對象將包括像ID用戶名合計和訂單項這樣一些字段的標准的JavaBean格式的setter和getter方法訂單項對象將同樣的用JavaBean的格式為它的字段設置setter和getter方法
  Hibernate在XML文件裡映射領域對象到關系數據庫訂單和訂單項對象將有兩個映射文件來表達這種映射有像XDoclet()這樣的工具來幫助這種映射Hibernate將映射領域對象到這些文件
Orderhbmxml
OrderLineItemhbmxml
你可以在WebContent/WEBINF/classes/com/meagle/bo目錄裡找到這些生成的文件配置Hibernate SessionFactory(_docs/api/net/sf/hibernate/l)使它知道是在和哪個數據庫通信使用哪個數據源或連接池加載哪些持久對象SessionFactory提供的Session(_docs/api/net/sf/hibernate/l)對象是Java對象和像選取保存更新刪除對象這樣一些持久化功能間的翻譯接口我們將在後面的部分討論Hibernate操作Session對象需要的SessionFactory配置

  業務層配置(Business Layer Configuration )

  既然我們已經有了領域對象(domain objects)我們需要有業務服務對象來執行應用邏輯執行向持久層的調用獲得從用戶接口層(UI layer)的請求處理事務處理異常為了將所有這些連接起來並且易於管理我們將使用Spring框架的bean管理方面(bean management aspect)Spring使用控制反轉(IoC)或者setter依賴注入來把這些對象連好這些對象在一個外部的XML文件中被引用控制反轉是一個簡單的概念它允許對象接受其它的在一個高一些的層次被創建的對象使用這種方法你的對象從必須創建其它對象中解放出來並降低對象耦合

  這兒是個不使用IoC的對象創建它的從屬對象( object creating its dependencies without IoC)的例子這導致緊的對象耦合

  圖2沒有使用IoC的對象組織對象A創建對象B和C

  這兒是一個使用IoC的例子它允許對象在一個高一些層次被創建和傳進另外的對象所以另外的對象能直接使用現成的對象·[譯者注另外的對象不必再親自創建這些要使用的對象](allows objects to be created at higher levels and passed into objects so that they can use the implementations directly)

  圖3對象使用IoC組織對象A包含setter方法它們接受到對象B和C的接口這也可以用對象A裡的接受對象B和C的構建器完成

建立我們的業務服務對象(Building Our Business Service Objects)
  我們將在我們的業務對象中使用的setter方法接受的是接口這些接口允許對象的松散定義的實現這些對象將被設置或者注入在我們這個例子裡我們將使我們的業務服務對象接受一個DAO去控制我們的領域對象的持久化當我們在這篇文章的例子中使用Hibernate( While the examples in this article use Hibernate)我們可以容易的轉換到一個不同的持久框架的實現通知Spring使用新的實現的DAO對象你能明白編程到接口和使用依賴注入模式是怎樣寬松耦合你的業務邏輯和你的持久化機制的
  這兒是業務服務對象的接口它是一個DAO對象依賴的樁(Here is the interface for the business service object that is stubbed for a DAO object dependency: )

  public interface IOrderService {
  public abstract Order saveNewOrder(Order order)
    throws OrderException
           OrderMinimumAmountException;

  public abstract List findOrderByUser(
                                     String user)
                           throws OrderException;

  public abstract Order findOrderById(int id)
                           throws OrderException;

  public abstract void setOrderDAO(
                             IOrderDAO orderDAO);
}

  注意上面的代碼有一個為DAO對象准備的setter方法這兒沒有一個getOrderDAO方法因為它不是必要的因為不太有從外面訪問連著的OrderDAO對象的需要DAO對象將被用來和我們的持久層溝通我們將用Spring把業務服務對象和DAO對象連在一起因為我們編碼到接口我們不會緊耦合實現

下一步是寫我們的DAO實現對象因為Spring有內建的對Hibernate的支持這個例子DAO將繼承HibernateDaoSupport(
orm/hibernate/support/Hibl)類這使得我們容易取得一個到HibernateTemplate(
orm/hibernate/Hl)類的引用HibernateTemplate是一個幫助類它能簡化Hibernate Session的編碼和處理HibernateExceptions這兒是DAO的接口

  public interface IOrderDAO {

  public abstract Order findOrderById(
                                    final int id);

  public abstract List findOrdersPlaceByUser(
                           final String placedBy);

  public abstract Order saveOrder(
                               final Order order);
}

  我們還有兩個對象要和我們的業務層連在一起這包括HibernateSessionFactory和一個TransactionManager對象這在Spring配置文件裡直接完成Spring提供一個HibernateTransactionManager(
orm/hibernate/HibernateTrl)它將從工廠綁定一個Hibernate Session到一個線程來支持事務(見ThreadLocal()獲取更多的信息)這兒是HibernateSessionFactory和HibernateTransactionManager的Spring配置

         class="org.springframework.orm.hibernate.
              LocalSessionFactoryBean">
  


      
        com/meagle/bo/Order.hbm.xml
      

      
        com/meagle/bo/OrderLineItem.hbm.xml
      

    
  



  

  

  

  net.sf.hibernate.dialect.MySQLDialect
      


      

  false
      


      

  C:/MyWebApps/.../WEB-INF/proxool.xml
      


      

  spring
      


    


  





         class="org.
                springframework.
                orm.
                hibernate.
                HibernateTransactionManager">
  

  
  


  

  每一個對象能被Spring配置裡的一個標記引用。TW.wiNGWIT.coM在這個例子裡,bean “mySessionFactory”代表一個HibernateSessionFactory,bean “myTransactionManager”代表一個Hibernate transaction manager。注意transactionManger bean有一個叫作sessionFactory的屬性元素。HibernateTransactionManager有一個為sessionFactory准備的setter和getter方法,它們是用來當Spring容器啟動時的依賴注入。sessionFactory屬性引用mySessionFactory bean。這兩個對象現在當Spring容器初始化時將被連在一起。這種連接把你從為引用和創建這些對象而創建singleton對象和工廠中解放出來,這減少了你應用程序中的代碼維護。mySessionFactory bean有兩個屬性元素,它們翻譯成為mappingResources 和 hibernatePropertes准備的setter方法。通常,如果你在Spring之外使用Hibernate,這個配置將被保存在hibernate.cfg.xml文件中。不管怎樣,Spring提供了一個便捷的方式--在Spring配置文件中合並Hibernate的配置。獲得更多的信息查閱Spring API()。

既然我們已經配置了我們的容器服務beans和把它們連在了一起,我們需要把我們的業務服務對象和我們的DAO對象連在一起。然後,我們需要把這些對象連接到事務管理器。
  
這是在Spring配置文件裡的樣子:

  
  class="org.
         springframework.
         transaction.
         interceptor.
         TransactionProxyFactoryBean">
  

  
  


  

  
  


  

  

  

  PROPAGATION_REQUIRED,readOnly,-OrderException
      


      

  PROPAGATION_REQUIRED,-OrderException
      


    


  





         class="com.
                meagle.
                service.
                spring.
                OrderServiceSpringImpl">
  

  
  





         class="com.
                meagle.
                service.
                dao.
                hibernate.
                OrderHibernateDAO">
  

  
  


  圖4是我們已經連在一起的東西的一個概覽。它展示了每個對象是怎樣相關聯的和怎樣被Spring設置進其它對象中。把這幅圖和示例應用中的Spring配置文件對比查看它們之間的關系。

  圖4:這是Spring怎樣將在這個配置的基礎上裝配beans。

  這個例子使用一個TransactionProxyFactoryBean,它有一個為我們已經定義了的事務管理者准備的setter方法。這是一個有用的對象,它知道怎樣處理聲明的事務操作和你的服務對象。你可以通過transactionAttributes屬性定義事務怎樣被處理,transactionAttributes屬性為方法名定義模式和它們怎樣參與進一個事務。獲得更多的關於在一個事務上配置隔離層和提交或回滾查閱TransactionAttributeEditor(
transaction/interceptor/Transactiol)。

  TransactionProxyFactoryBean(
transaction/interceptor/Transactionl)類也有一個為一個target准備的setter,target將是一個到我們的叫作orderTarget的業務服務對象的引用(a reference)。 orderTarget bean定義使用哪個業務服務對象並有一個指向setOrderDAO()的屬性。orderDAO bean將居於這個屬性中,orderDAO bean是我們的和持久層交流的DAO對象。

  還有一個關於Spring和bean要注意的是bean能以兩種模式工作。這兩種模式被定義為singleton和prototype。一個bean默認的模式是singleton,意味著一個共享的bean的實例將被管理。這是用於無狀態操作--像一個無狀態會話bean將提供的那樣。當bean由Spring提供時,prototype模式允許創建bean的新實例。你應當只有在每一個用戶都需要他們自己的bean的拷貝時才使用prototype模式。

提供一個服務定位器(Providing a Service Locator)
  既然我們已經把我們的服務和我們的DAO連起來了,我們需要把我們的服務暴露給其它層。通常是一個像使用Struts或Swing這樣的用戶接口層裡的代碼來使用這個服務。一個簡單的處理方法是使用一個服務定位器模式的類從一個Spring上下文中返回資源。這也可以靠引用bean ID通過Spring來直接完成。
  這兒是一個在Struts Action中怎樣配置一個服務定位器的例子:

  public abstract class BaseAction extends Action {

  private IOrderService orderService;

  public void setServlet(ActionServlet
                                 actionServlet) {
    super.setServlet(actionServlet);
    ServletContext servletContext =
               actionServlet.getServletContext();

    WebApplicationContext wac =
      WebApplicationContextUtils.
         getRequiredWebApplicationContext(
                                 servletContext);

      this.orderService = (IOrderService)
                     wac.getBean("orderService");
  }

  protected IOrderService getOrderService() {
    return orderService;
  }
}

  用戶接口層配置 (UI Layer Configuration)
  示例應用的用戶接口層使用Struts框架。這兒我們將討論當為一個應用分層時和Struts相關的部分。
讓我們從在struts-config.xml文件裡檢查一個Action配置開始。

  type="agle.action.SaveOrderAction"
    name="OrderForm"
    scope="request"
    validate="true"
    input="/NewOrder.jsp">
  Save New Order
      path="/NewOrder.jsp"
    scope="request"
    type="agle.exception.OrderException"/>
      path="/NewOrder.jsp"
    scope="request"
    type="com.
          meagle.
          exception.
          OrderMinimumAmountException"/>
  
  

  SaveNewOrder Action被用來持久化一個用戶從用戶接口層提交的訂單。這是一個典型的Struts Action;然而,注意這個action的異常配置。這些Exceptions為我們的業務服務對象也在Spring 配置文件(applicationContext-hibernate.xml)中配置了(在transactionAttributes屬性裡)。當這些異常被從業務層擲出我們能在我們的用戶接口裡恰當的處理它們。第一個異常,OrderException,當在持久層裡保存訂單對象失敗時將被這個action使用。這將引起事務回滾和通過業務對象傳遞把異常傳回給Struts層。OrderMinimumAmountException,在業務對象邏輯裡的一個事務因為提交的訂單達不到最小訂單數量而失敗也將被處理。然後,事務將回滾和這個異常能被用戶接口層恰當的處理。

  最後一個連接步驟是使我們的表現層和我們的業務層交互。這已經通過使用前面討論的服務定位器來完成了。服務層充當一個到我們的業務邏輯和持久層的接口。這兒是 Struts中的SaveNewOrder Action可能怎樣使用一個服務定位器調用一個業務方法:

  public ActionForward execute(
  ActionMapping mapping,
  ActionForm form,
  javax.servlet.http.HttpServletRequest request,
  javax.servlet.http.HttpServletResponse response)
  throws java.lang.Exception {

  OrderForm oForm = (OrderForm) form;

  // Use the form to build an Order object that
  // can be saved in the persistence layer.
  // See the full source code in the sample app.

  // Obtain the wired business service object
  // from the service locator configuration
  // in BaseAction.
  // Delegate the save to the service layer and
  // further upstream to save the Order object.
  getOrderService().saveNewOrder(order);

  oForm.setOrder(order);

  ActionMessages messages = new ActionMessages();
  messages.add(
      ActionMessages.GLOBAL_MESSAGE,
new ActionMessage(
      "message.order.saved.successfully"));

  saveMessages(request, messages);

  return mapping.findForward("success");
}

  結論
  這篇文章按照技術和架構覆蓋了許多話題。從中而取出的主要思想是怎樣更好的給你的應用程序分層:用戶接口層、持久邏輯層、和其它任何你需要的應用層。這樣可以解耦你的代碼,允許添加新的代碼組件,使你的應用在將來更易維護。這裡覆蓋的技術能很好的解決這類的問題。不管怎樣,使用這樣的構架可以讓你用其他技術代替現在的層。


From:http://tw.wingwit.com/Article/program/Java/ky/201311/28590.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.