Spring的輕量級的bean容器為業(yè)務(wù)對象(business objects)、DAO對象和資源(如:JDBC數(shù)據(jù)源或者Hibernate SessionFactorie等)對象提供了IoC類型的裝配能力。Spring使用一個xml格式的應(yīng)用配置文件為開發(fā)者提供了一種通過解析定制的屬性文件來手動管理單實例對象或者工廠對象的選擇性。由于Spring將非入侵性做為一個重要的目標(biāo),因此可以由Spring配置管理的bean對象均不需要依賴Spring自有的接口和類就可以通過它們的bean屬性完成配置。這個概念可以被應(yīng)用到任何環(huán)境中,無論你開發(fā)的是一個J2EE的web應(yīng)用還是一個桌面應(yīng)用甚至只是一個applet都可以。

??? 在使用Hibernate的應(yīng)用中, Spring的對DAO對象通常的事務(wù)管理特別應(yīng)該引起關(guān)注。它的目的就是分離數(shù)據(jù)訪問和事務(wù)處理,使事務(wù)性業(yè)務(wù)對象不與任何特殊的數(shù)據(jù)訪問或者事務(wù)策略綁在一起,從而不影響業(yè)務(wù)對象的可復(fù)用性。這種劃分既可以經(jīng)由事務(wù)模板(TransactionTemplate)用編程的方式實現(xiàn),也可以經(jīng)由面向方面(AOP)事務(wù)攔截器(TransactionTemplate)用聲明的方式實現(xiàn)。無論是本地的Hibernate / JDBC事務(wù),還是JTA事務(wù)都支持對象外的事務(wù)策略,這對于本地的無狀態(tài)會話Bean(Stateless Session Beans)是一個非常有用的選擇。

??? Spring的HibernateTemplate類提供了一個簡單的方式實現(xiàn)了Hibernate-based DAO對象而不必關(guān)心如何獲得Hibernate的Session實例,也不必關(guān)心多方參與的事務(wù)處理。無需使用try-catch塊,也無需進(jìn)行事務(wù)檢查。一個簡單的Hibernate訪問方法就完全解決了些麻煩! 無論是在多個DAO接口還是在多方事務(wù)的情況下,Spring使得多種DAO對象無縫地協(xié)同工作。例如:某些DAO對象可能是基于plain JDBC的實現(xiàn),更適合于經(jīng)由Spring的JdbcTemplate來避免手動的異常處理。

????? 你可以單獨地使用許多Spring特性,因為Spring的所有對象都是設(shè)計成可復(fù)用的JavaBean對象的集合。也不要因為Spring可以提供一個完整的應(yīng)該框架而氣餒!使用其他的Spring特性時,應(yīng)用配置概念是一個附加的特性,并不是一個必須的特性。無論如何,當(dāng)你要決定去構(gòu)建一個象Spring這樣的內(nèi)在的基礎(chǔ)架構(gòu)的時候,在使用Spring的路途上沒有什么范圍上的限制。

1. 介紹: 資源管理

?????? 典型的業(yè)務(wù)應(yīng)用系統(tǒng)常常由于重復(fù)的資源管理代碼而導(dǎo)致混亂。許多項目試著用自己的方法來解決這個問題,有時要為此付出失敗的代價,Spring針對適當(dāng)?shù)馁Y源管理提倡了一種引人注目的簡單方法:即經(jīng)由模板來倒置控制(Inversion of control),例如:基礎(chǔ)類使用回調(diào)接口,或者應(yīng)用AOP攔截器。其基礎(chǔ)核心是適當(dāng)?shù)馁Y源處理和將特殊的API異常轉(zhuǎn)換為一個unchecked的基礎(chǔ)異常。

?????? Spring引入了一個DAO異常層適用于任何數(shù)據(jù)訪問策略。對于直接的JDBC,JdbcTemplate類關(guān)注于連接處理,并且關(guān)注于對SQLException轉(zhuǎn)換為適當(dāng)?shù)腄ataAccessException,包括對特殊的數(shù)據(jù)庫SQL錯誤轉(zhuǎn)換為有意義的異常。 經(jīng)由不同的事務(wù)管理對象,Spring支持JTA和JDBC事務(wù)。Spring 也提供對Hibernate和JDO的支持,它的這種支持由與JdbcTemplate類的作用相類似的HibernateTemplate類和JdoTemplate類, 以及HibernateInterceptor類、JdoInterceptor類,還有Hibernate、JDO 事務(wù)管理類組成。

