Eclipse下使用J2EE客戶端和Hibernate

Eclipse加載各個插件時,每個插件採用單獨的線程,因此插件之間的同名對象是不同的,不能進行類型轉換。這樣的話,如果某些插件需要採用別的方式加載某些類庫,將不能與Eclipse本身加載的類進行轉換。此問題突出表現在J2EE客戶端接口上。

    爲什麼不同線程加載的同名類文件在JVM內部是不同的?請參考《深入Java虛擬機》一書,對其有比較詳細的描述。

    eclipse中,不同的插件由eclipse生成一個線程,由此線程負責相應插件需要的類文件的加載。這樣的好處是避免了不同插件之間可能存在的類文件的衝突(例如版本不一致引起的問題),但是也帶來了不同的插件之間加載的類不同的問題(例如假設兩個插件都加載了log4j,則兩個插件之間的log4j對於JVM來說是兩個不同的對象)。

    在採用J2EE結構時,通常提供給J2EE客戶端各種遠程接口。通過JNDI查找遠程Home接口,並進行強制類型轉換。例如:

ExampleHome home  =(ExampleHome)getContext().lookup(Example_JNDI_NAME);

注意,默認時,通過lookup查找來的對象是由sun.misc.Launcher加載的。但是在Eclipse插件中引用的ExampleHome是由eclipse本身加載的,所以上述轉換在eclipse環境中將產生cast error.解決的辦法就是讓ExampleHome這些遠程接口全部由sun.misc.Launcher加載,最簡便的辦法就是把這些包放入java系統路徑下,作爲系統組件由jvm加載。

    另外一種解決方式就是創建特定的InitialContextFactoryBuilder,使之從eclipse里加載。

    NamingManager具有一個靜態方法setInitialContextFactoryBuilder,可以安裝一個InitialContextFactoryBuilder。採用這點,參考java的實現,可以編寫特定的Context工廠類,從而從eclipse中加載相應的資源。這樣就可以避免轉換錯誤。如下代碼所示:

public static InitialContext getContext() throws NamingException {

    if (initialContext == null) {

    //同步

        synchronized (syncObj) {

           if (initialContext == null) {

               Hashtable props = new Hashtable();

              props.put(InitialContext.INITIAL_CONTEXT_FACTORY,

                         "org.jnp.interfaces.NamingContextFactory");

              props.put(InitialContext.PROVIDER_URL,

                         "jnp://127.0.0.1:1099");

              //使用自定義InitialContextFactoryBuilder

              NamingManager.setInitialContextFactoryBuilder(new

SoftappInitialContextFactoryBuilder());

              initialContext = new InitialContext(props);            

              }

           }

       }

       return initialContext;

    }

 

class SoftappInitialContextFactoryBuilder implements

InitialContextFactoryBuilder{

    public InitialContextFactory

createInitialContextFactory(Hashtable env) throws NamingException {

       InitialContextFactory factory;

       String className = env != null ?

             (String)env.get(Context.INITIAL_CONTEXT_FACTORY) : null;

       if (className == null) {

           NoInitialContextException ne = new

NoInitialContextException(

              "Need to specify class name in environment or system " +

              "property, or as an applet parameter, or in an " +

              "application resource file:  " +

               Context.INITIAL_CONTEXT_FACTORY);

           throw ne;

         }

         try {

              //注意此句,this.getClass().getClassLoader()獲得的類加載器

              //正是eclipse的類加載器,從而在調用getContext().lookup

//能得正確的類

factory = (InitialContextFactory)     

this.getClass().getClassLoader().

loadClass(className).newInstance();

         } catch(Exception e) {

              NoInitialContextException ne =

                  new NoInitialContextException(

                      "Cannot instantiate class: " + className);

              ne.setRootCause(e);

              throw ne;

         }

         return factory;

    }

 

    類似的問題存在於eclipse環境中使用hibernatehibernate在解析*.hbm.xml時會查找相應的類文件並進行加載。當hibernate本身作爲插件插入到eclipse中時,那麼*.hbm.xml對應的類文件必須放置到hibernate插件本身中去(可以通過庫的方式加載),否則也會由於加載的線程不同造成類轉換錯誤。

 

    另外注意,eclipse的插件不允許循環引用。

發佈了32 篇原創文章 · 獲贊 5 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章