Java SE中的自定义ClassLoader和源码分析委托模型机制

类加载器

  • Bootstrap ClassLoader:是用C++语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载%JAVA_HOME%/jre/lib,-Xbootclasspath参数指定的路径以及%JAVA_HOME%/jre/classes中的类。

  • Extension ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类。

  • AppClassLoader:加载classpath指定的类,是最常使用的一种加载器。

案例实战

通过一个案例来,探究一番

public class Test {
    public static void main(String[] arg) {
         //获取Test类的类加载器
        ClassLoader appClassLoader = Test.class.getClassLoader();
        System.out.println(" Test类的加载器是:" + appClassLoader.getClass().getSimpleName());
       //获取AppClassLoader的父类
        ClassLoader extClassLoader=appClassLoader.getParent();
        System.out.println(" Test类的加载器的父类是:" + extClassLoader.getClass().getSimpleName());
        //获取ExtClassLoader的parent
        ClassLoader  bootstrapClassLoader=extClassLoader.getParent();
        System.out.println(" Test类的加载器的父类的父类是:" + bootstrapClassLoader);
    }
}

输出结果是:

 Test类的加载器是:AppClassLoader
 Test类的加载器的父类是:ExtClassLoader
 Test类的加载器的父类的父类是:null

从以上可知,加载Test类是AppClassLoader。AppClassLoader的父类是ExtClassLoader。ExtClassLoader的父类BootstrapClassLoder,但却为null?由于Bootstrap Loader是用C++语言写的,依java的观点来看,逻辑上并不存在Bootstrap Loader的类实体。

最终,归纳如下图所示:
在这里插入图片描述

委托模型机制

理解:

扫描二维码关注公众号,回复: 15825248 查看本文章

当类加载器有载入类的需求时,会先请示其Parent,让Parent使用其提供的路径帮忙载入。若Parent找不到,那么才自己依照自己的搜索路径搜索类。

好处:

因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。

自定义ClassLoader

既然JVM已经提供了默认的类加载器,为什么还要定义自已的类加载器呢?

因为Java中提供的默认ClassLoader,只加载指定目录下的jar和class,如果我们想加载其它位置的类或jar时,比如:我要加载网络上的一个class文件,通过动态加载到内存之后,要调用这个类中的方法实现我的业务逻辑。在这样的情况下,默认的ClassLoader就不能满足我们的需求了,所以需要定义自己的ClassLoader。

实战案例

1.编写额外的类文件

先创建项目工程下,创建一个plugi目录,然后在该目录下创建,一个PluginTest.class文件,如下图所示:
在这里插入图片描述

编写以下代码:

package com.xingen.classloader.plugin;

public class PluginTest {
    public PluginTest() {
    }

    public void print() {
        System.out.append("Plugin加载,自定义ClassLoader加载class文件");
    }
}

该class模拟外部的需要加载的类,与src不在同一路径下。

2. 项目的src下编写相关代码

先来查看下,ClassLoader中的查找类的loadClass()方法的源码:

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
        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) {
                       //先父类parent中查找。
                        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) {//若parent中没找到,调用findClass()在本身中查找。
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // 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;
        }
    }

从以上可知:

  • ClassLoader加载机制是委派双亲机制,会先parent先去查找对应的类,若是没有找到,则在本身中查找。

因此,定义ClassLoader的子类,需要继承ClassLoader外,还需要重写findClass()

先定义ClassLoader的子类,重写findClass()

public class HotClassLoader extends ClassLoader {
    private static final ClassLoader ParentClassLoader = ClassLoader.getSystemClassLoader();
    private String filePath;

    public HotClassLoader(String filePath) {
        super(ParentClassLoader);
        this.filePath = filePath;
    }

    /**
     * 重写父类的findClass()
     * <p>
     * loadClass()中已经实现搜索类的算法,
     * 当loadClass()搜索不到就会调用findClass()
     *
     * @param name
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        System.out.println("HotClassLoader 执行 findClass() ");
        byte[] bytes = StreamUtils.fileToByteArray(new File(filePath));
        //调用ClassLoader的defineClass(),传入byte,从而构建出该类的Class对象。
        Class<?> mClass = defineClass(name, bytes, 0, bytes.length);
        return mClass;
    }
}

编写工具类:将额外的类文件,转成byte。

public class StreamUtils {

    /**
     *  file转成byte
     * @param file
     * @return
     */
    public static byte[] fileToByteArray(File file){
        byte[] bytes=null;
        FileInputStream fileInputStream=null;
        ByteArrayOutputStream byteArrayOutputStream=null;
        try {
            fileInputStream=new FileInputStream(file);
            FileChannel fileChannel=fileInputStream.getChannel();
            byteArrayOutputStream=new ByteArrayOutputStream();
            WritableByteChannel writableByteChannel = Channels.newChannel(byteArrayOutputStream);
             ByteBuffer buffer=ByteBuffer.allocate(1024);
            while (true) {
                int i = fileChannel.read(buffer);
                if (i == 0 || i == -1) {
                    break;
                }
                buffer.flip();
                 writableByteChannel.write(buffer);
                buffer.clear();
            }
            bytes=byteArrayOutputStream.toByteArray();
        }catch (Exception e){
            bytes=null;
            e.printStackTrace();

        }finally {
            try {
                if (byteArrayOutputStream!=null){
                    byteArrayOutputStream.close();
                }
                if (fileInputStream!=null){
                    fileInputStream.close();
                }
            }catch (Exception e2){
            }
        }
        return bytes;
    }
}

编写测试代码:

public class TestClient {
    public static void main(String[] args) {
        testClassLoader();
    }
    private static void testClassLoader() {
        String dir = System.getProperty("user.dir");
        File pluginFile = new File(dir + File.separator + "plugin" + File.separator + "PluginTest.class");
        //额外的PluginTest类的路径
        final String filePath = pluginFile.getAbsolutePath();
        try {
            //创建ClassLoader子类对象
            HotClassLoader hotClassLoader = new HotClassLoader(filePath);
            //包名下的类名
            final String className = "com.xingen.classloader.plugin.PluginTest";
            //将该类加载到JVM
            Class<?> objectClass = hotClassLoader.loadClass(className);
            if (objectClass == null) {
                System.out.println(" 加载plugin中的类失败");
            } else {
                //反射调用额外路径下类中的方法
                Method printMethod = objectClass.getDeclaredMethod("print");
                printMethod.setAccessible(true);
                printMethod.invoke(objectClass.newInstance(), null);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

创建ClassLoader的子类,调用loadClass(),传入类名,获取到对应Class对象,从而调用其相关的方法。

最后,运行案例,控制台输出结果:

HotClassLoader 执行 findClass() 
Plugin加载,自定义ClassLoader加载class文件

完整的项目结构如下所示:

在这里插入图片描述

案例的地址: https://github.com/13767004362/JavaDemo/tree/master/ClassLoaderDemo

猜你喜欢

转载自blog.csdn.net/hexingen/article/details/84800669
今日推荐