?????? 最主要的目的是要使應(yīng)用的層次分明,為此將數(shù)據(jù)訪問和事務(wù)處理同應(yīng)用對象分離開來。所有的業(yè)務(wù)對象都不再依賴數(shù)據(jù)訪問或者事務(wù)策略。不再有硬編碼的資源查找代碼,不再有難以替換的單例對象,也不再需要定制服務(wù)注冊。

????? 所有的單獨的數(shù)據(jù)訪問特性均無需依賴于Spring,可以單獨使用,無需讓Spring知道,同時也可以通過Spring的應(yīng)用配置(提供基于XML的配置和對普通JavaBean實例的交叉引用)來進(jìn)行裝配。在一個典型的Spring應(yīng)用中,大部分重要的對象都是普通的JavaBean:數(shù)據(jù)訪問模板對象(data access templates)、數(shù)據(jù)訪問對象(使用數(shù)據(jù)訪問模板對象的對象)、事務(wù)管理對象及業(yè)務(wù)對象(使用數(shù)據(jù)訪問對象和事務(wù)對象的對象),web表示分解對象、web控制對象(使用業(yè)務(wù)對象的對象)等等。

2. 應(yīng)用配置中的資源定義

??? 為了避免應(yīng)用對象將資源查找的代碼硬編碼,Spring允許在應(yīng)用配置中將一個如JDBC DataSource或者Hibernate SessionFactory定義為一個Bean。應(yīng)用對象如果需要訪問資源只需要通過Bean引用(DAO定義在下一部分說明)接受先前定義的實例的引用。以下的內(nèi)容引用自一個應(yīng)用配置定義,顯示了如何建立一個JDBC DataSource和一個Hibernate的SessionFactory:

?<beans>

<bean id="myDataSource" class="org.springframework.jndi

.JndiObjectFactoryBean">
<property name="jndiName">
<value>jdbc/myds</value>
</property>
</bean>

<bean id="mySessionFactory" class="org.springframework.orm.hibernate

.LocalSessionFactoryBean">
<property name="mappingResources">
<list>
<value>product.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">net.sf.hibernate.dialect

.MySQLDialect</prop>
</props>
</property>
<property name="dataSource">
<ref bean="myDataSource"/>
</property>
</bean>

...

</beans>

??? ? 注意選擇是用JNDI來定位數(shù)據(jù)源還是從一個象Jakarta Commons DBCP BasicDataSource這樣的本地定義取得一個數(shù)據(jù)源,只是一個改變配置的事:

<bean id="myDataSource"
????? class="org.apache.commons

.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName">
<value>org.hsqldb.jdbcDriver</value>
</property>
<property name="url">
<value>jdbc:hsqldb:hsql://localhost:9001</value>
</property>
<property name="username">
<value>sa</value>
</property>
<property name="password">
<value></value>
</property>
</bean>

???? ? 你也可以使用一個JNDI查找SessionFactory,但是通常對于EJB環(huán)境之外的應(yīng)用來說并不是需要的(參考"container resources vs local resources"部分的討論)。

3. 倒置控制(Inversion of Control): 模板和回調(diào)

??? 模板的基本編程模式就象你將在下面看到那樣,至于方法就如同任何定制的數(shù)據(jù)訪問對象或者業(yè)務(wù)的對象的方法一樣。除了需要向其提供一個Hibernate的SessionFactory之外,再沒有對周圍執(zhí)行對象的信賴的限制。雖然最好是從一個Spring的應(yīng)用配置中經(jīng)由一個簡單setSessionFactory bean的屬性設(shè)置使用Bean引用來獲得它,但隨后你可以從任何地方獲得它。隨后的引用片段包括一段在Spring應(yīng)用配置中對DAO定義的配置,其中引用了在其前面定義的SessionFactory,和一段DAO方法的實現(xiàn)的例子。

<beans>

<bean id="myProductDao" class="product.ProductDaoImpl">
<property name="sessionFactory">
<ref bean="mySessionFactory"/>
</property>
</bean>

