Java基础进阶——“ClassLoader之二——自定义ClassLoader”

1704阅读 0评论2012-02-09 qhw
分类:Java

为什么要使用自定义ClassLoader 很多时候人们会选择使用自定义的ClassLoader,而不使用系统的ClassLoader。这样做的原因是,在编译时无法预知运行时会需要哪些Class,特别是在一些AppServer中,比如Tomcat、Avalon-Phonix、Jboss;或是程序提供某种插件(Plug-In)的特性,让用户可以在只拥有程序二进制代码的情况下添加自己的功能,比如Ant、 Jxta-shell等。

ClassLoader内部结构 通常定制一个ClassLoader很简单,一般只需要很少的几个步骤就可以完成。 Java规范规定,所有的用户自定义ClassLoader都必须从抽象类“java.lang.ClassLoader”类继承而来。下面先看一下这个类的内部实现,以帮助我们更好的理解相关内容。
  1. protected synchronized Class<?> loadClass(String name, boolean resolve)
  2.     throws ClassNotFoundException
  3.     {
  4.     // First, check if the class has already been loaded
  5.     Class c = findLoadedClass(name);
  6.     if (c == null) {
  7.      try {
  8.         if (parent != null) {
  9.          c = parent.loadClass(name, false);
  10.         } else {
  11.          c = findBootstrapClass0(name);
  12.         }
  13.      } catch (ClassNotFoundException e) {
  14.      // If still not found, then invoke findClass in order
  15.      // to find the class.
  16.      c = findClass(name);
  17.      }
  18.     }
  19.     if (resolve) {
  20.      resolveClass(c);
  21.     }
  22.     return c;
  23.     }
通常我们使用ClassLoader.loadClass(String name):Class根据指定的类名得到一个相应的Class实例。从Java源代码中我们可以看到,缺省的ClassLoader做了如下的工作: 1. 调用FindLoadedClass(String):Class 检查一下这个Class是否已经被加载过了。由于JVM 规范规定ClassLoader可以在缓存保留它所加载的Class,因此如果一个Class已经被加载过,直接从缓存中获取即可。 2. 调用它的父类的LoadClass()方法,如果它的父类不为空,则使用JVM内部的ClassLoader(即著名的BootstrapClassloader)来加载这个Class。在第10行我们可以看到使用了一个Native方法来调用这个Bootstrap classloader。 3. 如果上面两步都没有找到,调用findClass(String):Class方法来查找并加载这个Class。 因此我们只要覆盖这个findClass(String):Class方法即可达到定义ClassLoader的要求。
1.FileClassLoader.java
  1. package jack.classloader.own.local;
  2. //Load local class file
  3. import java.io.ByteArrayOutputStream;
  4. import java.io.File;
  5. import java.io.FileInputStream;

  6. public class FileClassLoader extends ClassLoader {
  7.     
  8.     public Class<?> findClass(String name){
  9.         byte [] data = loadClassData(name);
  10.         return defineClass(name, data, 0, data.length);
  11.     }
  12.     
  13.     private byte[] loadClassData(String name){
  14.         FileInputStream fis = null;
  15.         byte [] data = null;
  16.         try {
  17.             fis = new FileInputStream(
  18.                 new File(getFinalPath(name)));
  19.             ByteArrayOutputStream baos = new ByteArrayOutputStream();
  20.             int ch = 0;
  21.             while ((ch = fis.read()) != -1) {
  22.                 baos.write(ch);
  23.             }
  24.             data = baos.toByteArray();
  25.         } catch (Exception e) {
  26.             e.printStackTrace();
  27.         }
  28.         return data;
  29.     }
  30.     
  31.     private String getFinalPath(String name){
  32.         String finalPath = "";
  33.         String path = System.getProperty("user.dir");
  34.         String [] nameStrings = name.split("\\.");
  35.         String tempStr = "";
  36.         for(String str : nameStrings){
  37.             tempStr = tempStr + str + File.separatorChar;
  38.         }
  39.         tempStr = tempStr.substring(0, tempStr.lastIndexOf(File.separatorChar));
  40.         finalPath = path + File.separator + "bin" + File.separator + tempStr + ".class";
  41.         return finalPath;
  42.     }
  43. }
2.RemoteClassLoader.java
  1. package jack.classloader.own.remote;


  2. //Looa remote class file
  3. import java.io.InputStream;

  4. import java.net.URL;

  5. import java.net.URLConnection;



  6. public class RemoteClassLoader extends ClassLoader {

  7.     

  8.     public Class<?> findClass(String name){

  9.         byte [] data = loadClassData(name);

  10.         name = "jack.classloader.test." + name;

  11.         return defineClass(name, data, 0, data.length);

  12.     }

  13.     

  14.     private byte[] loadClassData(String name){

  15.         byte [] data = null;

  16.         try {

  17.             URL urlRemote = new URL("" + name + ".class");//须保证远程文件地址可达

  18.             URLConnection uConnection = urlRemote.openConnection();

  19.             InputStream inputStream = uConnection.getInputStream();

  20.             int length = uConnection.getContentLength();

  21.             data = new byte[length];

  22.             inputStream.read(data);

  23.             inputStream.close();

  24.             return data;

  25.         } catch (Exception e) {

  26.             System.out.println(name +"类没有找到!");

  27.             e.printStackTrace();

  28.         }

  29.         return data;

  30.     }

  31. }
3.MyApp.java
  1. package jack.classloader.own;

  2. import jack.classloader.own.local.FileClassLoader;
  3. import jack.classloader.own.remote.RemoteClassLoader;
  4. import jack.classloader.test.IService;

  5. public class MyApp {

  6.     /**
  7.      * @param args
  8.      * @throws IllegalAcces***ception
  9.      * @throws InstantiationException
  10.      */
  11.     @SuppressWarnings("unchecked")
  12.     public static void main(String[] args) throws InstantiationException, IllegalAcces***ception {
  13.         //local
  14.         FileClassLoader loader = new FileClassLoader();
  15.         Class<?> objClass = loader.findClass("jack.classloader.test.Service");
  16.         IService service = (IService)objClass.newInstance();
  17.         System.out.println(objClass.getName());
  18.         System.out.println(objClass.getClassLoader());
  19.         System.out.println(service);
  20.         service.service();
  21.         
  22.         System.out.println();
  23.         //remote
  24.         RemoteClassLoader remoteLoader = new RemoteClassLoader();
  25.         Class<?> objClassRemote = remoteLoader.findClass("Service");
  26.         IService serviceRemote = (IService)objClassRemote.newInstance();
  27.         serviceRemote.service();
  28.     }

  29. }
上一篇:Java基础进阶——Java IO实例
下一篇:Java基础进阶——“Java反射之一”