• 使用自定义 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 :
    " + System.getProperty(ext));
            String cp = "java.class.path";
            System.out.println("java.class.path :
    " + 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:	estclcomlkAbcBean.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就行了。

  • 相关阅读:
    socketpair + signal + select 的套路
    java 远程调用 RPC
    Java 序列化
    Java Socket 编程
    云计算 IaaS,SaaS,PaaS的区别?一个通俗易懂的吃货文章
    如何应对网站反爬虫策略?如何高效地爬大量数据?
    java NIO详解
    LigerUI LigerGrid getSelectedRows() 多选顺序 不是从上到下修改方法
    JSON风格指南
    Json 工具介绍 fastjson gson jackson
  • 原文地址:https://www.cnblogs.com/FlyAway2013/p/5640586.html
Copyright © 2020-2023  润新知