...

</beans>

public class ProductDaoImpl implements ProductDao {

private SessionFactory sessionFactory;

public void setSessionFactory(SessionFactory 
   sessionFactory) {
this.sessionFactory = sessionFactory;
}

public List loadProductsByCategory(final String 
  category) {
HibernateTemplate hibernateTemplate =
new HibernateTemplate(this.sessionFactory);

return (List) hibernateTemplate.execute(
new HibernateCallback() {
public Object doInHibernate(Session session) throws
 HibernateException {
List result = session.find(
"from test.Product product where product.category=?",
category, Hibernate.STRING);
// do some further stuff with the result list
return result;
}
}
);
}
}

?????? 一個回調(diào)的實現(xiàn)可以被有效地用在任何Hibernate數(shù)據(jù)訪問中。在任何情況下都由HibernateTemplate來管理Session的開閉和自動的多方事務(wù)。模板實例是線程安全和可重用的,因此它們可以做為其他類的變量。


?????? 對于簡單的單步的動作,象find, load, saveOrUpdate或者delete的調(diào)用,HibernateTemplate提供更為便利的選擇以代替象一行的回調(diào)的執(zhí)行。此外,Spring提供了一個方便的基本類,就是HibernateDaoSupport類,它提供了setSessionFactory方法來接受一個SessionFactory,同時提供了getSessionFactory和getHibernateTemplate方法供其繼承類使用。將這些結(jié)合起來,允許對于典型的需求給出了非常簡單的DAO實現(xiàn):

public class ProductDaoImpl extends HibernateDaoSupport implements
 ProductDao {

public List loadProductsByCategory(String category) {
return getHibernateTemplate().find(
"from test.Product product where product.category=?", category,
Hibernate.STRING);
}
}

4. 應(yīng)用一個AOP攔截器代替一個模板

?? 除使用HibernateTemplate之外的另一個選擇就是使用Spring的AOP HibernateInterceptor。用直接在一個委托的try/catch塊中編寫Hibernate代碼,配合相應(yīng)的在應(yīng)用配置中分別的攔截器配置來代替執(zhí)行回調(diào)。下面的片段顯示了一個Spring應(yīng)用配置中的DAO, interceptor和proxy的各自的定義,同時給出了一個DAO方法實現(xiàn)的例子:

<beans>

...

<bean id="myHibernateInterceptor"
      class="org.springframework.orm.hibernate
.HibernateInterceptor">
<property name="sessionFactory">
<ref bean="mySessionFactory"/>
</property>
</bean>

<bean id="myProductDaoTarget" class="product.ProductDaoImpl">
<property name="sessionFactory">
<ref bean="mySessionFactory"/>
</property>
</bean>

<bean id="myProductDao" class="org.springframework.aop
.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>product.ProductDao</value>
</property>
<property name="interceptorNames">
<list>
<value>myHibernateInterceptor</value>
<value>myProductDaoTarget</value>
</list>
</property>
</bean>

...

</beans>

?
public class ProductDaoImpl extends HibernateDaoSupport 
   implements ProductDao {

public List loadProductsByCategory(final String category)
 throws MyException {
Session session = SessionFactoryUtils
.getSession(getSessionFactory(), false);
try {
List result = session.find(
"from test.Product product where product.category=?",
category, Hibernate.STRING);
if (result == null) {
throw new MyException("invalid search result");
}
return result;
}
catch (HibernateException ex) {
throw SessionFactoryUtils.convertHibernateAccessException(ex);
}
}
}

??????? 這個方法將只在有一個與它配合的HibernateInterceptor時才能正常工作,HibernateInterceptor為它負(fù)責(zé)在方法調(diào)用前線程綁定Session的開啟和方法調(diào)用后的關(guān)閉。getSession方法調(diào)用中的"false"標(biāo)志是要確認(rèn)Session必須是已經(jīng)存在的,如果沒有發(fā)現(xiàn)任何一個Session,SessionFactoryUtils將會為其創(chuàng)建一個。如果已經(jīng)有一個Session句柄綁定在本線程上,比如是由一個HibernateTransactionManager事務(wù)綁定的,在任何情況下SessionFactoryUtils會自動接入這個Session。HibernateTemplate在底層也使用SessionFactoryUtils,與以上說的方式基本是一樣的。

