Hibernate原始碼解析 Hibernate中的動態代理Javassist
阿新 • • 發佈:2019-02-20
今天,我們來看看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過程做了什麼事。為了簡單起見,為了便於關注問題點,我們用下面的測試程式碼:
現在你只需要知道從這兒開始就可以了。
JavassistProxyFactory#getProxy(Serializable, SessionImplementor)
在這裡有個事有必要提前說一下。
這裡的JavassistProxyFactory這類是在Hibernate啟動的時候建立的,由EntityPersister操刀建立的。一個POJO對應有一個EntityPersister,一個EntityPersister對應有一個ProxyFactory(JavassistProxyFactory)。想知道它什麼時候建立,方法很簡單,只需要在JavassistProxyFactory打個斷點就可以了。
下面正式來看看Javassist在這一場戰爭中做了什麼,戰鬥力如何。上面流程到這裡,繼續讀下面的程式碼(JavassistLazyInitializer只有一個私有構造)
在建立代理物件的時候,有沒有忽然發現有點不大對勁呢?那尼,factory已經存在?什麼時候發生的事?這種覺得就是,在戰爭跟敵方大戰好幾場,忽然發現,打都是自己的感覺。那種感覺,讓人瞬間就崩潰了。其它,前面已經說了,JavassistProxyFactory在Hibernate啟動的時候就建立了。其實,建立之後,EntityPersister還對它進行了初始化(JavassistProxyFactory#postInstance(……);)這部分如下:
上面這兩段程式碼告訴我們一件。先搞清楚一個關係,一個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。
在開始之前,說一句話:最近有點忙,有些日子沒寫部落格了。主要是沒幾個人看,因此也不怎麼想寫了。
好的,現在正式開始。
大家都知道Hibernate中load一個物件跟get一個物件是有區別的。簡單的說,load出來的是一個代理物件,而get出來是實實在在的POJO物件。也說是,load的過程做了延遲載入(Hibernate:lazy initializer proxy)。待用到這個物件的問題才去查詢資料庫,把記錄放到物件中。在此,Hibernate不向資料庫發SQL,這是load與get的區別。
今天的重點就是介紹load過程做了什麼事。為了簡單起見,為了便於關注問題點,我們用下面的測試程式碼:
-
Session session = sf.openSession();
- session.beginTransaction();
- UserVO user = null;
- user = (UserVO)session.load(UserVO.class, 2);
- session.getTransaction().commit();
現在你只需要知道從這兒開始就可以了。
JavassistProxyFactory#getProxy(Serializable, SessionImplementor)
在這裡有個事有必要提前說一下。
這裡的JavassistProxyFactory這類是在Hibernate啟動的時候建立的,由EntityPersister操刀建立的。一個POJO對應有一個EntityPersister,一個EntityPersister對應有一個ProxyFactory(JavassistProxyFactory)。想知道它什麼時候建立,方法很簡單,只需要在JavassistProxyFactory打個斷點就可以了。
下面正式來看看Javassist在這一場戰爭中做了什麼,戰鬥力如何。上面流程到這裡,繼續讀下面的程式碼(JavassistLazyInitializer只有一個私有構造)
- public HibernateProxy getProxy(Serializable id, SessionImplementor session) throws HibernateException {
- return JavassistLazyInitializer.getProxy(
- factory,
- entityName,
- persistentClass,
- interfaces,
- getIdentifierMethod,
- setIdentifierMethod,
- componentIdType,
- id,
- session,
- overridesEquals
- );
- }
- publicstatic HibernateProxy getProxy(
- final Class factory, final String entityName, final Class persistentClass,
- final Class[] interfaces, final Method getIdentifierMethod,
- final Method setIdentifierMethod, final CompositeType componentIdType,
- final Serializable id, final SessionImplementor session,
- finalboolean classOverridesEquals) throws HibernateException {
- final JavassistLazyInitializer instance = new JavassistLazyInitializer(
- entityName,
- persistentClass,
- interfaces, id,
- getIdentifierMethod,
- setIdentifierMethod,
- componentIdType,
- session,
- classOverridesEquals
- );
- final HibernateProxy proxy;
- try {
- proxy = ( HibernateProxy ) factory.newInstance(); // 建立代理物件
- }
- catch ( Exception e ) {
- thrownew HibernateException(
- "Javassist Enhancement failed: "
- + persistentClass.getName(), e
- );
- }
- ( ( ProxyObject ) proxy ).setHandler( instance );
- instance.constructed = true;
- return proxy;
- }
在建立代理物件的時候,有沒有忽然發現有點不大對勁呢?那尼,factory已經存在?什麼時候發生的事?這種覺得就是,在戰爭跟敵方大戰好幾場,忽然發現,打都是自己的感覺。那種感覺,讓人瞬間就崩潰了。其它,前面已經說了,JavassistProxyFactory在Hibernate啟動的時候就建立了。其實,建立之後,EntityPersister還對它進行了初始化(JavassistProxyFactory#postInstance(……);)這部分如下:
- publicvoid postInstantiate(final String entityName,
- final Set interfaces, final Method setIdentifierMethod,
- CompositeType componentIdType) throws HibernateException {
- this.entityName = entityName;
- this.persistentClass = persistentClass;
- this.interfaces = (Class[]) interfaces.toArray(NO_CLASSES);
- this.getIdentifierMethod = getIdentifierMethod;
- this.setIdentifierMethod = setIdentifierMethod;
- this.componentIdType = componentIdType;
- factory = JavassistLazyInitializer.getProxyFactory( persistentClass, this.interfaces );
- overridesEquals = ReflectHelper.overridesEquals(persistentClass);
- }
- publicstatic Class getProxyFactory(Class persistentClass,
- Class[] interfaces) throws HibernateException {
- // note: interfaces is assumed to already contain HibernateProxy.class
- try {
- ProxyFactory factory = new ProxyFactory();
- factory.setSuperclass( interfaces.length == 1 ? persistentClass : null );
- factory.setInterfaces( interfaces );
- factory.setFilter( FINALIZE_FILTER );
- return factory.createClass();
- } catch ( Throwable t ) {
- LOG.error(LOG.javassistEnhancementFailed(persistentClass.getName()), t);
- thrownew HibernateException(LOG.javassistEnhancementFailed(persistentClass.getName()), t);
- }
- }
上面這兩段程式碼告訴我們一件。先搞清楚一個關係,一個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。
- public Object invoke(final Object proxy,
- final Method thisMethod, final Method proceed,
- final Object[] args) throws Throwable {
- if ( this.constructed ) {
- Object result;
- try {
- result = this.invoke( thisMethod, args, proxy );
- }
- catch ( Throwable t ) {
- thrownew Exception( t.getCause() );
- }
- if ( result == INVOKE_IMPLEMENTATION ) {
- Object target = getImplementation();
- final Object returnValue;
- try {
- if ( ReflectHelper.isPublic( persistentClass, thisMethod ) ) {
- if ( !thisMethod.getDeclaringClass().isInstance( target ) ) {
- thrownew ClassCastException( target.getClass().getName() );
- }