JAVA 17 -classloader类加载机制及自定义类加载器的实现

概述

用如下代码测试

public class TestClassLoader {
    public static void main(String[] arsg){
        TestClassLoader loader=new TestClassLoader();
        String s="test";
        System.out.println(s.getClass().getClassLoader());
        System.out.println(loader.getClass().getClassLoader().getParent());
        System.out.println(loader.getClass().getClassLoader());
    }
}

发现classloader有三种

null

jdk.internal.loader.ClassLoaders$PlatformClassLoader@2530c12

jdk.internal.loader.ClassLoaders$AppClassLoader@63947c6b

3个ClassLoader类的关系及实例化过程

其中null其实为BootClassLoader,我们查看源代码可以发现其实有三个loader 

private static final BootClassLoader BOOT_LOADER;
private static final PlatformClassLoader PLATFORM_LOADER;
private static final AppClassLoader APP_LOADER;

三个loader类的关系如下图,可以看出他们顶级父类都ClassLoader

在查看这3个的实例化过程,我可以看出PLATFORME_LOADER的parent是BOOT_LOADER、APP_LOADER的parent是PLATFORM_LOADER

static {
    ArchivedClassLoaders archivedClassLoaders = ArchivedClassLoaders.get();
    if (archivedClassLoaders != null) {
        // assert VM.getSavedProperty("jdk.boot.class.path.append") == null
        BOOT_LOADER = (BootClassLoader) archivedClassLoaders.bootLoader();
        setArchivedServicesCatalog(BOOT_LOADER);
        PLATFORM_LOADER = (PlatformClassLoader) archivedClassLoaders.platformLoader();
        setArchivedServicesCatalog(PLATFORM_LOADER);
    } else {
        // -Xbootclasspath/a or -javaagent with Boot-Class-Path attribute
        String append = VM.getSavedProperty("jdk.boot.class.path.append");
        URLClassPath ucp = (append != null && !append.isEmpty())
                ? new URLClassPath(append, true)
                : null;
        BOOT_LOADER = new BootClassLoader(ucp);
        PLATFORM_LOADER = new PlatformClassLoader(BOOT_LOADER);
    }
    // A class path is required when no initial module is specified.
    // In this case the class path defaults to "", meaning the current
    // working directory.  When an initial module is specified, on the
    // contrary, we drop this historic interpretation of the empty
    // string and instead treat it as unspecified.
    String cp = System.getProperty("java.class.path");
    if (cp == null || cp.isEmpty()) {
        String initialModuleName = System.getProperty("jdk.module.main");
        cp = (initialModuleName == null) ? "" : null;
    }
    URLClassPath ucp = new URLClassPath(cp, false);
    if (archivedClassLoaders != null) {
        APP_LOADER = (AppClassLoader) archivedClassLoaders.appLoader();
        setArchivedServicesCatalog(APP_LOADER);
        APP_LOADER.setClassPath(ucp);
    } else {
        APP_LOADER = new AppClassLoader(PLATFORM_LOADER, ucp);
        ArchivedClassLoaders.archive();
    }
}

3个classLoader的loader类的过程

其他们都是调用父类的BuiltinClassLoader的方法,其实就是对顶级类ClassLoader中loadClass方法重写 

@Override
protected Class<?> loadClass(String cn, boolean resolve)
    throws ClassNotFoundException
{
    Class<?> c = loadClassOrNull(cn, resolve);
    if (c == null)
        throw new ClassNotFoundException(cn);
    return c;
}
protected Class<?> loadClassOrNull(String cn, boolean resolve) {
    synchronized (getClassLoadingLock(cn)) {
        // check if already loaded
        Class<?> c = findLoadedClass(cn);

        if (c == null) {

            // find the candidate module for this class
            LoadedModule loadedModule = findLoadedModule(cn);
            if (loadedModule != null) {

                // package is in a module
                BuiltinClassLoader loader = loadedModule.loader();
                if (loader == this) {
                    if (VM.isModuleSystemInited()) {
                        c = findClassInModuleOrNull(loadedModule, cn);
                    }
                } else {
                    // delegate to the other loader
                    c = loader.loadClassOrNull(cn);
                }

            } else {

                // check parent
                if (parent != null) {
                    c = parent.loadClassOrNull(cn);
                }

                // check class path
                if (c == null && hasClassPath() && VM.isModuleSystemInited()) {
                    c = findClassOnClassPathOrNull(cn);
                }
            }

        }

        if (resolve && c != null)
            resolveClass(c);

        return c;
    }
}

