继承ClassLoader实现一个简单的自定义类加载器,可加载非项目的类文件

作者: admin 分类: JAVA 发布时间: 2019-08-06 14:15  阅读: 58 views

有时候,需要做自定义类加载器,业务场景类似tomcat

a)、要保证部署在tomcat上的每个应用依赖的类库相互独立,不受影响。
b)、由于tomcat是采用java语言编写的,它自身也有类库依赖,为了安全考虑,tomcat使用的类库要与部署的应用的类库相互独立。
c)、有些类库tomcat与部署的应用可以共享,比如说servlet-api,使用maven编写web程序时,servlet-api的范围是provided,
表示打包时不打包这个依赖,因为我们都知道服务器已经有这个依赖了。
d)、部署的应用之间的类库可以共享。这听起来好像与第一点相互矛盾,但其实这很合理,类被类加载器加载到虚拟机后,
会生成代表该类的class对象存放在永久代区域,这时候如果有大量的应用使用spring来管理,如果spring类库不能共享,
那每个应用的spring类库都会被加载一次,将会是很大的资源浪费。

先看下classLoader中的注释,要做自定义类加载器,需要有两个方法
findClass(String name) 和 loadClassData(String name)

 * <p> The network class loader subclass must define the methods {@link
 * #findClass <tt>findClass</tt>} and <tt>loadClassData</tt> to load a class
 * from the network.  Once it has downloaded the bytes that make up the class,
 * it should use the method {@link #defineClass <tt>defineClass</tt>} to
 * create a class instance.  A sample implementation is:
 *
 * <blockquote><pre>
 *     class NetworkClassLoader extends ClassLoader {
 *         String host;
 *         int port;
 *
 *         public Class findClass(String name) {
 *             byte[] b = loadClassData(name);
 *             return defineClass(name, b, 0, b.length);
 *         }
 *
 *         private byte[] loadClassData(String name) {
 *             // load the class data from the connection
 *              . . .
 *         }
 *     }
 * </pre></blockquote>

以下是继承 classLoader类后的一个简单实现

package com.chl.classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 *
 * 自定义类加载器
 * @author chenhailong
 * @date 2019年5月22日 下午7:22:50
 */
public class MyClassLoader extends ClassLoader {

    private String root;

    public String getRoot() {
        return root;
    }

    public void setRoot(String root) {
        this.root = root;
    }

    /**
     * 找到类
     */
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = loadClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            return defineClass(name, classData, 0, classData.length);
        }
    }

    /**
     * 加载类
     * @param className
     * @return
     */
    @SuppressWarnings("resource")
    private byte[] loadClassData(String className) {
        String fileName = root + File.separatorChar + className.replace('.', File.separatorChar) + ".class";
        try {
            InputStream ins = new FileInputStream(fileName);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int bufferSize = 1024;
            byte[] buffer = new byte[bufferSize];
            int length = 0;
            while ((length = ins.read(buffer)) != -1) {
                baos.write(buffer, 0, length);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {

        MyClassLoader cc = new MyClassLoader();
        cc.setRoot("/Users/chenhailong/Desktop");
        Class<?> testClass = null;
        try {
            testClass = cc.loadClass("com.base.test.Fast");
            Object object = testClass.newInstance();
            Method[] mm = testClass.getMethods();
            for (Method m : mm) {
                System.out.println(
                        "方法名称:" + m.getName() + "-参数个数:" + m.getParameterCount() + "-返回类型" + m.getReturnType());
            }

            try {
                Method main = testClass.getMethod("main", String[].class);
                String[] s = new String[1];
                main.invoke(object, s);

            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }

            System.out.println("");
            System.out.println("当前加载器:" + object.getClass().getClassLoader());
            System.out.println("父级加载器:" + object.getClass().getClassLoader().getParent());
            System.out.println("父父级加载器:" + object.getClass().getClassLoader().getParent().getParent());
            System.out.println("父父父级加载器:" + object.getClass().getClassLoader().getParent().getParent().getParent());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

}

由于要做个加载外部类的例子。所以需要自建一个类,并有方法。如下:


package com.base.test; public class Fast{ public static void main(String[] args) { System.out.println("--"); } public void test(String s) { System.out.println("输出参数:"+s); } }

如果没有定义上面类,或者存放位置不对的话,会报错如下

java.io.FileNotFoundException: /Users/chenhailong/Desktop/com/base/test/Fast.class (No such file or directory)
    at java.io.FileInputStream.open0(Native Method)
    at java.io.FileInputStream.open(FileInputStream.java:195)
    at java.io.FileInputStream.<init>(FileInputStream.java:138)
    at java.io.FileInputStream.<init>(FileInputStream.java:93)
    at com.chl.classloader.MyClassLoader.loadClassData(MyClassLoader.java:42)
    at com.chl.classloader.MyClassLoader.findClass(MyClassLoader.java:30)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at com.chl.classloader.MyClassLoader.main(MyClassLoader.java:63)
java.lang.ClassNotFoundException
    at com.chl.classloader.MyClassLoader.findClass(MyClassLoader.java:32)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at com.chl.classloader.MyClassLoader.main(MyClassLoader.java:63)

如果正常的话,信息如下:

方法名称:main-参数个数:1-返回类型void
方法名称:test-参数个数:1-返回类型void
方法名称:wait-参数个数:2-返回类型void
方法名称:wait-参数个数:1-返回类型void
方法名称:wait-参数个数:0-返回类型void
方法名称:equals-参数个数:1-返回类型boolean
方法名称:toString-参数个数:0-返回类型class java.lang.String
方法名称:hashCode-参数个数:0-返回类型int
方法名称:getClass-参数个数:0-返回类型class java.lang.Class
方法名称:notify-参数个数:0-返回类型void
方法名称:notifyAll-参数个数:0-返回类型void
--

当前加载器:com.chl.classloader.MyClassLoader@3d012ddd
父级加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
父父级加载器:sun.misc.Launcher$ExtClassLoader@1c4af82c
父父父级加载器:null


   原创文章,转载请标明本文链接: 继承ClassLoader实现一个简单的自定义类加载器,可加载非项目的类文件

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

更多阅读