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的一些常用操作進行了回調封裝,可以自己分析下。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章