1. 程式人生 > >Hibernate原始碼解析 Hibernate中的動態代理Javassist

Hibernate原始碼解析 Hibernate中的動態代理Javassist

今天,我們來看看Hibernate中的動態代理,對動態代理有興趣的朋友可以讀一讀,看看Hibernate的作者們是怎麼實現的,為我們之後用的時候提供一點思路。正如你所知Hibernate動態代理並不是用Java原生的方式,而是用了Javassist。Javassist可以直接操縱位元組碼,因此它用代理的效率會高一些。當然,還有其它框架。比如CGLib、BECL.但CGLib停止更新了很久了,據說Hibernate的作者曾嘗試聯絡CGLib的作者,結果失敗了。另外,Javassist也是JBOSS的一員。
在開始之前,說一句話:最近有點忙,有些日子沒寫部落格了。主要是沒幾個人看,因此也不怎麼想寫了。


好的,現在正式開始。
大家都知道Hibernate中load一個物件跟get一個物件是有區別的。簡單的說,load出來的是一個代理物件,而get出來是實實在在的POJO物件。也說是,load的過程做了延遲載入(Hibernate:lazy initializer proxy)。待用到這個物件的問題才去查詢資料庫,把記錄放到物件中。在此,Hibernate不向資料庫發SQL,這是load與get的區別。
今天的重點就是介紹load過程做了什麼事。為了簡單起見,為了便於關注問題點,我們用下面的測試程式碼:
  1. Session session = sf.openSession();  
  2. session.beginTransaction();  
  3. UserVO user = null;  
  4. user = (UserVO)session.load(UserVO.class2);  
  5. session.getTransaction().commit();  
如果,你知道Hiberneate用Javassist的話,那你應該可以知道,今天的一切應該發生在org.hibernate.proxy.pojo.javassist這個包中。就算你不知道,它也是今天的主要戰場。如果,你想用傳統的方法找到這裡,也是可以的,直接在session.load(UserVO.class, 2);在這個方法打個斷點。然後一步一步的跟進去就可以了。大致流程請見文尾。

現在你只需要知道從這兒開始就可以了。
JavassistProxyFactory#getProxy(Serializable, SessionImplementor)

在這裡有個事有必要提前說一下。
這裡的JavassistProxyFactory這類是在Hibernate啟動的時候建立的,由EntityPersister操刀建立的。一個POJO對應有一個EntityPersister,一個EntityPersister對應有一個ProxyFactory(JavassistProxyFactory)。想知道它什麼時候建立,方法很簡單,只需要在JavassistProxyFactory打個斷點就可以了。

下面正式來看看Javassist在這一場戰爭中做了什麼,戰鬥力如何。上面流程到這裡,繼續讀下面的程式碼(JavassistLazyInitializer只有一個私有構造)
  1. public HibernateProxy getProxy(Serializable id, SessionImplementor session) throws HibernateException {  
  2.     return JavassistLazyInitializer.getProxy(  
  3.             factory,  
  4.             entityName,  
  5.             persistentClass,  
  6.             interfaces,  
  7.             getIdentifierMethod,  
  8.             setIdentifierMethod,  
  9.             componentIdType,  
  10.             id,  
  11.             session,  
  12.             overridesEquals  
  13.     );  
  14. }  
  15. publicstatic HibernateProxy getProxy(  
  16.         final Class factory, final String entityName, final Class persistentClass,  
  17.         final Class[] interfaces, final Method getIdentifierMethod,  
  18.         final Method setIdentifierMethod, final CompositeType componentIdType,  
  19.         final Serializable id, final SessionImplementor session,  
  20.         finalboolean classOverridesEquals) throws HibernateException {  
  21.     final JavassistLazyInitializer instance = new JavassistLazyInitializer(  
  22.             entityName,  
  23.             persistentClass,  
  24.             interfaces, id,  
  25.             getIdentifierMethod,  
  26.             setIdentifierMethod,  
  27.             componentIdType,  
  28.             session,  
  29.             classOverridesEquals  
  30.     );  
  31.     final HibernateProxy proxy;  
  32.     try {  
  33.         proxy = ( HibernateProxy ) factory.newInstance();  // 建立代理物件
  34.     }  
  35.     catch ( Exception e ) {  
  36.         thrownew HibernateException(  
  37.                 "Javassist Enhancement failed: "
  38.                 + persistentClass.getName(), e  
  39.         );  
  40.     }  
  41.     ( ( ProxyObject ) proxy ).setHandler( instance );  
  42.     instance.constructed = true;  
  43.     return proxy;  
  44. }  

