使用自定义 classloader 的正确姿势

详细的原理就不多说了,网上一大把, 但是, 看了很多很多, 即使看了jdk 源码, 说了罗里吧嗦, 还是不很明白:

 

到底如何正确自定义ClassLoader, 需要注意什么

ExtClassLoader 是什么鬼

自定义ClassLoader具体是如何加载 类的。。

 

直接上代码:

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.util.Date;

import com.lk.AbcBean;


public class ClassLoaderLK extends ClassLoader {
    /**
     * @param args
     */
    public static void main(String[] args) {
//        this.class.getSystemClassLoader();
        
        String ext = "java.ext.dirs";
        System.out.println("java.ext.dirs :\n" + System.getProperty(ext));
        String cp = "java.class.path";
        System.out.println("java.class.path :\n" + System.getProperty(cp));
        
        ClassLoader currentClassloader = ClassLoaderLK.class.getClassLoader();
        
        String pp = "d:\\testcl\\";
        ClassLoaderLK cl = new ClassLoaderLK(currentClassloader, pp);
        
        System.out.println();
        System.out.println("currentClassloader is " + currentClassloader);
        System.out.println();
        String name = "com.lk.AbcBean.class";
        name = "com.lk.AbcBean";
        try {
            Class<?> loadClass = cl.loadClass(name);
            
            Object object = loadClass.newInstance();
            
//            AbcBean ss = (AbcBean) object; // 无法转换的 (1)
//            ss.greeting();  (1)
            
            System.out.println();
            System.out.println(" invoke some method !");
            System.out.println();
            
            Method method = loadClass.getMethod("greeting");
            method.invoke(object);
            
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    
    private ClassLoader parent = null; // parent classloader
    private String path;

    public ClassLoaderLK(ClassLoader parent, String path) {
        super(parent);
        this.parent = parent; // 这样做其实是无用的
        this.path = path;
    }

    public ClassLoaderLK(String path) {
        this.path = path;
    }
    
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
//        return super.loadClass(name);
        Class<?> cls = findLoadedClass(name);
        if (cls == null) {
//            cls = getSystemClassLoader().loadClass(name); (2)// SystemClassLoader 会从classpath下加载
//            if (cls == null) {(2)
            // 默认情况下, 当前cl的parent是 SystemClassLoader, 
            // 而当前cl的parent的parent 才是ExtClassLoader
                ClassLoader parent2 = getParent().getParent();
//                System.out.println("Classloader is : " + parent2); 
                
                try {
                    System.out.println("try to use ExtClassLoader to load class : " + name); 
                    cls = parent2.loadClass(name);
                } catch (ClassNotFoundException e) {
                    System.out.println("ExtClassLoader.loadClass :" + name + " Failed"); 
                }
//            }(2)
            
            if (cls == null) {
                System.out.println("try to ClassLoaderLK load class : " + name); 
                cls = findClass(name);
                
                if (cls == null) {
                    System.out.println("ClassLoaderLK.loadClass :" + name + " Failed"); 
                } else {
                    System.out.println("ClassLoaderLK.loadClass :" + name + " Successful"); 
                }
                
            } else {
                System.out.println("ExtClassLoader.loadClass :" + name + " Successful"); 
            }
        }
        return cls;
    }
    
    @Override
    @SuppressWarnings("rawtypes")
    protected Class<?> findClass(String name) throws ClassNotFoundException {
//        return super.findClass(name);
        System.out.println( "try findClass " + name);
        InputStream is = null;
        Class class1 = null;
        try {
            String classPath = name.replace(".", "\\") + ".class";
//            String[] fqnArr = name.split("\\."); // split("."); 是不行的, 必须split("\\.")
//            if (fqnArr == null || fqnArr.length == 0) {
//                System.out.println("ClassLoaderLK.findClass()");
//                fqnArr = name.split("\\.");
//            } else {
//                System.out.println( name  +  fqnArr.length);
//            }
            
            String classFile = path + classPath;
            byte[] data = getClassFileBytes(classFile );
            
            class1 = defineClass(name, data, 0, data.length);
            if (class1 == null) {
                System.out.println("ClassLoaderLK.findClass() ERR ");
                throw new ClassFormatError();
            }
            
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return class1;
    }

    private byte[] getClassFileBytes(String classFile) throws Exception {
        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();
    }
    
}

 

随便的一个java 类, 简单起见,就写一个bean吧

package com.lk;

import java.util.Date;

public class AbcBean {
    
    @Override
    public String toString() {
        return "AbcBean [name=" + name + ", age=" + age + "]";
    }
    
    String name;
    int age;
    Date birthDay;
    
    public Date getBirthDay() {
        return birthDay;
    }
    public void setBirthDay(Date birthDay) {
        this.birthDay = birthDay;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
    public void greeting() {
        System.out.println("AbcBean.greeting()");
    }
}

 

直接执行,结果:

currentClassloader is sun.misc.Launcher$AppClassLoader@513cf0

try to use ExtClassLoader to load class : com.lk.AbcBean
ExtClassLoader.loadClass :com.lk.AbcBean Failed
try to ClassLoaderLK load class : com.lk.AbcBean
try findClass com.lk.AbcBean
java.io.FileNotFoundException: d:\testcl\com\lk\AbcBean.class (系统找不到指定的路径。)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(FileInputStream.java:138)
    at java.io.FileInputStream.<init>(FileInputStream.java:97)
    at ClassLoaderLK.getClassFileBytes(ClassLoaderLK.java:172)
    at ClassLoaderLK.findClass(ClassLoaderLK.java:145)
    at ClassLoaderLK.loadClass(ClassLoaderLK.java:112)
    at ClassLoaderLK.main(ClassLoaderLK.java:38)
Exception in thread "main" java.lang.NullPointerException
    at ClassLoaderLK.main(ClassLoaderLK.java:40)
ClassLoaderLK.loadClass :com.lk.AbcBean Failed

 

将com.lk 目录全部复制到 d:\\testcl\\ 下,

currentClassloader is sun.misc.Launcher$AppClassLoader@513cf0

try to use ExtClassLoader to load class : com.lk.AbcBean ExtClassLoader.loadClass :com.lk.AbcBean Failed try to ClassLoaderLK load class : com.lk.AbcBean try findClass com.lk.AbcBean try to use ExtClassLoader to load class : java.lang.Object ExtClassLoader.loadClass :java.lang.Object Successful ClassLoaderLK.loadClass :com.lk.AbcBean Successful invoke some method ! try to use ExtClassLoader to load class : java.lang.String ExtClassLoader.loadClass :java.lang.String Successful try to use ExtClassLoader to load class : java.lang.System ExtClassLoader.loadClass :java.lang.System Successful try to use ExtClassLoader to load class : java.io.PrintStream ExtClassLoader.loadClass :java.io.PrintStream Successful AbcBean.greeting()

 

 

将AbcBean打包成 jar 放置到 jdk 下的jre 的ext目录 ( 打包成 zip 也是可行的! 但是rar是不行的!!!  why ? 估计zip和jar都是使用的java 的zip流, 而rar是后面产生的新格式,故没有被支持。另外, 仅仅拷贝class 过去也是不行的! )

执行结果:

currentClassloader is sun.misc.Launcher$AppClassLoader@513cf0

try to use ExtClassLoader to load class : com.lk.AbcBean
ExtClassLoader.loadClass :com.lk.AbcBean Successful

 invoke some method !

AbcBean.greeting()

 

可见ExtClassLoader 是如何作用了的吧!!

 

总结,

1 从 invoke some method 前后的日志,可见 类加载 的大致过程。

2 代码中 (1), 的部分是注释了的, 因为 不同类加载器加载的类是 不能直接cast的。。   但把(1),(2) 同时解开注释, 又可以了, 这是因为他们都是使用的系统类加载器, 自定义的类加载器相当于没有生效。。( 这个当然不是我们需要的结果。)

3 loadClass, findClass 两个方法的复写是必须的。 上面代码中的loadClass 的写法其实有点问题, 参照classloader 源码, 应该还需要一步: parent加载不上了, 使用bootstrap 加载, 不过感觉一般应该是用不上的—— 谁需要去替换 jdk 的rt.jar 的类 ??

4 ExtClassLoader 是去加载 jdk 下 jre  ext 目录的类似jar 的文件——  后缀是不是jar 不要紧, 内容是jar就行了。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的ClassLoader是一个关键组件,它负责将Java类加载到JVM中。JavaClassLoader可以分为三个层次:Bootstrap ClassLoader、Extension ClassLoader和Application ClassLoader自定义ClassLoader可以使得我们更好地控制Java类的加载过程,例如可以从特定的路径或者网络中加载类。 下面是一个简单的自定义ClassLoader的示例代码: ```java public class MyClassLoader extends ClassLoader { private String classPath; public MyClassLoader(String classPath) { this.classPath = classPath; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] data = loadClassData(name); return defineClass(name, data, 0, data.length); } private byte[] loadClassData(String name) { String fileName = classPath + File.separatorChar + name.replace('.', File.separatorChar) + ".class"; try { FileInputStream fis = new FileInputStream(fileName); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int len; byte[] buffer = new byte[1024]; while ((len = fis.read(buffer)) != -1) { baos.write(buffer, 0, len); } return baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } return null; } } ``` 上述代码中,我们继承了ClassLoader类,并实现了findClass方法,在该方法中,我们可以根据自己的需求去加载Java类。在示例中,我们从指定的路径中加载类的字节码文件,并将其转换为字节数组,最后调用defineClass方法生成Class对象。注意,这里的路径需要与ClassLoader所在的类路径相对应。 我们可以通过以下代码来使用自定义ClassLoader: ```java MyClassLoader myClassLoader = new MyClassLoader("/path/to/class/files"); Class<?> clazz = myClassLoader.loadClass("com.example.Test"); Object obj = clazz.newInstance(); Method method = clazz.getMethod("hello"); method.invoke(obj); ``` 上述代码中,我们通过自定义ClassLoader加载了Test类,并调用了hello方法。 需要注意的是,JavaClassLoader是一个层级结构,类的加载过程会从上至下依次进行,因此我们需要根据具体的需求来选择ClassLoader的层次。在自定义ClassLoader时,我们需要保证其所在的类路径与被加载的类所在的类路径相对应,否则就会出现ClassNotFoundException。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值