从以上代码逻辑我们可以看出如果当前classloader是APP_LOADER,他先让其PARENT(PLATFORM_LOADER)的PARENT(BOOT_LOADER)去找类,如果BOOT_LOADER找不到类,在让PLATFORM_LOADER去找,若找不到在让APP_LOADER去找,如果APP_LOADER加载不到类,会义PLATFORM_LOADER加载类,如果也加载不到在让APP_LOADER加载,这就是传说的JAVA类加载双亲委派机制

实践

根据以上原理如查我们自已定义定义目录下加载类 

package java1.lang1;

public class Integer1 {
    public  String toString(String s){
        System.out.println(s);
        return "ni mei";
    }

    public static void main(String[] args){

        String s=new String();
        s.toString();

    }
}

并将此类从project中删除,并class文件放在“G:\CPTEST\java1\lang1”下,如何实现呢,需要自定义如下类加载器

package std.loader;
import java.io.*;

public class MyDefindClassLoader extends ClassLoader {

    private final ClassLoader parent=this.getClass().getClassLoader();

    private String classPath;


    public MyDefindClassLoader(String path){
        this.classPath=path;

    }

    @Override
    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);
                    }
                } 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);


                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

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

        try {
            System.out.println("mydefineClassLoader loading class "+name);
            String realpath=this.classPath+"/"+name.replace(".","/")+".class";
            File f=new File(realpath);
            FileInputStream inputStream=new FileInputStream(f);
            byte[] b=new byte[(int) f.length()];
            inputStream.read(b);
            return defineClass(name, b, 0, b.length);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }


    }




}

用如下代码进行TEST

package std.loader;

import java.lang.reflect.Method;

public class TestClassLoader {
    public static void main(String[] arsg){
        TestClassLoader loader=new TestClassLoader();

        System.out.println(loader.getClass().getClassLoader().getParent().getParent());
        System.out.println(loader.getClass().getClassLoader().getParent());
        System.out.println(loader.getClass().getClassLoader());

        MyDefindClassLoader myDefindClassLoader=new MyDefindClassLoader("G:/CPTEST");
        try {
            Class<?>  integer1=  myDefindClassLoader.loadClass("java1.lang1.Integer1");

            Method method= integer1.getDeclaredMethod("toString",String.class);
            method.invoke(integer1.newInstance(),"Are you sure  jjj?");
        } catch ( Exception e) {
            e.printStackTrace();
        }

        System.out.println(myDefindClassLoader.getClass().getClassLoader());



    }
}

执行日志如下:

null

jdk.internal.loader.ClassLoaders$PlatformClassLoader@2530c12

jdk.internal.loader.ClassLoaders$AppClassLoader@63947c6b

mydefineClassLoader loading class java1.lang1.Integer1

Are you sure jjj?

jdk.internal.loader.ClassLoaders$AppClassLoader@63947c6b

如果类Integer1 不从project中删除运行的如果下,主要原因是主要自定义的MyDefindClassLoader 中parent先从项目中找到了类,不会从自定义目录中去找。

null

jdk.internal.loader.ClassLoaders$PlatformClassLoader@2530c12

jdk.internal.loader.ClassLoaders$AppClassLoader@63947c6b

Are you sure jjj?

jdk.internal.loader.ClassLoaders$AppClassLoader@63947c6b

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值