在建立代理物件的時候,有沒有忽然發現有點不大對勁呢?那尼,factory已經存在?什麼時候發生的事?這種覺得就是,在戰爭跟敵方大戰好幾場,忽然發現,打都是自己的感覺。那種感覺,讓人瞬間就崩潰了。其它,前面已經說了,JavassistProxyFactory在Hibernate啟動的時候就建立了。其實,建立之後,EntityPersister還對它進行了初始化(JavassistProxyFactory#postInstance(……);)這部分如下:
  1. publicvoid postInstantiate(final String entityName,   
  2.         final Set interfaces, final Method setIdentifierMethod,   
  3.         CompositeType componentIdType) throws HibernateException {  
  4.     this.entityName = entityName;  
  5.     this.persistentClass = persistentClass;  
  6.     this.interfaces = (Class[]) interfaces.toArray(NO_CLASSES);  
  7.     this.getIdentifierMethod = getIdentifierMethod;  
  8.     this.setIdentifierMethod = setIdentifierMethod;  
  9.     this.componentIdType = componentIdType;  
  10.     factory = JavassistLazyInitializer.getProxyFactory( persistentClass, this.interfaces );  
  11.     overridesEquals = ReflectHelper.overridesEquals(persistentClass);  
  12. }  
  13. publicstatic Class getProxyFactory(Class persistentClass,  
  14.             Class[] interfaces) throws HibernateException {  
  15.     // note: interfaces is assumed to already contain HibernateProxy.class
  16.     try {  
  17.         ProxyFactory factory = new ProxyFactory();  
  18.         factory.setSuperclass( interfaces.length == 1 ? persistentClass : null );  
  19.         factory.setInterfaces( interfaces );  
  20.         factory.setFilter( FINALIZE_FILTER );  
  21.         return factory.createClass();  
  22.     } catch ( Throwable t ) {  
  23.         LOG.error(LOG.javassistEnhancementFailed(persistentClass.getName()), t);  
  24.         thrownew HibernateException(LOG.javassistEnhancementFailed(persistentClass.getName()), t);  
  25.     }  
  26. }  

上面這兩段程式碼告訴我們一件。先搞清楚一個關係,一個POJO(對Hibernate來說就是一個PersistentClass)有一個EntityPersister,一個EntityPersister只會建立一個ProxyFactory,建完之後存在EnttiyPersister中。因此,它就只初始化一次,結果就是ProxyFactory只被建立一次,也就是說每個都類都有自己的一個ProxyFactory(javassist.util.proxy.ProxyFactory)。這個物件用來生產Proxy物件,生產該POJO的代理物件。
這就是這兩段合夥欺騙我們的內幕。

這ProxyFactory是這樣的,它生產的每個都物件都繼續它的POJO並實現HibernateProxy。從這裡出去的只是一個半成品,它還會被JavassistProxyFactoryInitializer這個類加工。
注意一下,這個類實現了MethodHandler。還是MethodHandler這個介面來自javassist.util.proxy就說這麼多吧,接下來你自己猜吧。

算了,我還是厚道點,直接說了吧。這個介面,跟java.lang.reflect.InvocationHandler 一個作用。即是在呼叫被代理物件時,會調一下這裡面的invoke方法。(java se6 api中這麼說的:在代理例項上處理方法呼叫並返回結果。在與方法關聯的代理例項上呼叫方法時,將在呼叫處理程式上呼叫此方法。)
因此,它就可以當成(本來就是)MethodHandler的實現放入ProxyObject#setHandler()中,這一動作完成一個偉大的使命,它讓這個ProxyFactory生產出來的物件成為了成品貨了。

不難知道,之後它的代理物件的方法被呼叫時,都會調MethodHandler#invoke(……),這裡的MethodHandler的具體實現為JavassistProxyFactoryInitializer。
  1. public Object invoke(final Object proxy,  
  2.         final Method thisMethod, final Method proceed,  
  3.         final Object[] args) throws Throwable {  
  4.     if ( this.constructed ) {  
  5.         Object result;  
  6.         try {  
  7.             result = this.invoke( thisMethod, args, proxy );  
  8.         }  
  9.         catch ( Throwable t ) {  
  10.             thrownew Exception( t.getCause() );  
  11.         }  
  12.         if ( result == INVOKE_IMPLEMENTATION ) {  
  13.             Object target = getImplementation();  
  14.             final Object returnValue;  
  15.             try {  
  16.                 if ( ReflectHelper.isPublic( persistentClass, thisMethod ) ) {  
  17.                     if ( !thisMethod.getDeclaringClass().isInstance( target ) ) {  
  18.                         thrownew ClassCastException( target.getClass().getName() );  
  19.                     }  
  20.