我剛剛在上一篇博文中將Spring對HibernateSession的管理做了一些皮毛的分析,主要圍繞着Spring是怎樣平衡Session的關閉時間。即在是否需要延時Session有效期以保證頁面的調用。
那么現在我們來看看Spring是怎樣管理Session的生產者:SessionFactory的。
首先,我們在學習Spring的時候一般都會涉及到將SSH框架整合起來使用了,還記得我們怎樣配置Spring嗎?
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans 3 xmlns="http://www.springframework.org/schema/beans" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xmlns:p="http://www.springframework.org/schema/p" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 7 8 …… 9 <bean id="dataSource" 10 class="org.apache.commons.dbcp.BasicDataSource"> 11 <property name="driverClassName" 12 value="oracle.jdbc.driver.OracleDriver"> 13 </property> 14 <property name="url" 15 value="jdbc:oracle:thin:@localhost:1521:orcl"> 16 </property> 17 <property name="username" value="……"></property> 18 <property name="password" value="……"></property> 19 </bean> 20 <bean id="sessionFactory" 21 class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 22 <property name="dataSource"> 23 <ref bean="dataSource"></ref> 24 </property> 25 <property name="hibernateProperties"> 26 <props> 27 <prop key="hibernate.dialect"> 28 org.hibernate.dialect.Oracle10gDialect 29 </prop> 30 <prop key="hibernate.hbm2ddl.auto">update</prop> 31 </props> 32 </property> 33 <property name="mappingResources"> 34 <list><value>orm/Users.hbm.xml</value></list> 35 </property> 36 </bean> 37 38 <bean id="userDao" class="……"> 39 <property name="sessionFactory" ref="sessionFactory"/> 40 </bean> 41 <bean id="userServer" class="……"> 42 <property name="userdao" ref="userDao"/> 43 </bean> 44 <bean id="userAction" class="……"> 45 <property name="userserver" ref="userServer"/> 46 </bean> 47 …… 48 </beans>
在不涉及到聲明性事務和自動裝配時我們一般按上述配置(上述配置為MyEclipse環境下的自動生成)。
不知道大家有沒有注意到:我們的Dao需要的SessionFactory是org.hibernate.SessionFactory(我們的Dao為了代碼重用,繼承自HibernateDaoSupport),而上面的裝配卻沒有給一個SessionFactory或其子類。
org.springframework.orm.hibernate3.support.HibernateDaoSupport源代碼(省略了注釋):
1 package org.springframework.orm.hibernate3.support; 2 3 import org.hibernate.HibernateException; 4 import org.hibernate.Session; 5 import org.hibernate.SessionFactory; 6 7 import org.springframework.dao.DataAccessException; 8 import org.springframework.dao.DataAccessResourceFailureException; 9 import org.springframework.dao.support.DaoSupport; 10 import org.springframework.orm.hibernate3.HibernateTemplate; 11 import org.springframework.orm.hibernate3.SessionFactoryUtils; 12 public abstract class HibernateDaoSupport extends DaoSupport { 13 14 private HibernateTemplate hibernateTemplate; 15 16 public final void setSessionFactory(SessionFactory sessionFactory) { 17 if (this.hibernateTemplate == null || sessionFactory != this.hibernateTemplate.getSessionFactory()) { 18 this.hibernateTemplate = createHibernateTemplate(sessionFactory); 19 } 20 } 21 22 protected HibernateTemplate createHibernateTemplate(SessionFactory sessionFactory) { 23 return new HibernateTemplate(sessionFactory); 24 } 25 26 public final SessionFactory getSessionFactory() { 27 return (this.hibernateTemplate != null ? this.hibernateTemplate.getSessionFactory() : null); 28 } 29 30 public final void setHibernateTemplate(HibernateTemplate hibernateTemplate) { 31 this.hibernateTemplate = hibernateTemplate; 32 } 33 34 public final HibernateTemplate getHibernateTemplate() { 35 return this.hibernateTemplate; 36 } 37 38 @Override 39 protected final void checkDaoConfig() { 40 if (this.hibernateTemplate == null) { 41 throw new IllegalArgumentException("'sessionFactory' or 'hibernateTemplate' is required"); 42 } 43 } 44 45 46 protected final Session getSession() 47 throws DataAccessResourceFailureException, IllegalStateException { 48 49 return getSession(this.hibernateTemplate.isAllowCreate()); 50 } 51 52 53 protected final Session getSession(boolean allowCreate) 54 throws DataAccessResourceFailureException, IllegalStateException { 55 56 return (!allowCreate ? 57 SessionFactoryUtils.getSession(getSessionFactory(), false) : 58 SessionFactoryUtils.getSession( 59 getSessionFactory(), 60 this.hibernateTemplate.getEntityInterceptor(), 61 this.hibernateTemplate.getJdbcExceptionTranslator())); 62 } 63 64 protected final DataAccessException convertHibernateAccessException(HibernateException ex) { 65 return this.hibernateTemplate.convertHibernateAccessException(ex); 66 } 67 68 protected final void releaseSession(Session session) { 69 SessionFactoryUtils.releaseSession(session, getSessionFactory()); 70 } 71 72 }
從源代碼可以看到,屬性所需類型確實為org.hibernate.SessionFactory(上述代碼16行處可看出)。
我是偶然發現這一點的,在后來,我順着LocalSessionFactoryBean的父類或接口向上追溯:
LocalSessionFactoryBean extends AbstractSessionFactoryBean implements BeanClassLoaderAware
AbstractSessionFactoryBean extends HibernateExceptionTranslator
implements FactoryBean<SessionFactory>, InitializingBean, DisposableBean
在經過分析后,我把重點放在了FactoryBean<SessionFactory>這個接口上,它實際上是一個泛型接口,原型如下:
org.springframework.beans.factory.FactoryBean源代碼(省略了注釋):
1 package org.springframework.beans.factory; 2 3 public interface FactoryBean<T> { 4 5 T getObject() throws Exception; 6 7 Class<?> getObjectType(); 8 9 boolean isSingleton(); 10 11 }
看到這里,我一度認為自己掌握了這種方式,於是我開始着手進行實驗(以下是我進行實驗的項目,只是一個添加了Spring支持的Java Project。注意添加Spring的aop支持):
1 import org.springframework.context.ApplicationContext; 2 import org.springframework.context.support.ClassPathXmlApplicationContext; 3 4 public class Test { 5 // main方法,從Spring容器中拿到對象 6 public static void main(String[] args) { 7 ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); 8 System.out.println(ac.getBean("tt")); 9 } 10 } 11 // 用作實體類,可以看做org.hibernate.SessionFactory 12 class E { 13 14 } 15 // 用做泛型接口,可以看作org.springframework.beans.factory.FactoryBean 16 interface MyFactoryBean<T> { 17 18 } 19 // 用做真正構造得到的類,可以看作org.springframework.orm.hibernate3.LocalSessionFactoryBean 20 class T implements MyFactoryBean<E> { 21 22 } 23 // 用作使用實體作為屬性的類,可以看作org.springframework.orm.hibernate3.support.HibernateDaoSupport 24 class M{ 25 private E e; 26 public void setE(E e){ 27 this.e = e; 28 } 29 }
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans 3 xmlns="http://www.springframework.org/schema/beans" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xmlns:p="http://www.springframework.org/schema/p" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 7 8 <!-- 我寫得不是很語義化,大家重在理解 --> 9 10 <!-- 簡單理解,此處的dd是實現了泛型接口的類 class T implements MyFactoryBean<E> 而實際上,它和E無關 --> 11 <!-- 此處的M需要一個E來作為屬性,它的set方法需要一個E類型的參數,但我們給它一個T --> 12 <bean id="dd" class="T"></bean> 13 <bean id="tt" class="M"> 14 <property name="e" ref="dd" /> 15 </bean> 16 </beans>
這樣就可以嗎?事實證明:NO!
ps:也對,這樣都行的話,國足能進世界杯(進過嗎?不清楚)。
得到一個異常(大家翻譯一下就明了):java.lang.IllegalStateException: Cannot convert value of type [T] to required type [E] for property 'e': no matching editors or conversion strategy found。
看到這個異常,我忽然想到:難道是因為沒有類似org.springframework.beans.factory.FactoryBean中的getObject方法?
於是,我連夜進行測試,在接口和實現中添加方法,改進后如下
1 interface MyFactoryBean<T> { 2 T getObject() throws Exception; 3 4 Class<?> getObjectType(); 5 6 boolean isSingleton(); 7 } 8 class T implements MyFactoryBean<E> { 9 10 @Override 11 public E getObject() throws Exception { 12 return new E(); 13 } 14 15 @Override 16 public Class<?> getObjectType() { 17 return E.class; 18 } 19 20 @Override 21 public boolean isSingleton() { 22 return false; 23 } 24 25 }
結果證明:純屬坑爹,這不是換湯不換葯嗎?
最后,我使用了Spring自帶的FactoryBean:
1 import org.springframework.beans.factory.FactoryBean; 2 //…… 3 class T implements FactoryBean<E> { 4 5 @Override 6 public E getObject() throws Exception { 7 return new E(); 8 } 9 10 @Override 11 public Class<?> getObjectType() { 12 return E.class; 13 } 14 15 @Override 16 public boolean isSingleton() { 17 return false; 18 } 19 20 } 21 //……
這才成功了!
由此可見,Spring用了某種轉換來搞定這個事情,其中設計到多種設計模式,下次再深入研究。
(最后編輯時間2012-10-11 17:32:29)