?????? HibernateInterceptor的主要益處是它允許在數(shù)據(jù)訪問代碼中拋出checked application exception,而HibernateTemplate由于受限于回調(diào)只能在其中拋出unchecked exceptions。注意到這點我們可以推遲各自的檢驗,同時在回調(diào)后拋出應(yīng)用異常。攔截方式的主要缺點是它需要在配置中進(jìn)行特殊的配置。HibernateTemplate在大多數(shù)情況下都是一種簡單好用的方法。

5. 程序事務(wù)劃分

?? 在這種底層的數(shù)據(jù)訪問服務(wù)之上,事務(wù)處理可以在更高的應(yīng)用層被劃分 ,形成一些操作。這里除了需要一個Spring的PlatformTransactionManager對象外,對于周圍運行的業(yè)務(wù)對象也沒有任何限制。同樣的,其后你可以從任何地方獲得它們,但是經(jīng)由Bean引用的方式通過setTransactionManage方法獲得更為適合,象productDAO要經(jīng)由一個setProductDao方法獲得一樣。下面的引用片段顯示了在一個Spring應(yīng)用配置中的事務(wù)管理對象和業(yè)務(wù)對象的定義,并且還提供了一個業(yè)務(wù)方法實現(xiàn)的例子:

<beans>

...

<bean id="myTransactionManager"
      class="org.springframework.orm.hibernate
.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="mySessionFactory"/>
</property>
</bean>

<bean id="myProductService" class="product.ProductServiceImpl">
<property name="transactionManager">
<ref bean="myTransactionManager"/>
</property>
<property name="productDao">
<ref bean="myProductDao"/>
</property>
</bean>

</beans>

?
public class ProductServiceImpl implements ProductService {

private PlatformTransactionManager transactionManager;
private ProductDao productDao;

public void setTransactionManager(PlatformTransactionManager 
  transactionManager) {
this.transactionManager = transactionManager;
}

public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}

public void increasePriceOfAllProductsInCategory(final String 
  category) {
TransactionTemplate transactionTemplate =
new TransactionTemplate(this.transactionManager);
transactionTemplate.setPropagationBehavior(TransactionDefinition
    .PROPAGATION_REQUIRED);
transactionTemplate.execute(
new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(TransactionStatus 
  status) {
List productsToChange = productDAO.loadProductsByCategory(category);
...
}
}
);
}
}

6. 聲明性事務(wù)劃分

?????? 我們還可以選擇使用Spring的AOP TransactionInterceptor通過在應(yīng)用配置中定義攔截器配置來代替事務(wù)劃分代碼的事務(wù)處理方式。這允許我們保持業(yè)務(wù)對象獨立于每個業(yè)務(wù)對象中重復(fù)的事務(wù)劃分代碼。此外,事務(wù)行為和隔離層次的變化可以通過一個配置文件來改變而不需要對業(yè)務(wù)對象的實現(xiàn)造成影響。

<beans>

...

<bean id="myTransactionManager"
      class="org.springframework.orm.hibernate
.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="mySessionFactory"/>
</property>
</bean>

<bean id="myTransactionInterceptor"
      class="org.springframework.transaction
.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref bean="myTransactionManager"/>
</property>
<property name="transactionAttributeSource">
<value>
product.ProductService.increasePrice*=PROPAGATION_REQUIRED
product.ProductService.someOtherBusinessMethod=
PROPAGATION_MANDATORY
</value>
</property>
</bean>

<bean id="myProductServiceTarget" class="product
.ProductServiceImpl">
<property name="productDao">
<ref bean="myProductDao"/>
</property>
</bean>

<bean id="myProductService" class="org.springframework.aop
.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>product.ProductService</value>
</property>
<property name="interceptorNames">
<list>
<value>myTransactionInterceptor</value>
<value>myProductServiceTarget</value>
</list>
</property>
</bean>

</beans>

?
public class ProductServiceImpl implements ProductService {

private ProductDao productDao;

public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}

public void increasePriceOfAllProductsInCategory(final String
 category) {
List productsToChange = this.productDAO
.loadProductsByCategory(category);
...
}
}

