個人認為,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; }}
在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。