Java 回调机制及其Spring 中HibernateTemplate的源码分析
在分析HibernateTemplate前,首先从网上了解下一些关于回调的一些概念。我相信在了解其原理实现的基础上,可以更好的进行开发和扩展,关键得理解其思想。
软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。
同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用。(简单来说就是顺序执行啦。)
回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口。(个人觉得有点像模板方法的实现。)
异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。(差不多就是异步执行吧。)
回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。同步调用是三者当中最简单的,而回调又常常是异步调用的基础。
由于本文章主要关注的是Java 的回调机制,所以其他就不介绍了。下面来看看Java 是怎么来实现回调机制的。package callback; /** * * @author zhxing * @since 2010.4.16 * */ public class Test{ private Callback callback; public void setCallback(Callback callback){ this.callback=callback; } //实际方法 public void foo(){ System.out.println("this is foo();"); //在这里调用回调函数 if(callback!=null){ callback.doSomething(); } } //测试 public static void main(String[] args) { Test t=new Test(); t.setCallback(new Callback(){ public void doSomething() { System.out.println("this is callback."); } }); t.foo(); } } //回调函数 interface Callback { void doSomething(); }
一般在我们数据库开发中,都会有打开和关闭数据、捕获异常等的一些固定的操作,写久了也就特别的反感了。是吧。。下面来利用回调简化数据库操作,例子很简单,对于数据的打开和关闭我只做了简单的描述。
package callback; public class UserDao { private UserTemplate template=new UserTemplate(); //这个实际上也是用回调方法,不过在UserTemplate 进行了封装 public void add(final String name){ this.template.add(name); } //这个是用回调方法来实现的 public String getName(){ return (String)this.template.execute(new UserCallback(){ public Object doSomething() { System.out.println("成功读取数据库。。。name= zhxing"); return "zhxing"; } }); } //测试 public static void main(String[] args) { UserDao u=new UserDao(); u.add("zhxing"); System.out.println(u.getName()); } } package callback; //回调函数的实现 public class UserTemplate { //调用回调函数,这里是不是有点像模板方法了,呵呵 public Object execute(UserCallback callback){ System.out.println("打开数据库连接"); Object o=callback.doSomething(); System.out.println("关闭数据库连接"); return o; } //这里用来简化常用的操作 public void add(final String name){ execute(new UserCallback(){ public Object doSomething() { System.out.println("name="+name+" 成功加入数据库。。。"); return null; } }); } } package callback; //回调函数接口 public interface UserCallback { Object doSomething(); }
好了,进入正题吧。。来研究下Spring 中HibernateTemplate 是怎么实现了。。其实上面的那个例子已经可以说明全部了,呵呵。。下面我们按照开发的流程来进行一些源码分析。
当我们用Spring+Hibernate实现Dao层的时候,一般会去继承HibernateDaoSupport这个类。
来分析下这个HibernateDaoSupport类(具体方法细节请查看源码):public abstract class HibernateDaoSupport extends DaoSupport { private HibernateTemplate hibernateTemplate; //Set the Hibernate SessionFactory to be used by this DAO. public final void setSessionFactory(SessionFactory sessionFactory) { } protected HibernateTemplate createHibernateTemplate(SessionFactory sessionFactory) { } //返回这个HiberanteTemplate 中的成员变量SessionFactory public final SessionFactory getSessionFactory() { } public final void setHibernateTemplate(HibernateTemplate hibernateTemplate) { this.hibernateTemplate = hibernateTemplate; } public final HibernateTemplate getHibernateTemplate() { return this.hibernateTemplate; } //检查HibernateTemplate是否为空,如果类中的HibernateTemplate未设置则会报错 protected final void checkDaoConfig() { if (this.hibernateTemplate == null) { throw new IllegalArgumentException("'sessionFactory' or 'hibernateTemplate' is required"); } } //获得Hibernate中的session,可以在HibernateTemplate中配置是否在Session没创建时新创建一个,在事务处理中很有用 protected final Session getSession() throws DataAccessResourceFailureException, IllegalStateException { return getSession(this.hibernateTemplate.isAllowCreate()); } //这里是获得Hibernate 的session 的主要方法 protected final Session getSession(boolean allowCreate) throws DataAccessResourceFailureException, IllegalStateException { return (!allowCreate ? SessionFactoryUtils.getSession(getSessionFactory(), false) : SessionFactoryUtils.getSession( getSessionFactory(), this.hibernateTemplate.getEntityInterceptor(), this.hibernateTemplate.getJdbcExceptionTranslator())); } //把HibernateException异常转换成DataAccessException异常 protected final DataAccessException convertHibernateAccessException(HibernateException ex) { return this.hibernateTemplate.convertHibernateAccessException(ex); } //释放Hibernate 的session protected final void releaseSession(Session session) { SessionFactoryUtils.releaseSession(session, getSessionFactory()); } }
从上面可知道,HibernateDaoSupport 的主要作用是获取Hibernate 的Session (如果直接用HibernateTemplate是没有办法获得Session 的)和管理HibernateTemplate。在Spring 中可以对直接继承该类,然后在Dao 的配置上就可以直接进行注入SessionFactory了,还可以直接从Dao中获取Hibernate的Session进行操作。 现在回到正题,进行分析下HibernateTemplate,先看下他的成员变量有哪些。
public class HibernateTemplate extends HibernateAccessor implements HibernateOperations { //是否在Session不存在的时候进行创建 private boolean allowCreate = true; //是否每次都生成新的Session private boolean alwaysUseNewSession = false; //是不是用原生的Hibernate 的session而不是用它的代理 private boolean exposeNativeSession = false; //检查Hibernate 的session 是否为只读模式 private boolean checkWriteOperations = true; //缓存Query private boolean cacheQueries = false; //设置Query缓存的名称 private String queryCacheRegion; //设置批量获取的大小 private int fetchSize = 0; //设置最大的数据集 private int maxResults = 0; }
HibernateTemplate 封装了很多Hibernate 的原始的操作,但把Hibernate 的session设置为了protected,而不能在它的子类外进行获取。下面来看看我们最常用get 和save 方法。
public Object get(Class entityClass, Serializable id) throws DataAccessException { return get(entityClass, id, null); } //实际get调用这个方法,增加了一个锁模式。 public Object get(final Class entityClass, final Serializable id, final LockMode lockMode) throws DataAccessException { //这里就是我们上面讨论的回调方法了。。 return executeWithNativeSession(new HibernateCallback() { public Object doInHibernate(Session session) throws HibernateException { if (lockMode != null) { return session.get(entityClass, id, lockMode); } else { return session.get(entityClass, id); } } }); } public Serializable save(final Object entity) throws DataAccessException { //这里也是用了回调,和上面一样的。 return (Serializable) executeWithNativeSession(new HibernateCallback() { public Object doInHibernate(Session session) throws HibernateException { checkWriteOperationAllowed(session); return session.save(entity); } }); }
下面来看看这个回调函数的方法。
protected Object doExecute(HibernateCallback action, boolean enforceNewSession, boolean enforceNativeSession) throws DataAccessException { Assert.notNull(action, "Callback object must not be null"); //判断是否强制新建Session Session session = (enforceNewSession ? SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor()) : getSession()); //判断是否设置了事务且是否强制新建session boolean existingTransaction = (!enforceNewSession && (!isAllowCreate() || SessionFactoryUtils.isSessionTransactional(session, getSessionFactory()))); if (existingTransaction) { logger.debug("Found thread-bound Session for HibernateTemplate"); } FlushMode previousFlushMode = null; try { //设置并返回FlushMode previousFlushMode = applyFlushMode(session, existingTransaction); //设置过滤器 enableFilters(session); //是否创建session 代理 Session sessionToExpose = (enforceNativeSession || isExposeNativeSession() ? session : createSessionProxy(session)); //这里调用回调函数了 Object result = action.doInHibernate(sessionToExpose); flushIfNecessary(session, existingTransaction); return result; } catch (HibernateException ex) { throw convertHibernateAccessException(ex); } catch (SQLException ex) { throw convertJdbcAccessException(ex); } catch (RuntimeException ex) { // Callback code threw application exception... throw ex; } finally { if (existingTransaction) { logger.debug("Not closing pre-bound Hibernate Session after HibernateTemplate"); disableFilters(session); if (previousFlushMode != null) { session.setFlushMode(previousFlushMode); } } else { // Never use deferred close for an explicitly new Session. if (isAlwaysUseNewSession()) { SessionFactoryUtils.closeSession(session); } else { //把session绑定到线程变量中(ThreadLocal) SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory()); } } } }
到这里基本就简单分析完了,关于HibernateTemplate的其他操作,其实都差不多的,对Hibernate的一些常用操作进行了回调封装,可以自己分析下。