概覽
輕量級的企業應用開發越來越受到廣大JEE應用開發者的追捧而Spring框架又是輕量級容器的傑出代表由於Spring的使用日漸廣泛因此已有許多基於WebSphere應用服務器(WAS)的應用采用了Spring框架本文首先介紹使用Spring開發Web應用的基本問題然後結合WebSphere應用服務器講述Spring應用如何結合容器提供的服務文章目的是與大家一起探討如何更好的采用Spring框架開發基於WebSphere應用服務器的應用
Spring框架的主要思想描述
Spring框架的核心思想我們可以用兩個字來描述那就是解耦應用程序的各個部分之間(包括代碼內部和代碼與平台之間)盡量形成一種松耦合的結構使得應用程序有更多的靈活性應用內部的解耦主要通過一種稱為控制反轉(IOC)的技術來實現控制反轉的基本思想就是本來由應用程序本身來主動控制的調用等邏輯轉變成由外部配置文件來被動控制通常我們用一個所謂的好萊塢原則(Dont call me I will call you)來比喻這種控制反轉的關系由於控制反轉的概念相對比較廣泛很多應用服務器實際上也實現了不同程度的控制反轉技術只是這些應用服務器對應用程序的侵入性太強因此Martin Fowler專門寫了一篇文章討論控制反轉這個概念並提出一個描述更為准確的概念叫依賴注入(Dependency Injection)
Spring框架中的各個部分都充分使用了這種依賴注入的技術實現從而給應用以最大的靈活度實際上這種依賴注入的參數化應用控制並不是Spring的首創比如IBM的多渠道應用整合平台(Branch Transformation ToolkitBTT)很早就采用了這種外部參數化控制的技術BTT中的對象工廠與Spring框架中的BeanFactory也有著異曲同工之妙
Spring框架另外一個比較重要的技術是它對於面向切面的編程(AOP)的支持隨著應用復雜度的逐漸上升和對應用靈活性要求的提高IT邏輯和業務邏輯盡量分離的呼聲也越來越高AOP技術作為實現這種分離的一種比較好的途徑而越來越受到大家的重視Spring提供的是一種動態AOP實現也即通過代理模式動態地在目標對象的方法前後插入相應的處理代碼應用程序與底層應用服務器平台的解耦也可以借助AOP技術來實現Spring內置的AOP支持是一種錦上添花的功能它使得一些本來必須由容器支持的功能比如事務控制可以脫離開容器運行從而達到瘦身的目的這也是為什麼Spring框架常被人成為輕量級容器的一個原因
Spring框架可以與許多已有的框架技術結合使用JEE技術應用的一個重要特點是相關的開源社區非常活躍Web應用的不同層次都有非常多優秀的開源框架存在比如Web層的StrutsOR映射層的Hibernate等Spring框架並不重新發明輪子它的出現不是為了替代這些已有的框架相反Spring框架在設計上可以獨立構建應用或者結合已有的框架一起構建應用另外一個值得指出的地方是Spring框架的幾大模塊之間相互耦合度很小因此Spring框架的使用可以根據實際需要選其部分模塊循序漸進的使用而非必須統統照搬
基於Spring的Web應用基礎
Web應用的典型層次
Web應用一般在邏輯上根據功能分為以下幾層
.展示層
這一層主要如何生成展示給最終用戶的界面盡可能少的包含業務邏輯處理對於基於JEE的Web應用JSP是其最為常見的一種技術Spring對於展示層的支持非常靈活除了直接支持JSP之外它還支持基於FreeMarker模板基於Velocity模板或其它文檔類型的界面等的表現層實現
.業務層
業務層一般包含主要的業務邏輯尤其是與用例相對應的那些業務邏輯另外這一層也適合包含事務管理和安全控制方面的邏輯良好的業務層設計可以使得展示層可以采用不同的技術而不影響業務層業務層的功能上可以類比於JEE技術中的無狀態會話BEAN層次
.數據訪問對象(DAO)接口層
DAO實際上就是數據接口層在應用中建議通過接口來體現DAO的存在使得數據訪問可以與底層持久化層的具體實現相分離一般在DAO接口中主要就是實現數據對象的查詢存儲刪除等操作從理論上講DAO層與底層數據的存儲方式是獨立的也就是說並不一定要求是關系型數據庫Spring框架在設計的時候也考慮到了其它非關系型數據庫數據源的情況
.持久業務對象
持久業務對象是問題域中業務對象的持久化表示比如一個用戶對象一個銀行帳戶等我們一般通過某種O/R映射技術來實現這些業務對象的持久化持久業務對象是可以包含業務邏輯的與業務層所包含的業務邏輯不同的地方是持久業務對象所包含的是與具體業務對象直接相關且更為通用的業務邏輯
.企業信息系統
企業信息系統泛指Web應用需要連接的後台系統一般可以分為三大類即ERP系統企業傳統的遺留系統和關系型數據庫大部分Web應用都是基於關系型數據庫的這也是像Spring等常見框架所主要考慮的企業信息系統
設計良好的Web應用在層次一般是上一層依賴下一層但是下一層不依賴上一層我們可以暫時概括為向下而不向上依賴原則為了使得不同層次之間的依賴降到最低建議使用接口耦合這一點又是Spring框架發揮它外部配置優勢的地方
MVC的選擇
雖然說MVC這種模式早在Java語言出現前就有了但是這種模式在JEE時代才大行其道為廣大Web應用開發者所接受對於各種基於MVC的框架而言其要解決的問題主要可以分為以下幾部分
.將Web頁面中的輸入封裝成一個數據對象比如像Struts的表單BEANSpring MVC中的命令類等
.根據請求的不同由負責分發的控制器來映射和調用相應的邏輯處理單元並將上面的數據對象作為參數傳入
.邏輯處理單元完成相應的處理之後又把結果放入一個數據對象
.在選擇的展現界面中把返回的數據對象通過某種方式顯示出來
在使用Spring構建MVC的時候可以選擇直接使用Spring自己的MVC實現或者利用Spring對已有的一些MVC框架的支持比如Spring可以支持StrutsWebWork等與它們結合使用Spring引以為傲的非侵入的特性在Spring MVC上表現得並不如人意它與Servlet API的耦合度較其它部分高而且需要使用一些Spring的接口和類
Spring MVC的主要分發器實現是orgspringframeworkwebservletDispatcherServlet這是Spring MVC的訪問入口Spring提供SimpleFormControllerAbstractCommandController等類來幫助應用構建各種控制器動作並用ModelAndView類來聯系展示和邏輯返回數據如上節所述Spring MVC能夠支持不同的界面展示技術而且界面的展示和其後面控制器的實現是分離的也即界面展示技術的變化不用修改控制器的實現只需要利用Spring的控制反轉技術修改外部配置文件即可比如在使用JSP展示技術時外部配置文件的viewResolver定義如下
<bean id=viewResolver
class=orgspringframeworkwebservletviewInternalResourceViewResolver>
<property name=viewClass>
<value>orgspringframeworkwebservletviewJstlView</value>
</property>
<property name=prefix><value>/view/</value></property>
<property name=suffix><value>jsp</value></property>
</bean>
如果切換到FreeMaker模板技術那麼除了頁面模板的修改之外主要就是把對應的外部配置文件更改一下即可如下所示具體的展示邏輯部分不用做什麼修改
<bean id=viewResolver
class=orgspringframeworkwebservletviewfreemarkerFreeMarkerViewResolver>
<property name=viewClass>
<value>
orgspringframeworkwebservletviewfreemarkerFreeMarkerView
</value>
</property>
<property name=suffix><value>ftl</value></property>
</bean>
<bean id=freemarkerConfig
class=orgspringframeworkwebservletviewfreemarkerFreeMarkerConfigurer>
<property name=templateLoaderPath>
<value>/view/</value>
</property>
</bean>
如果不使用Spring的MVC框架而想結合已有的一些MVC框架Spring也是支持的Spring對於常見的MVC框架都提供了支持包括StrutsWebWorkTapestry和JSF等結合使用這些框架的一個好處是可以使用一些已有的熟悉的技術另外結合Spring的AOP攔截器可以相對比較容易地處理框架動作共有的事情比如動作的日志處理等如果選擇這些MVC框架那麼在使用框架的配置文件和應用的Spring配置文件都需要做相應的修改比如使用Struts的時候Strutsconfigxml配置文件中的映射動作類型一般會設置成orgspringframeworkwebstrutsDelegatingActionProxy或者設置控制器為orgspringframeworkwebstrutsDelegatingRequestProcessor然後需要在相應應的WebApplicationContext中定義與Struts Action對應的Bean這樣就可以充分利用Spring的控制反轉技術來管理Struts的Action了
另外在使用這些框架的時候要解決的一個問題是上下文的裝載比如使用Struts可以使用ContextLoaderPlugin來裝載Web上下文這個ContextLoaderPlugin替換了原來通過DispacherServlet裝載的方式需要在strutsconfigxml文件中添加如下條目<plugin className=orgspringframeworkwebstrutsContextLoaderPlugIn/>這種方式可以使Spring的Web上下文隨著Struts ActionServlet的初始化而裝載
因此如果用戶已有的應用是基於某個MVC框架或者用戶熟悉某個框架那麼可以利用Spring對這些框架的支持結合使用因為我們的目的本來就是為了解決問題而不是為了使用某種技術但是對其它用戶而言如果不是對已有的一些MVC框架比較熟悉的話那就直接使用Spring的MVC框架就可以了
Web Context設置
對於不依賴於應用服務器的Spring 上下文(Context)設置通常在應用代碼中通過FileSystemXmlApplicationContext或ClasspathXmlApplicationContext來獲取比如使用這樣的代碼來得到上下文
ApplicationContext ctx = new FileSystemXmlApplicationContext(configxml);
但是按照控制反轉的原則應用程序代碼應該盡可能少的知道上下文的設置因此在基於Spring的Web應用中這樣的代碼也可以省去Spring可以通過配置讓Web容器自動裝載上下文配置文件從本質上講Web應用的ServletContext就是Spring用來存放應用上下文的地方Spring中與Web Context裝載相關的有幾個類
.ContextLoaderListener一般的應用服務器如WAS都能先裝載Listener如果不是的話那麼只能使用ContextLoaderServlet
.ContextLoaderServlet需要配置<loadonstartup>使得它率先裝載真正裝載Context的類是ContextLoader上面兩個類只是兩種調用ContextLoader的不同途徑ContextLoader內部實際調用的是XmlWebApplicationContext其缺省配置文件為/WEBINF/applicationContextxml
如果使用ContextLoaderListener其在webxml中的配置一般如下
<listener>
<listenerclass>
orgsprntextContextLoaderListener
</listenerclass>
</listener>
如果使用ContextLoaderServlet其在webxml中的配置一般如下
<servlet>
<servletname>context</servletname>
<servletclass>
orgsprntextContextLoaderServlet
</servletclass>
<loadonstartup></loadonstartup>
</servlet>
應用本身可能除了基於HTTP的Web渠道之外還通過別的渠道對外提供服務因此一個比較好的做法是把展示相關的配置與後面業務處理部分的配置分開這樣如果更改了應用的訪問渠道只需要修改對應的配置文件即可因此Spring提供了一個WebApplicationContext的概念在WebApplicationContext中一般包含與Web訪問相關的配置定義包括各種控制動作的定義界面展示的定義等等
WebApplicationContext一般由DispatcherServlet來初始化在上下文層次結構上可以把它看成是ApplcationContext的子上下文在缺省的情況下DispatcherServlet裝載的配置文件名稱為其Servlet名稱Servletxml但是可以通過contextConfigLocation參數來定制DispatcherServlet在webxml中的定義示例如下
<servlet>
<servletname>Dispatcher</servletname>
<servletclass>
orgspringframeworkwebservletDispatcherServlet
</servletclass>
<loadonstartup></loadonstartup>
</servlet>
<initparam>
<paramname>contextConfigLocation</paramname>
<paramvalue>/WEBINF/context/Webcontrollersxml</paramvalue>
</initparam>
數據持久化層
雖然使用JEE技術的Web應用可以連接多種不同的企業信息系統(EIS)但是毫無疑問數據庫是其中最為重要和常見的一種正因如此Spring對數據庫訪問提供了非常完備的支持數據訪問對象(DAO)模式是JEE模式中非常重要的一種它的主要目的是使得持久化層與業務邏輯層分離從而屏蔽持久化層的具體實現我們可以把Spring的DAO支持分為兩大類一是直接基於Spring JDBC模板的數據訪問另一類是基於某種O/R映射框架的數據訪問這裡剛好可以使用Spring的控制反轉特性通過外部配置文件來定義DAO接口和實際實現類之間的關系Spring框架目前支持的O/R映射框架包括HibernateJDOTopLinkiBATIS等
假設我們定義了一個userDAO當使用JDBC來實現這個DAO的時候定義的類可以如下所示
public class userDAOJDBC extends JdbcDaoSupport implements userDAO{ … }
如果使用Hibernate來實現這個DAO的時候定義的類如下
public class UserDAOHibernate extends HibernateDaoSupport implements UserDAO { … }
Spring對於其它的O/R映射機制都有相應的抽象類供應用使用比如對於iBATIS有SqlMapClientDaoSupport對於JDO有JdoDaoSupport等
下面我們看一下如何在Spring的配置文件中定義上述DAO與具體實現的關系假設我們的userDAO具體實現是通過Hibernate那麼在applicationContextxml中的一個DAO可以定義如下
<bean id=userDAO class=comfgwdaohibernateUserDAOHibernate>
<property name=sessionFactory>
<ref local=sessionFactory/>
</property>
</bean>
在這裡我們實際DAO接口定義是comfgwdaoUserDAO而具體實現類為comfgwdaohibernateUserDAOHibernate顯然對於其它DAO的實現我們只需要在配置文件中修改相應的實現類(具體實現類當然是比不可少的)和屬性即可比如對於JDBC的DAO實現屬性就定義成相應的數據源
Spring與WebSphere應用服務器的配合
Spring與底層JEE應用服務器還是存在一些需要結合的地方這裡給出WAS中的一些結合點
使用WAS數據源
在Java應用程序中數據庫的連接一般有兩種方式來得到一種是通過javasqlDriverManager的方式來得到數據庫連接這種方式不依賴於應用服務的支持但是也不提供數據庫連接池的功能另外一種方式是通過javaxsqlDataSource的方式來得到數據庫連接在傳統基於JEE的應用需要通過JNDI來得到數據源(javaxsqlDataSource)對象然後再通過數據源來得到相應的數據庫連接常見的應用服務器都支持這種方式且一般都提供了數據庫連接池的支持雖然說我們一般推薦使用數據庫連接池但是也有一些時候我們需要脫離開應用服務器的環境使用數據庫(比如單元測試比如應用移植等)然而應用程序使用這兩種方式的時候代碼是不一樣的因此只能通過代碼來應變Spring提供了一個統一使用數據源的解決方案然後通過控制反轉的機制用外部配置文件來指定使用的數據源這樣一方面可以統一這兩種得到數據庫連接的方式另一方面也不需要像通常的JEE應用通過繁瑣的JNDI代碼來得到數據源這樣應用程序也就不需要知道使用的何種數據源
Spring提供了一個DriverManagerDataSource類來統一第一種方式的數據源獲取如果使用WAS中的Cloudscape數據庫用外部配置文件可配置如下
<bean id=dataSource class=orgspringframeworkjdbcdatasourceDriverManagerDataSource>
<property name=driverClassName>
<value>comibmdbjjdbcDBjDriver</value>
</property>
<property name=url>
<value>jdbc:dbj:D:\\DBName</value>
</property>
</bean>
Spring提供了JndiObjectFactoryBean類來支持第二種方式的數據源獲取假設WAS中已經配置好的數據源名稱為jdbc /MyDB那麼用外部配置文件可配置如下
<bean id=dataSource
class=orgspringframeworkjndiJndiObjectFactoryBean>
<property name=jndiName><value>java:comp/env/jdbc/MyDB</value></property>
</bean>
或者
<bean id=dataSource
class=orgspringframeworkjndiJndiObjectFactoryBean>
<property name=jndiName><value>jdbc/MyDB</value></property>
<property name=resourceRef><value>true</value></property>
</bean>
從上面配置我們可以得知通過使用Spring應用程序能夠統一使用不同的數據源實現如果使用環境發生變化那麼只需要修改Spring的配置文件即可對於部署在WAS上的Web應用在生產環境中推薦使用WAS實現的數據庫連接池一方面是因為連接池實現地比較完善另一方面使用WAS提供的數據庫連接池可以很完善地支持JTA事務
使用WAS的JTA
Web應用程序在使用事務的時候常常會涉及一個事務類型的選擇是選擇像JDBC事務這樣的本地事務呢還是使用JTA支持的全局事務這個與應用程序需要涉及到的事務管理器類型和個數密切相關Spring本身不支持分布式事務因此分布式事務需要底層的JTA但是Spring提供了事務的抽象即底層真正事務實現可以切換而不影響應用程序代碼這樣應用程序可以依賴於底層WAS也可以輕易地脫離開應用服務器的環境這一點與前面數據源的抽象非常類似
WAS本身對於事務劃分有兩種支持方式一種是聲明式的當然這種管理方式需要EJB容器的支持即所謂的容器管理事務(CMT)另外一種方式是編程式的通過程序代碼來直接使用JTA編程接口Spring對於事務的劃分也可以分為聲明式和編程式兩種方式對於Spring編程式的事務劃分方式總體上可以分為兩大類一類是通過直接使用實現PlatformTransactionManager接口的類另一類是通過使用TransactionTemplate模板類模板類的使用可以簡化事務控制代碼Spring對於聲明式事務劃分的支持實際上是利用了它的AOP機制相對於編程式事務劃分這種基於AOP的方式比較靈活而且對代碼的侵入性幾乎為零因此如果沒有特殊需要推薦使用這種事務劃分方式基於AOP的常用事務劃分方式可以使用ProxyFactoryBean加TransactionInterceptor方式或者使用TransactionPorxyFactoryBean的方式前一種方式相對比較靈活而後一種則對使用相對比較簡單
無論是哪一種事務劃分方式底層都需要一個事務管理機制作為支撐如果是單一的事務資源管理器那麼根據所使用的後台事務管理資源不同的類型可以選擇的PlatformTransactionManager實現有DataSourceTransactionManagerHibernateTransactionManager JdoTransactionManager PersistenceBrokerTransactionManager和JmsTransactionManager等無論是單個還是多個事務資源管理器都可以使用JtaTransactionManager類如果使用JtaTransactionManager那麼所有事務管理實際都會委托給底層應用服務器的JTA實現
例如如果使用JDBC或iBATIS那麼我們可以使用簡單的DataSourceTransactionManager外部配置文件片斷如下
<bean id=transactionManager
class=orgspringframeworkjdbcdatasourceDataSourceTransactionManager>
<property name=dataSource>
<ref local=dataSource />
</property>
</bean>
如果使用Hibernate那麼我們可以使用HibernateTransactionManager外部配置文件片斷如下
<bean id=transactionManager class=orgspringframeworkormhibernate
HibernateTransactionManager>
<property name=sessionFactory><ref local=sessionFactory/></property>
</bean>
使用WAS的JTA支持我們只需要把上述對應bean中的class屬性改成class屬性改為orgspringframeworktransactionjtaJtaTransactionManager然後再把屬性改為WebSphere對應的TransactionManager參考如下
<bean id=wasTxMgr
class=orgspringframeworktransactionjtaWebSphereTransactionManagerFactoryBean/>
<bean id=transactionManager
class=orgspringframeworktransactionjtaJtaTransactionManager>
<property name=transactionManager>
<ref local=wasTxMgr/>
</property>
</bean>
通過采用Spring的事務支持底層事務采用何種方式的決定就不必在一開始開發就做出決定因為我們能夠通過Spring的外部配置文件來進行切換真正的事務支持不過雖然也有第三方的JTA支持但是WAS能夠提供非常穩定的XA支持因此推薦使用WAS的JTA尤其是當應用涉及到分布事務處理的時候這樣無論應用涉及幾個事務資源都可以統一解決
如何加載Spring的JAR包
Spring框架的核心JAR包是springjar但是根據實際使用情況需要一些擴展JAR包和依賴JAR包那在WAS中如何處理這些JAR包文件呢?在Web應用中一個簡單而直接的處理方式放是把這些使用到的JAR文件都拷貝到對應的WEBINF/lib目錄下面這種方法雖然簡單但是當有多個Spring應用程序的時候這種處理方式就需要在每個應用的WEBINF/lib目錄下都拷貝一份相同的JAR文件這裡可以通過共享庫的方式來統一解決類庫共享這個問題
共享庫就是WAS專門用來解決不同應用程序之間共享JAR或本地庫文件的一種機制共享庫由一個名字一個JAVA類路徑和/或一個裝載JNI庫本地庫路徑組成它可以分別在單元節點和服務器級別定義但是共享庫定義了並不意味著它會被裝載只有當這個共享庫與某個應用程序或應用服務器關聯之後它才會被加載如果一個共享庫與一個應用程序關聯那麼這個共享庫由應用程序類加載器加載如果一個共享庫與應用服務器關聯那麼這個共享庫就需要一個專門定義的類加載器來加載這個類加載器需要用戶自己定義其操作如下選應用服務器比如server類加載器新建一個類加載器加載器與共享庫關聯
在創建這個類加載器之前一般都需要預先定義好共享庫 根據上面的介紹可知通過共享庫解決Spring應用的JAR包共享問題主要就是兩個步驟一是把Spring應用中需要共享的JAR包定義成為一個共享庫二是選定相應的WAS服務器實例把它與上面創建的共享庫關聯起來這樣此WAS服務器實例上的所有應用都能夠使用共享庫中定義的JAR包使用共享庫這種方式的時候要注意理解類的裝載次序和方式如果是這種與WAS服務器實例關聯的共享庫JAR包其類加載器在層次結構上在應用程序類加載器上面即是它的父加載器關於WAS的類裝載器結構和策略可以進一步參考WAS信息中心
結束語
Spring框架的核心內容並不依賴於任何容器但是顯然基於Web的應用是Spring主要的應用類型了解和使用Spring框架一方面可以簡化應用的開發和測試另一方也可以加深對JEE技術的理解另外輕量級的Web應用開發正在成為一種趨勢因此何樂而不為之上面所討論的只是Spring使用中常見的一些內容Spring框架自己也正變得越來越復雜當然SpringHibernate等框架中體現的一些思想也正被JEE 規范所借鑒尤其是EJB 中也有了控制反轉的應用和POJO的大量使用實際上無論是JEE技術標准還是Spring等框架其目的都是如何簡化企業應用的開發只是作為標准JEE要考慮的內容更為廣泛一些程度也更為深入一些
From:http://tw.wingwit.com/Article/program/Java/ky/201311/28856.html