???? ?如同使用HibernateInterceptor一樣,TransactionInterceptor允許任何checked application exception從回調(diào)代碼中拋出,而TransactionTemplate受回調(diào)限制在其內(nèi)部拋出unchecked exceptions,在出現(xiàn)一個unchecked application exception的情況時,TransactionTemplate將引發(fā)一個回滾或者這個事務(wù)由應(yīng)用(通過事務(wù)狀態(tài))標(biāo)記為回滾。TransactionInterceptor默認(rèn)情況也是同樣的行為,但是允許為每一個方法制定回滾策略。
????? ?建立聲明性事務(wù)的一個便利的方式是使用TransactionProxyFactoryBean,特別是如果沒有其他AOP攔截器的話,TransactionProxyFactoryBean將聯(lián)合定義為代理的自身與一個特殊的目標(biāo)Bean的事務(wù)配置。這將減少一個代理Bean對應(yīng)一個目標(biāo)Bean的配置情況。此外,你不必指定哪個接口或者哪個類必須定義事務(wù)方法。

<beans>

...

<bean id="myTransactionManager"
      class="org.springframework.orm.hibernate
.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="mySessionFactory"/>
</property>
</bean>

<bean id="myProductServiceTarget" class="product
.ProductServiceImpl">
<property name="productDao">
<ref bean="myProductDao"/>
</property>
</bean>

<bean id="myProductService"
      class="org.springframework.transaction.interceptor
.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="myTransactionManager"/>
</property>
<property name="target">
<ref bean="myProductServiceTarget"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="increasePrice*">PROPAGATION_REQUIRED</prop>
<prop key="someOtherBusinessMethod">PROPAGATION_MANDATORY</prop>
</props>
</property>
</bean>

</beans>

7. 事務(wù)管理策略

????????? 對于Hibernate應(yīng)用來說,無論是TransactionTemplate還是TransactionInterceptor都是委托驗實際的事務(wù)處理給PlatformTransactionManager實例,可以是一個HibernateTransactionManager(由一個單一的Hibernate的SessionFactory,使用一個ThreadLocal Session)或者可以是一個JtaTransactionManager(代理容器的JTA子系統(tǒng))。甚至你可以使用一個自定義的PlatformTransactionManager實現(xiàn)。
????? ?如果選擇從本地Hibernate事務(wù)管理轉(zhuǎn)為由JTA來進(jìn)行事務(wù)管理,例如:當(dāng)你的應(yīng)用的部署面對分布的事務(wù)需求時,也僅僅是改變一下配置的事。只要簡單地將Hibernate的事務(wù)管理換為JTA事務(wù)實現(xiàn)即可。所有的事務(wù)劃分和數(shù)據(jù)訪問無需做任何變動仍可以繼續(xù)工作,因為他們使用的都是普通的事務(wù)管理API。
?????? 對于分布式的事務(wù)會跨越多個Hibernate的session factories,僅僅是聯(lián)合JtaTransactionManager與多個LocalSessionFactoryBean定義作為事務(wù)策略。你的每一個DAO將通過它們各自的Bean屬性得到一個特殊的SessionFactory的引用。如果這一切都是在下面的JDBC數(shù)據(jù)源是事務(wù)容器,一個業(yè)務(wù)對象可以劃分事務(wù)跨越很多DAO和很多session factories而無需做特別的處理,對于使用JtaTransactionManager做為事務(wù)策略也是一樣的。

?
<beans>

<bean id="myDataSource1" class="org.springframework.jndi
.JndiObjectFactoryBean">
<property name="jndiName">
<value>jdbc/myds1</value>
</property>
</bean>

<bean id="myDataSource2" class="org.springframework.jndi.
JndiObjectFactoryBean">
<property name="jndiName">
<value>jdbc/myds2</value>
</property>
</bean>

<bean id="mySessionFactory1"
      class="org.springframework.orm.hibernate.
LocalSessionFactoryBean">
<property name="mappingResources">
<list>
<value>product.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">net.sf.hibernate.dialect.
MySQLDialect</prop>
</props>
</property>
<property name="dataSource">
<ref bean="myDataSource1"/>
</property>
</bean>

<bean id="mySessionFactory2"
      class="org.springframework.orm.hibernate.
