一、定义和作用

ClassLoader是Java的核心组件,主要工作在Class装载的加载阶段。所有的Class都是由ClassLoader进行加载的,ClassLoader 负责通过将Class文件里的二进制数据流装载进系统,然后交给Java虚拟机进行连接、初始化等操作。

二、分类

1.BootStrapClassLoader

作用:C++编写而成, 它是最顶层的类加载器,已经内嵌到JVM中了。在JVM启动时会初始化该ClassLoader,它主要用来读取Java的核心类库JRE/lib/rt.jar中所有的class文件,这个jar文件中包含了java规范定义的所有接口及实现。

2.ExtClassLoader

(1)作用

Java编写,它是用来加载Java的扩展类库javax.*,如读取JRE/lib/ext/*.jar中的包等(这里要注意,有些版本的是没有ext这个目录的)。

(2)源码-》查看加载文件路径:

static class ExtClassLoader extends URLClassLoader {
…
private static File[] getExtDirs() {
    String var0 = System.getProperty("java.ext.dirs");
    File[] var1;
    if (var0 != null) {
        StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
        int var3 = var2.countTokens();
        var1 = new File[var3];

        for(int var4 = 0; var4 < var3; ++var4) {
            var1[var4] = new File(var2.nextToken());
        }
    } else {
        var1 = new File[0];
    }

    return var1;
}

…
}

Idea中查看主要类目录

public class test {
    public static void main(String[] args) {
        System.out.println(System.getProperty("java.ext.dirs"));
    }
}

结果:
C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext

3.AppClassLoader

(1)作用

Java编写,它是用来读取CLASSPATH下指定的所有jar包或目录的类文件,一般情况下这个就是程序中默认的类加载器。

(2)源码-》查看加载文件路径:

static class AppClassLoader extends URLClassLoader {
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
    final String var1 = System.getProperty("java.class.path");
    final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
    return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
        public Launcher.AppClassLoader run() {
            URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
            return new Launcher.AppClassLoader(var1x, var0);
        }
    });
}

}

文件加载路径为:System.getProperty("java.class.path")

package com.spring.ioc.c5;

public class test {
    public static void main(String[] args) {
         System.out.println(System.getProperty("java.class.path"));
    }
}

路径:主要是...\javabase\target\classes下面的编译好的包和类

C:\Program Files\Java\jdk1.8.0_191\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\cldrdata.jar;C:\Program…
E:\02LocalProject\javabase\target\classes;
….

4.自定义ClassLoader

定制化开发,下面可以代码实现

5.Loader的双亲委派机制

(1)作用:可以避免同样的字节码加载多次到内存中,一次加载到内存中,如果后续需要,则直接取出使用,不需要,重复加载第二次。

(2)自下而上,检测是否已经加载,如果加载过,则直接调用该class;如果没有加载过,则采用自下而上重新加载。

(3)自上而下,如果寻找对应class文件,有则加载

 

javaee 包加载 java classloader加载jar包_java

(4)源码

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, false);
                    } else {
                        c = findBootstrapClassOrNull(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);

                    // 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关键方法以及层次关系

1. loadClass

作用:ClassLoader加载类的入口,此方法负责加载指定名字的类。ClassLoader的实现方法为先从已经加载的类中寻找,如没有则继续从父ClassLoader中寻找,如仍然没找到,则从BootstrapClassLoader中寻找,最后再调用findClass方法来寻找。

2.findClass

作用:查找二进制class文件,并且加载返回一个对象
源码:

protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

3. defineClass

作用:读取字节流,返回类对象
源码:

protected final Class<?> defineClass(String name, byte[] b, int off, int len)
    throws ClassFormatError
{
    return defineClass(name, b, off, len, null);
}

4.三者关系

javaee 包加载 java classloader加载jar包_ClassLoader_02

 

四、自定义classloader代码

1.写一个测试类InputClass.java

package com.spring.ioc.c5;

public class InputClass {
    static {
        System.out.println("Hello World! I am InputClass");
    }
}

使用idea的终端Terminal在当前目录下编译为InputClass.class文件
…\javabase\src\main\java\com\spring\ioc\c5>javac InputClass.java
生成…\javabase\src\main\java\com\spring\ioc\c5\InputClass.class

2.实现自定义加载类ClassLoaderDIY.java

package com.spring.ioc.c5;

import java.io.*;

public class ClassLoaderDIY extends ClassLoader{
    private String path; //加载class的路径(不包含文件名称)
    private String classLoaderName; //加载的class名称

    public ClassLoaderDIY(String path, String classLoaderName) {
        this.path = path;
        this.classLoaderName = classLoaderName;
    }

    //寻找类文件,并且以二进制数组方式加载进来
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] b=loadClassData(name);
        return defineClass(name,b,0,b.length);
    }

    //用于加载类文件
    private byte[] loadClassData(String name) {
        name=path+name+".class";  //即:路径+名称
        InputStream in=null;
        ByteArrayOutputStream out=null;

        try {
            in=new FileInputStream(new File(name));
            out=new ByteArrayOutputStream();
            int i=0;
            while ((i=in.read())!=-1){
                out.write(i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

        return out.toByteArray();

    }

}

3.测试类ClassLoadTest.java

package com.spring.ioc.c5;

public class ClassLoadTest {
    public static void main(String[] args) {
        ClassLoaderDIY classLoaderDIY=new ClassLoaderDIY("src\\main\\java\\com\\spring\\ioc\\c5\\","FirstClassLoader");
        Class c= null;
        try {
            c = classLoaderDIY.loadClass("com.spring.ioc.c5.InputClass");
            System.out.println(c.getClassLoader());
            c.newInstance();
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

注意:因为加载类,在com.spring.ioc.c5下面,所以,加载类时需要加载指定包目录:com.spring.ioc.c5.InputClass

4.测试结果

加载成功

sun.misc.Launcher$AppClassLoader@18b4aac2
Hello World! I am InputClass