JAVA ClassLoader簡述

今天學習一下ClassLoader的工作流程,在網上找了些資料,整理如下:
1.ClassLoader簡述
JVM在運行時會產生三個ClassLoader,Bootstrap ClassLoader、Extension ClassLoader和AppClassLoader.
其中,Bootstrap是用C++編寫的,我們在Java中看不到它,是null。
Extension ClassLoader用來加載擴展類,即/lib/ext中的類。最後AppClassLoader纔是加載Classpath的。ClassLoader加載類用的是委託模型。即先讓Parent類(而不是Super,不是繼承關係)尋找,Parent找不到才自己找。
三者的關係爲:AppClassLoader的Parent是ExtClassLoader,而ExtClassLoader的Parent爲Bootstrap ClassLoader。
加載一個類時,首先BootStrap先進行尋找,找不到再由ExtClassLoader尋找,最後纔是AppClassLoader。
爲什麼要設計的這麼複雜呢?其中一個重要原因就是安全性。比如在Applet中,如果編寫了一個java.lang.String類並具有破壞性。假如不採用這種委託機制,就會將這個具有破壞性的String加載到了用戶機器上,導致破壞用戶安全。但採用這種委託機制則不會出現這種情況。因爲要加載java.lang.String類時,系統最終會由Bootstrap進行加載,這個具有破壞性的String永遠沒有機會加載。
//A.java
public class A{
  public static void main(String[] args){
  A a=new A();
  System.out.println(System.getProperty("java.ext.dirs"));
  System.out.println(a.getClass().getClassLoader());
  B b=new B();
  b.print();
 }
}
//B.java
 public class B{
  public void print(){
  System.out.println(this.getClass().getClassLoader());
}
1、我們將它放在Classpath中,則打印出
  sun.misc.Launcher$AppClassLoader@92e78c
  sun.misc.Launcher$AppClassLoader@92e78c
  可見都是由AppClassLoader來加載的。
2、我們將其放在%jre%/lib/ext/classes(即ExtClassLoader的加載目錄,其加載/lib/ext中的jar文件或者子目錄classes中的class文件)中。則會打印出:
  sun.misc.Launcher$ExtClassLoader
  sun.misc.Launcher$ExtClassLoader
3、我們將A.class放到%jre%/lib/ext/classes中,而將B.class放到classpaht中又會怎麼樣呢?結果是:
  sun.misc.Launcher$ExtClassLoader
  Exception in thread "main" java.lang.NoClassDefFoundError:B
  at A.main(A.java:6)
怎麼會這樣呢?這其中有一個重要的問題:A類當然是由ExtClassLoader來加載的,B類要由哪個加載呢?B類要由調用它自己的類的類加載器。也就是說,A調用了B,所以B由A的類加載器ExtClassLoader來加載。ExtClassLoader根據委託機制,先拜託Bootstrap加載,Bootstrap沒有找到。然後它再自己尋找B類,還是沒找到,所以拋出異常。
ExtClassLoader不會請求AppClassLoader來加載!你可能會想:這算什麼問題,我把兩個類放到一起不就行了?
呵呵,沒這麼簡單。比如JDBC是核心類庫,而各個數據庫的JDBC驅動則是擴展類庫或在classpath中定義的。
所以JDBC由Bootstrap ClassLoader加載,而驅動要由AppClassLoader加載。等等,問題來了,Bootstrap不會請求AppClassLoader加載類啊。
那麼,他們怎麼實現的呢?我就涉及到一個Context ClassLoader的問題,調用Thread.getContextClassLoader。
2.自定義ClassLoader.
  最簡單的ClassLoader就是繼承ClassLoader類,多態findClass方法。
/**
*@author guojianjun
*@create date Mar 31, 2010
*/


package testMain;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;

public class MyClassLoader extends ClassLoader {
  private String _classpath;

  public MyClassLoader(String classpath) {
    this._classpath = classpath;
  }

  protected Class<?> findClass(String className)
      throws ClassNotFoundException {
    Class clazz = this.findLoadedClass(className);
    if (null == clazz) {
      try {
        String classFile = getClassFile(className);
        FileInputStream fis = new FileInputStream(classFile);
        FileChannel fileC = fis.getChannel();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        WritableByteChannel outC = Channels.newChannel(baos);
        ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
        while (true) {
          int i = fileC.read(buffer);
          if (i == 0 || i == -1) {
            break;
          }
          buffer.flip();
          outC.write(buffer);
          buffer.clear();
        }
        fis.close();
        byte[] bytes = baos.toByteArray();
        clazz = defineClass(className, bytes, 0, bytes.length);
      } catch (FileNotFoundException e) {
        e.printStackTrace();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    return clazz;
  }

  private byte[] loadClassBytes(String className)
      throws ClassNotFoundException {
    try {
      String classFile = getClassFile(className);
      FileInputStream fis = new FileInputStream(classFile);
      FileChannel fileC = fis.getChannel();
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      WritableByteChannel outC = Channels.newChannel(baos);
      ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
      while (true) {
        int i = fileC.read(buffer);
        if (i == 0 || i == -1) {
          break;
        }
        buffer.flip();
        outC.write(buffer);
        buffer.clear();
      }
      fis.close();
      return baos.toByteArray();
    } catch (IOException fnfe) {
      throw new ClassNotFoundException(className);
    }
  }

  private String getClassFile(String clasName) {
    StringBuffer sb = new StringBuffer(_classpath);
    clasName = clasName.replace('.', File.separatorChar) + ".class";
    sb.append(File.separator + clasName);
    return sb.toString();
  }
}
測試類:
/**
*@author guojianjun
*@create date Mar 30, 2010
*/


package po;

public class Student {
  private String stId = "Anran";
  private String stName = "Anran";
  private TClass6 stClass = null;



    

  public TClass6 getStClass() {
    return stClass;
  }

  public void setStClass(TClass6 stClass) {
    this.stClass = stClass;
  }

  public String getStId() {
    return stId;
  }

  public void setStId(String stId) {
    this.stId = stId;
  }

  public String getStName() {
    return stName;
  }

  public void setStName(String stName) {
    this.stName = stName;
  }

    
}
//test
public class TestLoader {
  public static void main(String[] args) throws ClassNotFoundException,
      InstantiationException, IllegalAccessException, SecurityException,
      NoSuchMethodException, IllegalArgumentException,
      InvocationTargetException {
    MyClassLoader a = new MyClassLoader("D:\\");
    Class test = a.findClass("po.Student");
    Object o = test.newInstance();
    Method m = test.getDeclaredMethod("getStName", new Class[] {});
    System.out.println(m.invoke(o, new Object[] {}));
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章