熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> JSP教程 >> 正文

一個簡單的自定義ClassLoader的實現

2013-11-15 11:47:17  來源: JSP教程 

  很多時候人們會使用一些自定義的ClassLoader 而不是使用系統的Class Loader大多數時候人們這樣做的原因是他們在編譯時無法預知運行時會需要那些Class特別是在那些appserver中比如tomcatAvalonphonixJboss中或是程序提供一些plugin的功能用戶可以在程序編譯好之後再添加自己的功能比如ant jxtashell等定制一個ClassLoader很簡單一般只需要理解很少的幾個方法就可以完成
  一個最簡單的自定義的ClassLoader從ClassLoader類繼承而來這裡我們要做一個可以在運行時指定路徑加載這個路徑下的class的ClassLoader
  通常我們使用ClassLoaderloadClass(String):Class方法通過給出一個類名就會得到一個相應的Class實例因此只要小小的改動這個方法就可以實現我們的願望了
  源碼
  
  protected synchronized Class loadClass(String name boolean resolve) throws ClassNotFoundException { // First check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c = parentloadClass(name false); }else{ c = findBootstrapClass(name); } }catch(ClassNotFoundException e){ // If still not found then call findClass in order // to find the class c = findClass(name); } } if (resolve) { resolveClass(c); } return c;}
  
  Source from ClassLoaderjava
  
  Firstcheck JavaAPI doc上面指出了缺省的loadClass方法所做的幾個步驟
   調用findLoadedClass(String):Class 檢查一下這個class是否已經被加載過了由於JVM 規范規定ClassLoader可以cache它所加載的Class因此如果一個class已經被加載過的話直接從cache中獲取即可
   調用它的parent 的loadClass()方法如果parent為空這使用JVM內部的class loader(即著名的bootstrap classloader)
   如果上面兩步都沒有找到調用findClass(String)方法來查找並加載這個class
  後面還有一句話在Java 版本以後鼓勵用戶通過繼承findClass(String)方法實現自己的class loader而不是繼承loadClass(String)方法
  既然如此那麼我們就先這麼做
  
  public class AnotherClassLoader extends ClassLoader { private String baseDir;private static final Logger LOG = LoggergetLogger(AnotherClassLoaderclass); public AnotherClassLoader (ClassLoader parent String baseDir) { super(parent); thisbaseDir = baseDir; } protected Class findClass(String name) throws ClassNotFoundException { LOGdebug(findClass + name); byte[] bytes = loadClassBytes(name); Class theClass = defineClass(name bytes byteslength);//A if (theClass == null) throw new ClassFormatError(); return theClass; } private byte[] loadClassBytes(String className) throws ClassNotFoundException { try { String classFile = getClassFile(className); FileInputStream fis = new FileInputStream(classFile); FileChannel fileC = fisgetChannel(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); WritableByteChannel outC = ChannelsnewChannel(baos); ByteBuffer buffer = ByteBufferallocateDirect(); while (true) { int i = fileCread(buffer); if (i == || i == ) { break; } bufferflip(); outCwrite(buffer); bufferclear(); } fisclose(); return baostoByteArray(); } catch (IOException fnfe) { throw new ClassNotFoundException(className); } } private String getClassFile(String name) { StringBuffer sb = new StringBuffer(baseDir); name = namereplace( FileseparatorChar) + class; sbappend(Fileseparator + name); return sbtoString(); }}
  
  [i]Ps:這裡使用了一些JDK的nio的代碼)[/i]
  很簡單的代碼關鍵的地方就在A處我們使用了defineClass方法目的在於把從class文件中得到的二進制數組轉換為相應的Class實例defineClass是一個native的方法它替我們識別class文件格式分析讀取相應的數據結構並生成一個class實例
  
  還沒完呢我們只是找到了發布在某個目錄下的class還有資源呢我們有時會用ClassgetResource():URL來獲取相應的資源文件如果僅僅使用上面的ClassLoader是找不到這個資源的相應的返回值為null
  
  同樣我們看一下原來的ClassLoader內部的結構
  
  public URL getResource(String name) { name = resolveName(name); ClassLoader cl = getClassLoader();//這裡 if (cl==null) { // A system class return ClassLoadergetSystemResource(name); } return clgetResource(name);}
  
  
  原來是使用加載這個class的那個classLoader獲取得資源
  
  
  public URL getResource(String name) { URL url; if (parent != null) { url = parentgetResource(name); } else { url = getBootstrapResource(name); } if (url == null) { url = findResource(name);//這裡 } return url;}
  
  
  
  
  這樣看來只要繼承findResource(String)方法就可以了修改以下我們的代碼
  
  
  //新增的一個findResource方法protected URL findResource(String name) { LOGdebug(findResource + name); try { URL url = superfindResource(name); if (url != null) return url; url = new URL(file:/// + converName(name)); //簡化處理所有資源從文件系統中獲取 return url; } catch (MalformedURLException mue) { LOGerror(findResource mue); return null; }}private String converName(String name) { StringBuffer sb = new StringBuffer(baseDir); name = namereplace( FileseparatorChar); sbappend(Fileseparator + name); return sbtoString();}
  
  
  好了到這裡一個簡單的自定義的ClassLoader就做好了你可以添加其他的調料(比如安全檢查修改class文件等)以滿足你自己的口味
  
  

From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19625.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.