LocalSessionFactoryBean">
<property name="mappingResources">
<list>
<value>inventory.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">net.sf.hibernate.
dialect.OracleDialect</prop>
</props>
</property>
<property name="dataSource">
<ref bean="myDataSource2"/>
</property>
</bean>

<bean id="myTransactionManager"
      class="org.springframework.transaction.jta.
JtaTransactionManager"/>

<bean id="myProductDao" class="product.ProductDaoImpl">
<property name="sessionFactory">
<ref bean="mySessionFactory1"/>
</property>
</bean>

<bean id="myInventoryDao" class="product.InventoryDaoImpl">
<property name="sessionFactory">
<ref bean="mySessionFactory2"/>
</property>
</bean>

<bean id="myProductServiceTarget" class="product.
ProductServiceImpl">
<property name="productDao">
<ref bean="myProductDao"/>
</property>
<property name="inventoryDao">
<ref bean="myInventoryDao"/>
</property>
</bean>

<bean id="myProductService"
      class="org.springframework.transaction.interceptor.
TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="myTransactionManager"/>
</property>
<property name="target">
<ref bean="myProductServiceTarget"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="increasePrice*">PROPAGATION_REQUIRED</prop>
<prop key="someOtherBusinessMethod">PROPAGATION_MANDATORY</prop>
</props>
</property>
</bean>

</beans>

????? ?無論是HibernateTransactionManager還是JtaTransactionManager允許適當(dāng)?shù)膶ibernate的在JVM層次的緩存處理-不需要容器-提供特殊的事務(wù)查找或者JCA連接器(只要不使用EJB發(fā)起事務(wù))。另外,HibernateTransactionManager能輸出JDBC連接供通常的JDBC訪問代碼使用。這樣就允許在高層次上的事務(wù)劃分是混合了Hibernate與JDBC而不要JTA的,只要只是訪問一個數(shù)據(jù)庫就可以!

8. 使用Spring管理應(yīng)用的Bean

??????? 一個Spring應(yīng)用配置定義可以被多種配置實現(xiàn)所加載,從FileSystemXmlApplicationContext和ClassPathXmlApplicationContext到XmlWebApplicationContext。這就允許在各種環(huán)境下重用Spring管理的數(shù)據(jù)訪問和業(yè)務(wù)對象。默認(rèn)情況下,一個Web應(yīng)用將有它自己的定義在“WEB-INF/applicationContext.xml”中的根配置。
?????? 在任何一個Spring應(yīng)用中,一個應(yīng)用配置定義在一個XML格式的文件中用來對應(yīng)用的所有有關(guān)的Bean進(jìn)行裝配,從Hibernate的session factory到自定義的數(shù)據(jù)訪問和業(yè)務(wù)對象(象上面所有的Bean那樣)。他們中的大多數(shù)不需要Spring容器知道他們,甚至即使是與其他Bean合作時也一樣,因為他們只是簡單的JavaBean之間的協(xié)作。下面的Bean定義可能是一個Spring Web 的MVC配置中用來訪問業(yè)務(wù)對象的配置的一部分。

?
<bean id="myProductList" class="product.ProductListController">
<property name="productService">
<ref bean="myProductService"/>
</property>
</bean>

????? Spring的Web控制器經(jīng)由Bean引用擁有它們需要的所有的業(yè)務(wù)和數(shù)據(jù)訪問對象,因此它們無需在應(yīng)用配置中做任何手工的Bean查找。但是當(dāng)使用Spring管理的Beans用于Struts或者是在EJB實現(xiàn),或者一個applet中時常常是需要必須手工查找一個Bean的。因此Spring的Bean可以被用在任何地方。也許只是需要是一應(yīng)用配置的引用,或者經(jīng)由一個web容器的Servlet配置屬性,或者從一個文件中或者類路徑的資源中創(chuàng)建它。

ApplicationContext context =WebApplicationContextUtils.
getWebApplicationContext(servletContext);
ProductService productService =
(ProductService) context.getBean("myProductService");

?ApplicationContext context =
new FileSystemXmlApplicationContext("C:/myContext.xml");
ProductService productService =
(ProductService) context.getBean("myProductService");

