Java程式設計極限考驗:ClassLoader類裝載策略

banq發表於2004-07-26
個人認為,Java程式設計中極限考驗是Classloader機制的掌握和靈活運用,特別是在複雜的系統,如存在動態類裝載,Reflect,EJB,AOP等環境。

CLass.forName()
和Thread.currentThread().getContextClassLoader())
是否一樣?

在很多文章中,都認為兩者是一致的,如Java研究組織中一篇文章,被我從google搜尋到的:
http://www.javaresearch.org/article/showarticle.jsp?column=31&thread=10178
文中說"這個方法可以用Class.forName()代替",在一般簡單情況是可以替代,但實際上有時候是不能替代的。

Classloader存在下面問題:
在一個JVM中可能存在多個ClassLoader,每個ClassLoader擁有自己的NameSpace。一個ClassLoader只能擁有一個class物件型別的例項,但是不同的ClassLoader可能擁有相同的class物件例項,這時可能產生致命的問題。如ClassLoaderA,裝載了類A的型別例項A1,而ClassLoaderB,也裝載了類A的物件例項A2。邏輯上講A1=A2,但是由於A1和A2來自於不同的ClassLoader,它們實際上是完全不同的,如果A中定義了一個靜態變數c,則c在不同的ClassLoader中的值是不同的。

因此,研究JBoss的ClassLoader策略,對於更好地實現EJB元件拼裝是用好處的,因為,一個專案中可能要用其他專案的EJB元件,如何實現執行時EJB元件共享,如何實現EJB元件打包是很重要的。


為了說明ClassLoader對於複雜架構是至重關鍵,列舉開源Portal產品Exo中ServivesManager類內容。

該類是Exo利用PicoCOntainer實現功能性Service JavaBeans初始化,在將那些Service性質的JavaBeans載入到pico中時,需要使用到Classloader,
Exo專門設立一個ServiceContext類:


public class ServiceContext {

  private ClassLoader cl;  //包含Classloader資訊
  private Services services;

  public ServiceContext(ClassLoader cl, Services services) {
    this.cl = cl;
    this.services = services;
  }

  public ClassLoader getCl() {
    return cl;
  }

  public Services getServices() {
    return services;
  }

}
<p class="indent">


在ServicesManager中,有:
private ClassLoader updatedClassLoader;

它的初始值是:
Thread.currentThread().getContextClassLoader();

如果,這裡寫Class.forName 那麼簡單,那麼你頭疼去吧。

但是這樣不夠:
在addService方法中,根據加入的不同ServiceContext實現類裝載:


 public void addService(ServiceContext context) {
    Services servicesToAdd = context.getServices();
    String name = servicesToAdd.getName();
    URLClassLoader cl = null;
    if(context.getCl() instanceof URLClassLoader) {
      cl = (URLClassLoader) context.getCl();
    } else {
      cl = URLClassLoader.newInstance(new URL[]{}, context.getCl());
    }

    updatedClassLoader = new URLClassLoader(cl.getURLs(), updatedClassLoader);
    synchronized (servicesContext) {
      servicesContext.put(name, context);
      reloadContainer();
    }
  }


其實向Picocontainer中加入一個服務很簡單,上述方法的主要程式碼是處理Classloader,考慮到Classloader有巢狀關係,上述程式碼小心使用這個Service服務的父Classloader,使用父Classloader裝載服務Service。


希望有興趣者一起討論。

相關文章