rainlife 发表于 2013-1-27 06:13:39

自定义ClassLoader

JVM在加载类的时候,都是通过ClassLoader的loadClass()方法来加载class的,loadClass(String name)方法:
public Class<?> loadClass(String name) throws ClassNotFoundException {return loadClass(name, false);    }
loadClass(String name)方法再调用loadClass(String name, boolean resolve)方法:
   - name - 类的二进制名称
   - resolve - 如果该参数为 true,则分析这个类
protected synchronized Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException    {// First, check if the class has already been loaded//JVM 规范规定ClassLoader可以在缓存保留它所加载的Class,如果一个Class已经被加载过,则直接从缓存中获取Class c = findLoadedClass(name);if (c == null) {    try {if (parent != null) {    c = parent.loadClass(name, false);} else {    c = findBootstrapClass0(name);}    } catch (ClassNotFoundException e) {      // If still not found, then invoke findClass in order      // to find the class.      c = findClass(name);    }}if (resolve) {    resolveClass(c);}return c;}
如果ClassLoader并没有加载这个class,则调用findBootstrapClass0:
private Class findBootstrapClass0(String name)throws ClassNotFoundException    {check();if (!checkName(name))    throw new ClassNotFoundException(name);return findBootstrapClass(name);    }
该方法会调用check()方法来判断这个类是否已经初始化,并且通过checkName(name)来判断由name指定的这个类是否存在
最后调用findBootstrapClass(name):
private native Class findBootstrapClass(String name)throws ClassNotFoundException;
而这个findBootstrapClass方法是一个native方法,这是我们的root loader,这个载入方法并非是由JAVA所写,而是C++写的,它会最终调用JVM中的原生findBootstrapClass方法来完成类的加载。

如果上面两个都找不到,则使用findClass(name)来查找指定类名的Class:
protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name);}
JDK5.0中的说明:
使用指定的二进制名称查找类。此方法应该被类加载器的实现重写,该实现按照委托模型来加载类。在通过父类加载器检查所请求的类后,此方法将被 loadClass 方法调用。默认实现抛出一个 ClassNotFoundException。
所以,我们在自定义类中,只需要重写findClass()即可。
MyClassLoader类:
public class MyClassLoader extends ClassLoader {    private String fileName;    public MyClassLoader(String fileName) {      this.fileName = fileName;    }    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 name) {      StringBuffer sb = new StringBuffer(fileName);      name = name.replace('.', File.separatorChar) + ".class";      sb.append(File.separator + name);      return sb.toString();    }}
该类中通过调用defineClass(String name, byte[] b, int off, int len)方法来定义一个类:
protected final Class<?> defineClass(String name, byte[] b, int off, int len)throws ClassFormatError    {return defineClass(name, b, off, len, null);    }
注:MyClassLoader加载类时有一个局限,必需指定.class文件,而不能指定.jar文件。该类中的大部分代码是从网上搜索到的,是出自一牛人之笔,只是不知道原帖在哪,希望不会被隐藏。
MainClassLoader类:
public class MainClassLoader {    public static void main(String[] args) {      try {            MyClassLoader tc = new MyClassLoader("F:\\OpenLib\\");            Class c = tc.findClass("Test");            c.newInstance();      } catch (ClassNotFoundException e) {            e.printStackTrace();         } catch (IllegalAccessException e) {            e.printStackTrace();      } catch (InstantiationException e) {            e.printStackTrace();         }    }}
最后是一个简单的Test测试类:
public class Test{public Test() {System.out.println("Test");}public static void main(String[] args) {System.out.println("Hello World");}}
页: [1]
查看完整版本: 自定义ClassLoader