我刚刚在上一篇博文中将Spring对HibernateSession的管理做了一些皮毛的分析,主要围绕着Spring是怎样平衡Session的关闭时间。即在是否需要延时Session有效期以保证页面的调用。
那么现在我们来看看Spring是怎样管理Session的生产者:SessionFactory的。
首先,我们在学习Spring的时候一般都会涉及到将SSH框架整合起来使用了,还记得我们怎样配置Spring吗?
1 27 8 …… 9 11 2013 1416 1718 19 22 37 3823 24 2526 3327 3228 org.hibernate.dialect.Oracle10gDialect29 30update 3134 3635
orm/Users.hbm.xml 39 4140 42 4443 45 47 ……4846
在不涉及到声明性事务和自动装配时我们一般按上述配置(上述配置为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 @Override39 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{ 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.SessionFactory12 class E {13 14 }15 // 用做泛型接口,可以看作org.springframework.beans.factory.FactoryBean16 interface MyFactoryBean{17 18 }19 // 用做真正构造得到的类,可以看作org.springframework.orm.hibernate3.LocalSessionFactoryBean20 class T implements MyFactoryBean {21 22 }23 // 用作使用实体作为属性的类,可以看作org.springframework.orm.hibernate3.support.HibernateDaoSupport24 class M{25 private E e;26 public void setE(E e){27 this.e = e;28 }29 }
1 27 8 9 10 11 12 13 14 1615
这样就可以吗?事实证明: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{ 2 T getObject() throws Exception; 3 4 Class getObjectType(); 5 6 boolean isSingleton(); 7 } 8 class T implements MyFactoryBean { 9 10 @Override11 public E getObject() throws Exception {12 return new E();13 }14 15 @Override16 public Class getObjectType() {17 return E.class;18 }19 20 @Override21 public boolean isSingleton() {22 return false;23 }24 25 }
结果证明:纯属坑爹,这不是换汤不换药吗?
最后,我使用了Spring自带的FactoryBean:
1 import org.springframework.beans.factory.FactoryBean; 2 //…… 3 class T implements FactoryBean{ 4 5 @Override 6 public E getObject() throws Exception { 7 return new E(); 8 } 9 10 @Override11 public Class getObjectType() {12 return E.class;13 }14 15 @Override16 public boolean isSingleton() {17 return false;18 }19 20 }21 //……
这才成功了!
由此可见,Spring用了某种转换来搞定这个事情,其中设计到多种设计模式,下次再深入研究。
欢迎您移步我们的交流群,无聊的时候大家一起打发时间:
或者通过QQ与我联系:
(最后编辑时间2012-10-11 17:32:29)