?ApplicationContext context =
new ClassPathXmlApplicationContext("myContext.xml");
ProductService productService =
(ProductService) context.getBean("myProductService");

9. 容器資源VS本地資源

?????? Spring的資源管理允許簡單地在一個JNDI SessionFactory和一個本地SessionFactory間做選擇,同樣允許在一個JNDI DataSource與本地DataSource間做選擇,而無需改變應(yīng)用的一行代碼。在容器中保存資源定義還是在應(yīng)用本地保存,主要是一個事務(wù)策略方面的事。比較一個Spring定義的本地SessionFactory與一個手工注冊的JNDI SessionFactory沒有任何益處。如果經(jīng)由Hibernate的JCA連接器注冊,才會有加入JTA事務(wù)的明顯益處,特別是對EJB。

?????? 一個重要的Spring事務(wù)提供的好處是它不與任何容器綁定。定義包括JTA在內(nèi)的策略,它都可以獨立工作或者在一個試驗環(huán)境中工作。特別是對典型的一個數(shù)據(jù)庫的事務(wù)來說,對于JTA這是一個非常輕量的和強大的選擇。當(dāng)使用本地EJB SLSB的事務(wù)時,你將同時依賴EJB容器和JTA-即使你只是訪問一個數(shù)據(jù)庫,即使只是使用SLSBs經(jīng)由CMT來聲明事務(wù)。選擇使用 JTA編程也需要一個J2EE環(huán)境。

?????? 就JTA自身和JNDI數(shù)據(jù)源來說JTA不只是包括容器依賴。對于不使用Spring的JTA驅(qū)動的Hibernate事務(wù),你必須使用HibernateJCA連接器或者在合適的JVM緩沖層專門寫Hibernate的事務(wù)代碼配置JTA事務(wù)。在只訪問一個數(shù)據(jù)庫的情況下,Spring驅(qū)動的事務(wù)可以與一個本地定義的Hibernate的SessionFactory配合良好,就如同與一個本地JDBC數(shù)據(jù)源相配合一樣。因此當(dāng)面對分布的事務(wù)需求時,你只需要轉(zhuǎn)換為Spring的JTA事務(wù)策略即可。

????? ?要注意一個JCA連接器需要特別的容器的部署步驟,并且顯然首先得支持JCA。這比使用本地資源定義和Spring驅(qū)動事務(wù)來部署一個簡單的Web應(yīng)用有更多的爭議。而且你常常需要企業(yè)版本的容器支持,象WebLogic Express就不提供JCA。一個只用一個數(shù)據(jù)庫的使用本地資源和事務(wù)的Spring應(yīng)用可以在任何J2EE的Web容器中工作,Web容器不必支持JTA, JCA和EJB,如:Tomcat, Resin甚至最小的Jetty。另外,這樣一個中間層就可以很容易地在桌面應(yīng)用或者在測試套件中被重用。

?????? 所有考慮過的事情包括:如果你不使用EJB,堅持使用本地SessionFactory,使用SpringHibernateTransactionManager或者JtaTransactionManager,你將獲得包括適當(dāng)處理的JVM層的緩存和分布事務(wù)的所有益處,而無需引起任何關(guān)于容器部署的爭論。經(jīng)由JCA連接器的一個Hibernate的SessionFactory的JNDI注冊只是在使用EJB的情況中才會有明顯的附加值。


10. Skeletons和例子

????? 配置使用Spring和HIbernate的一個J2EE的Web應(yīng)用的注釋和細(xì)節(jié)最好去看看在Spring Framework的例子中的“典型的Web應(yīng)用”Skeletons,它給出了適合于JDBC 和 Hibernate應(yīng)用的多種數(shù)據(jù)源及事務(wù)管理的配置項,仔細(xì)看一下事務(wù)攔截器的配置,它也同樣向你展示了如何配置AOP攔截器。

????? 在Spring的1.0 M2版中,例子Petclinic提供了JDBC和Hibernate的DAO實現(xiàn)和應(yīng)用配置的選擇。Petclinic
可以作為一個可工作的簡單應(yīng)用說明如何在一個Spring web 應(yīng)用中使用Hibernate,同樣也包括根據(jù)不同的事務(wù)策略來聲明事務(wù)劃分。

Links

Spring Framework website

Spring Framework documentation