天天看点

实现java中自定义类加载器

前言:

由于类的加载是采用委托模式,及先是父类加载器去加载加载失败才是子类

要实现一个类加载器不是复写loadClass而是复写findClass

源码主要逻辑就几句:

注意:这个findClass调用的源码:

    protected Class<?> loadClass(String name, boolean resolve)

        throws ClassNotFoundException

    {

        synchronized (getClassLoadingLock(name)) {

            // First, check if the class has already been loaded

            Class<?> c = findLoadedClass(name);

            if (c == null) {

                long t0 = System.nanoTime();

                try {

                    if (parent != null) {//如果有父类加载器就让父类去加载

                        c = parent.loadClass(name, false);

                    } else {//如果没有父类加载器这表明它是启动类加载器用它来加载

                        c = findBootstrapClassOrNull(name);

                    }

                } catch (ClassNotFoundException e) {

                    // ClassNotFoundException thrown if class not found

                    // from the non-null parent class loader

                }

                if (c == null) {

                    // If still not found, then invoke findClass in order

                    // to find the class.

                    long t1 = System.nanoTime();

                    c = findClass(name);//如果还没有早到就调用自己的findClass来加载

                    // this is the defining class loader; record the stats

                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);

                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);

                    sun.misc.PerfCounter.getFindClasses().increment();

                }

            }

            if (resolve) {

                resolveClass(c);

            }

            return c;

        }

    }

1. 复写findClass方法

2. 获取这个.class文件所对应的二进制流

3. 再在这个findClass最后返回 return defineClass(null, data, 0, data.length);

public class MyClassLoader extends ClassLoader{

   private String path;//这里用来记录加载的路径(相对eclipse所在项目)

   private final String fileType =".class";

   private int key =0;//默认不加密

   public MyClassLoader(String path){

    this.path =System.getProperty("user.dir")+"\\"+ path;//这个路径是你eclipse项目路径加上你输入的路径

   }

   public MyClassLoader() {

 super();

}

public MyClassLoader(ClassLoader parent) {

 super(parent);

}

public void setKey(int key) {

 this.key = key;

}

public void setPath(String path) {

 this.path = path;

}

private byte[] loadClassData(String name){//把要加载的.class变成二进制数组

    if(null==path)

     path = System.getProperty("user.dir");

    if(!path.endsWith("\\")){

     path+="\\";

    }

    String filename = path + name.substring(name.lastIndexOf(".")+1) +fileType;

    ByteArrayOutputStream bos = new ByteArrayOutputStream();//这个是将文件中获取的二进制存放用的

    try(

      BufferedInputStream bis =new BufferedInputStream(new FileInputStream(filename));

      ){

      byte[] buf  = new byte[1024];

      int len = -1;

      while((len=bis.read(buf))!=-1){

       for(int i=0;i<len;i++)

        buf[i]+=key;

       bos.write(buf, 0, len);

      }   

      return bos.toByteArray();

    }catch(Exception e){

     e.printStackTrace();

     throw new RuntimeException();

    }

   }

@Override

protected Class<?> findClass(String name) throws ClassNotFoundException {

  byte[] data = loadClassData(name);

 return defineClass(null, data, 0, data.length);

}

}

可能碰到的问题:

 1.总不能掉用你复写的findClass方法  原因:你调用loadClass的路径会被应用类加载器正确解析做法:如果你要加载的类在com.test.Hello (最后的Hello为类名)你可以写成loadClass(com.Hello) 不过你自己要在你的自定义的类加载器中给它拼去一个完整的路径。

      我的上面的做法时我只要你传人的类名 name.substring(name.lastIndex(".")+1) (因为loadClass(name) 父类加载不了时它会传人到findClass(name)) 余下的我自定义类中有path

2. 为什么我们加载成功后的类不能转成原有的对象而只能转成它的父类。

如果我们加载的类为Hello 它没有任何父类时它只能转成Object... 可能是我们defineClass中第一个参数传人null的原有

上面可能有不足如果有高手指点很乐意

继续阅读