7.0、JVM-类加载器、双亲委派模型、自定义类加载器

0、前置思考

0.1、类的唯一性

在Java中,对于任意一个类,都需要由加载它的类加载器对象和这个类本身一同确立其在Java虚拟机中的唯一性。
比如 tomcat 两个web应用互相不影响,就可以用两个不用的类加载器分别加载两个应用的class文件。

这里需要着重说3点

  • Java 中类的唯一性需要由 类加载器对象+class文件(包路径+名称) 唯一确定
  • 类加载器也可以认为是一个类,Java自带的三个 启动类加载器、扩展类加载器、应用程序类加载器的初始化由JVM保证,是全局唯一的,此时只需保证 class 的包路径唯一即可保证加载的是同一个类
  • 自定义类加载器也是一个类,先初始化这个类,然后再用这个类去加载指定目录下的类。此时需要自己决定实例化几个自定以类加载器。比如tomcat,每一个web应用、每一个jsp页面使用一个类加载器对象,这样实现了同路经类的其实不同,进而实现了web应用的相互隔离、Jsp页面的热部署。
  • 同一个唯一的类,不允许重复加载,要确保全局唯一。按照双亲委派模型不会有这个问题,自定义的话,要注意先从已加载类中获取,没有的话,再走自定义加载器的逻辑为好。

0.2、类加载器加载的是 .class 文件

默认的启动类加载器、扩展类加载器、应用系统类加载器各有默认的加载目录。
自定义类加载类可以指定自定义的目录。

0.2.1、获取class文件的方法
  • IDEA 编译、运行就会自动生成
  • 可以使用Java命令
 javac Hello.java 得到 Hello.class
  • 可以直接识别 jar包,包路径会转化为文件夹结构
    比如 import com.alibaba.fastjson.JSON 对应
    com\alibaba\fastjson\JSON.class

0.3、需要考虑用户自定义加载类不影响Jdk基础类,否则会造成混乱

1、JVM 类加载机制

1.1、BootstrpLoader (启动类加载器)

1.1.1、实现类:BootstrpLoader是C++编写的,逻辑上看没有实现类

Bootstrp loader是C++编写的,所以如果在Java语言中获取这个类加载器,会获得null,即逻辑上并不存在BootstrapLoader的类实体。
Jdk底层会自己处理,它是在Java虚拟机启动后初始化的

1.1.2、BootstrpLoader 作用范围
  • 加载%JAVA_HOME%/jre/lib 目录下的类,并且被虚拟机识别(仅按照文件名)
  • -Xbootclasspath 参数指定的路径 ,并且被虚拟机识别(仅按照文件名)
1.1.3、直接使用 BootstrpLoader

直接把类加载器赋值为 null,但是考虑到 BootstrpLoader 专门用于加载 jdk 底层的一些类库,又有路径和名称限制,这种使用场景基本可以忽略。

ClassLoader cl = null;

1.2、ExtClassLoader(扩展类加载器)

1.2.1、实现类:sun.misc.Launcher$ExtClassLoader
1.2.2、ExtClassLoader作用范围
  • 加载%JAVA_HOME%/lib/ext 目录下的类
  • java.ext.dirs 参数指定的路径
1.2.3、直接使用 ExtClassLoader

可以直接使用ExtClassLoader,获取方式是,系统类加载器的父加载器。

//获取Test类的类加载器 sun.misc.Launcher$AppClassLoader@4dc63996
	ClassLoader c = ClassLoader.getSystemClassLoader();
	
 //获取c这个类加载器的父类加载器 sun.misc.Launcher$ExtClassLoader@28a418fc
  ClassLoader c1 = c.getParent(); 

1.3、ApplicationClassLoader(应用程序类加载器/系统类加载器)

1.3.1、实现类:sun.misc.Launcher$AppClassLoader
1.3.2、ApplicationClassLoader 作用范围
  • 用户路径(ClassPath)上所指定的类库,比如 /WEB-INF/classes
1.3.3、直接使用 ExtClassLoader
  • 获取
ClassLoader cl = ClassLoader.getSystemClassLoader();
  • 一般而言也是默认的,即在没有指定自定义类加载器时,一般类都是由 ApplicationClassLoader 加载的
ClassLoader c  = Test.class.getClassLoader(); 

1.4、查看一个普通类的类加载器、父“类加载器”

/**
* 获取类加载器的测试方法
*/
@Test
public void test2() {
	//获取Test类的类加载器 sun.misc.Launcher$AppClassLoader@4dc63996
	ClassLoader c  = Test.class.getClassLoader();  
       System.out.println(c); 
       //获取c这个类加载器的父类加载器 sun.misc.Launcher$ExtClassLoader@28a418fc
       ClassLoader c1 = c.getParent(); 
       System.out.println(c1); 
       // getClassLoader() returning null indicates the bootstrap ClassLoader
       //获取c1这个类加载器的父类加载器 ,null,因为Bootstrp loader是C++编写的,依java的观点来看,逻辑上并不存在Bootstrap Loader的类实体
       //根装载器:Bootstrp loader
       //用C++语言写的,它是在Java虚拟机启动后初始化的,主要负责加载%JAVA_HOME%/jre/lib,
       //* -Xbootclasspath参数指定的路径
       ClassLoader c2 = c1.getParent();
       System.out.println(c2);
       
       //获取系统默认的ClassLoader sun.misc.Launcher$AppClassLoader@4dc63996
       ClassLoader c3=ClassLoader.getSystemClassLoader();
       System.out.println(c3);
       //获取创建当前线程的类加载器 
       ClassLoader c4= Thread.currentThread().getContextClassLoader();
       System.out.println(c4);
}

2、 双亲委派模型

简单说就是,先由自己的父类加载需要的类,一直向上传递,如果没有再由自己加载
类加载器可以重写,但是核心类是不允许使用自己的类加载器加载的,比如 jdk 核心包 java.lang.String ,只允许 bootstrap ClassLoader 进行加载

类加载器加载目录功能举例
BootStrap Class Loaderjre/lib/称为启动类加载器,是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库,如:rt.jar、resources.jar、charsets.jar等
Extension Class Loaderjre/lib/ext/称为扩展类加载器,负责加载Java的扩展类库
App Class Loaderclasspath目录下或者 -classpath 参数指定的包称为系统类加载器Tomcat Bootstrap 类即由 System Class Loarder 加载

2.1、Mac 查看 JDK 安装目录

举例 /Library/Java/JavaVirtualMachines/jdk1.8.0_231.jdk/Contents/Home

 /usr/libexec/java_home -V

3、自定义类加载器(自定义类指定目录,className包含package)

自定义类指定目录,className包含package

3.1、 extends ClassLoader

自定义类加载器的原则:组合而非继承
这里类加载器之间的父子关系一般不会以继承的关系来实现,而是都使用组合关系来复用父加载器的代码。
这意味着,当你在自定义类加载器时,也应该先委托父加载器即App ClassLoader进行加载。
那么问题来了,既然双亲委派模型只是一个规范,那我可以直接使用自定义加载器加载 String 类吗?答案是:不可以。因为JVM会检测,即使你不遵守双亲委派模型,你也无法加载核心类库的类,JVM自己会检测。

3.2、自定义类加载器

我们只需要继承 ClassLoader 然后覆盖其中的 findClass(name) 方法即可

3.2.1、不再建议覆盖重写 loadClass

loadClass 内部预留了 findClass 方法,应该在findClass中实现自定义逻辑
loadClass 内部,已经遵循了下面的逻辑

是否已加载(缓存)
-未加载
	-父加载器为null
		-是,使用 BootstrpLoader 加载
		-否,使用父类加载器加载
	-父类加载器加载完毕后,class还是空?
		-c=findClass(name); 即本加载器扩展的 findClass 逻辑
3.2.2、自定义类加载器

自定义类指定目录,className包含package

import java.io.FileInputStream;
import java.lang.reflect.Method;

/**
 * @Title:自定义类加载器
 * @Description:
 * @Analysis:
 * @Copyright: Copyright(c)2022
 * @Company: bestcxx
 * @Author: jie.wu
 * @Updatedby:
 */
public class MyClassLoader extends ClassLoader{

	/**自定义类指定目录,className包含package*/
    private String classpath;

    public MyClassLoader(String classpath) {
        this.classpath = classpath;
    }

	/**
	* 直接调用该方法,可以违反双亲委派模型
	* 若遵循双亲委派模型,则应调用自定义类的 loadClass方法
	*/
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            //如果已经加载过,则不需要从新加载
            Class<?> LoadedClass = findLoadedClass(name);
            if(Objects.nonNull(LoadedClass)){
                return LoadedClass;
            }
            byte[]  data = getData(name);
            return defineClass(name,data,0,data.length);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }
    }

    private byte[] getData(String name) throws Exception{
        name = name.replaceAll("\\.", "/");
        FileInputStream fis = new FileInputStream(classpath+"/"+name+".class");
        int len = fis.available();
        byte[] data = new byte[len];
        fis.read(data);
        fis.close();
        return data;
    }
3.2.3、自定义类加载器的使用
3.2.3.1、遵循双亲委派模型(调用自定义类加载器的 loadClass 方法)

loadClass 内部按照下面顺序:
0、缓存
1、启动类加载器加载
2、拓展类加载器加载
3、应用系统类加载器加载(classpath目录)
4、findClass 方法(指定目录下的class文件)

  • 如果 classpath下存在 Del 类,输出结果为
    在这里插入图片描述
    在这里插入图片描述

sun.misc.Launcher$AppClassLoader
你调用了方法 method()

  • 如果 classpath下不存在 Del 类,输出结果为
  • 在这里插入图片描述

在这里插入图片描述
MyClassLoader
你调用了方法 method()

  • 代码(调用自定义类加载器的 loadClass 方法)
import java.io.FileInputStream;
import java.lang.reflect.Method;

/**
 * @Title:自定义类加载器
 * @Description:
 * @Analysis:
 * @Copyright: Copyright(c)2022
 * @Company: bestcxx
 * @Author: jie.wu
 * @Updatedby:
 */
public class MyClassLoader extends ClassLoader{
	/**自定义类指定目录,className包含package*/
    private String classpath;

    public MyClassLoader(String classpath) {
        this.classpath = classpath;
    }

   /**
	* 直接调用该方法,可以违反双亲委派模型
	* 若遵循双亲委派模型,则应调用自定义类的 loadClass方法
	*/
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            //如果已经加载过,则不需要从新加载
            Class<?> LoadedClass = findLoadedClass(name);
            if(Objects.nonNull(LoadedClass)){
                return LoadedClass;
            }
            byte[]  data = getData(name);
            return defineClass(name,data,0,data.length);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }
    }

    private byte[] getData(String name) throws Exception{
        name = name.replaceAll("\\.", "/");
        FileInputStream fis = new FileInputStream(classpath+"/"+name+".class");
        int len = fis.available();
        byte[] data = new byte[len];
        fis.read(data);
        fis.close();
        return data;
    }

    public static void main(String[] args) throws Exception  {
        String classpath = "E:\\";
        MyClassLoader myClassLoader = new MyClassLoader(classpath);

        Class<?> cl = myClassLoader.loadClass("com.Del");
        System.out.println(cl.getClassLoader().getClass().getName());
        Object o = cl.newInstance();
        Method method=cl.getMethod("method", null);
        method.invoke(o, null);
    }
}
3.2.3.2、违反双亲委派模型(直接调用 findClass 方法)

findClass 内部直接按照指定目录去加载目标类了,因此加载器必然为自定义类加载器。
但是,需要注意的是,如果已经加载了一个唯一的类,再次直接用findClass 会报错
在这里插入图片描述
在这里插入图片描述

  • 代码(调用自定义类加载器的 findClass方法)
import java.io.FileInputStream;
import java.lang.reflect.Method;

/**
 * @Title:自定义类加载器
 * @Description:
 * @Analysis:
 * @Copyright: Copyright(c)2022
 * @Company: bestcxx
 * @Author: jie.wu
 * @Updatedby:
 */
public class MyClassLoader extends ClassLoader{
	/**自定义类指定目录,className包含package*/
    private String classpath;

    public MyClassLoader(String classpath) {
        this.classpath = classpath;
    }

    /**
	* 直接调用该方法,可以违反双亲委派模型
	* 若遵循双亲委派模型,则应调用自定义类的 loadClass方法
	*/
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            //如果已经加载过,则不需要从新加载
            Class<?> LoadedClass = findLoadedClass(name);
            if(Objects.nonNull(LoadedClass)){
                return LoadedClass;
            }
            byte[]  data = getData(name);
            return defineClass(name,data,0,data.length);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }
    }

    private byte[] getData(String name) throws Exception{
        name = name.replaceAll("\\.", "/");
        FileInputStream fis = new FileInputStream(classpath+"/"+name+".class");
        int len = fis.available();
        byte[] data = new byte[len];
        fis.read(data);
        fis.close();
        return data;
    }

    public static void main(String[] args) throws Exception  {
        String classpath = "E:\\";
        MyClassLoader myClassLoader = new MyClassLoader(classpath);
		
		//遵循双亲委派模型-找不到采用自定义类加载器加载
        //Class<?> cl = myClassLoader.loadClass("com.Del");
        //直接使用自定义类加载器加载
        Class<?> cl = myClassLoader.findClass("com.Del");
        System.out.println(cl.getClassLoader().getClass().getName());
        Object o = cl.newInstance();
        Method method=cl.getMethod("method", null);
        method.invoke(o, null);
    }
}
3.2.4、类加载器与类的唯一性验证

验证3点
1、双亲委派模型下,通过 自定义类加载器的 loadClass 方法加载 的类 的类加载器未必是自定义类加载器-这个上面已经验证
2、自定义类加载了类(不是由父类加载器加载,不是情况1),不同的自定义类加载器加载的类的是不同的类。
3、同一个类加载器实例化的两个类加载器对象,加载同一个类(不是由父类加载器加载的,不是情况11),被加载的类是不同的类

  • 代码(建造两个自定义类加载器,直接使用 findClass 加载同一个测试class文件,比较二者是不是一个类,验证结果为否)
    -比较类
import java.io.FileInputStream;
import java.util.Objects;

/**
 * @Title:自定义类加载器 标胶类
 * @Description:
 * @Analysis:
 * @Copyright: Copyright(c)2022
 * @Company: bestcxx
 * @Author: jie.wu
 * @Updatedby:
 */
public class My2ClassLoader extends ClassLoader{
	/**自定义类指定目录,className包含package*/
    private String classpath;

    public My2ClassLoader(String classpath) {
        this.classpath = classpath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            //如果已经加载过,则不需要从新加载
            Class<?> LoadedClass = findLoadedClass(name);
            if(Objects.nonNull(LoadedClass)){
                return LoadedClass;
            }
            byte[]  data = getData(name);
            return defineClass(name,data,0,data.length);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }
    }

    private byte[] getData(String name) throws Exception{
        name = name.replaceAll("\\.", "/");
        FileInputStream fis = new FileInputStream(classpath+"/"+name+".class");
        int len = fis.available();
        byte[] data = new byte[len];
        fis.read(data);
        fis.close();
        return data;
    }
}
import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @Title:自定义类加载器
 * @Description:
 * @Analysis:
 * @Copyright: Copyright(c)2022
 * @Company: bestcxx
 * @Author: jie.wu
 * @Updatedby:
 */
public class MyClassLoader extends ClassLoader{
	/**自定义类指定目录,className包含package*/
    private String classpath;

    public MyClassLoader(String classpath) {
        this.classpath = classpath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            //如果已经加载过,则不需要从新加载
            Class<?> LoadedClass = findLoadedClass(name);
            if(Objects.nonNull(LoadedClass)){
                return LoadedClass;
            }
            byte[]  data = getData(name);
            return defineClass(name,data,0,data.length);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }
    }

    private byte[] getData(String name) throws Exception{
        name = name.replaceAll("\\.", "/");
        FileInputStream fis = new FileInputStream(classpath+"/"+name+".class");
        int len = fis.available();
        byte[] data = new byte[len];
        fis.read(data);
        fis.close();
        return data;
    }

    public static void main(String[] args) throws Exception  {
        String classpath = "E:\\";
        MyClassLoader myClassLoader = new MyClassLoader(classpath);

        Class<?> cl = myClassLoader.findClass("com.Del");

        My2ClassLoader myClassLoader2 = new My2ClassLoader(classpath);
        Class<?> cl2 = myClassLoader2.findClass("com.Del");
        System.out.println(cl==cl2);//false
        //System.out.println(cl.getClassLoader().getClass().getName());
        Object o = cl.newInstance();
        Method method=cl.getMethod("method", null);
        method.invoke(o, null);
    }
}

  • 代码(使用一个自定义类加载器,声明俩对象,分别加载同一个测试class文件,比较二者是不是一个类,验证结果为否)
import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @Title:自定义类加载器
 * @Description:
 * @Analysis:
 * @Copyright: Copyright(c)2022
 * @Company: bestcxx
 * @Author: jie.wu
 * @Updatedby:
 */
public class MyClassLoader extends ClassLoader{
	/**自定义类指定目录,className包含package*/
    private String classpath;

    public MyClassLoader(String classpath) {
        this.classpath = classpath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            //如果已经加载过,则不需要从新加载
            Class<?> LoadedClass = findLoadedClass(name);
            if(Objects.nonNull(LoadedClass)){
                return LoadedClass;
            }
            byte[]  data = getData(name);
            return defineClass(name,data,0,data.length);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }
    }

    private byte[] getData(String name) throws Exception{
        name = name.replaceAll("\\.", "/");
        FileInputStream fis = new FileInputStream(classpath+"/"+name+".class");
        int len = fis.available();
        byte[] data = new byte[len];
        fis.read(data);
        fis.close();
        return data;
    }

    public static void main(String[] args) throws Exception  {
        String classpath = "E:\\";
        MyClassLoader myClassLoader = new MyClassLoader(classpath);

        Class<?> cl = myClassLoader.findClass("com.Del");

        MyClassLoader myClassLoader2 = new MyClassLoader(classpath);
        Class<?> cl2 = myClassLoader2.findClass("com.Del");
        System.out.println(cl==cl2);//false
        //System.out.println(cl.getClassLoader().getClass().getName());
        Object o = cl.newInstance();
        Method method=cl.getMethod("method", null);
        method.invoke(o, null);
    }
}

  • 代码(使用一个自定义类加载器,声明一个对象,加载同一个测试class文件两次,比较二者是不是一个类,验证结果为是)
import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @Title:自定义类加载器
 * @Description:
 * @Analysis:
 * @Copyright: Copyright(c)2022
 * @Company: bestcxx
 * @Author: jie.wu
 * @Updatedby:
 */
public class MyClassLoader extends ClassLoader{
	/**自定义类指定目录,className包含package*/
    private String classpath;

    public MyClassLoader(String classpath) {
        this.classpath = classpath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            //如果已经加载过,则不需要从新加载
            Class<?> LoadedClass = findLoadedClass(name);
            if(Objects.nonNull(LoadedClass)){
                return LoadedClass;
            }
            byte[]  data = getData(name);
            return defineClass(name,data,0,data.length);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }
    }

    private byte[] getData(String name) throws Exception{
        name = name.replaceAll("\\.", "/");
        FileInputStream fis = new FileInputStream(classpath+"/"+name+".class");
        int len = fis.available();
        byte[] data = new byte[len];
        fis.read(data);
        fis.close();
        return data;
    }

    public static void main(String[] args) throws Exception  {
        String classpath = "E:\\";
        MyClassLoader myClassLoader = new MyClassLoader(classpath);

        //Class<?> cl = myClassLoader.loadClass("com.Del");
        Class<?> cl = myClassLoader.findClass("com.Del");
        Class<?> cl2 = myClassLoader.findClass("com.Del");
        System.out.println(cl==cl2);
        //System.out.println(cl.getClassLoader().getClass().getName());
        Object o = cl.newInstance();
        Method method=cl.getMethod("method", null);
        method.invoke(o, null);
    }
}

4、使用自定义类加载器加载 jar/.class 文件

上面的内容都是围绕 .class 文件进行加载的,这里提供一个更加贴近实战的方法,即从 jar 文件中加载。

4.1、准备

  • 扫描的文件结构为
    E:\myclassloader
    lib\fastjson-1.2.78.jar
    classes\com\Del.class

  • 测试用 jar

<dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.78</version>
        </dependency>
    </dependencies>

4.2、自定义类加载器——扫描Jar文件 +.class 文件

import java.io.*;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @Title:自定义类加载器-扫描 jar
 * @Description:
 *  lib = classpath + "lib/ 放 jar 文件";
 *  classes = classpath + "classes/ 放 .class 文件";
 * @Analysis:
 * @Copyright: Copyright(c)2022
 * @Company: bestcxx
 * @Author: jie.wu
 * @Updatedby:
 */
public class MyClassLoader extends ClassLoader {
    /**
     * lib:表示加载的文件在jar包中
     * 类似tomcat就是{PROJECT}/WEB-INF/lib/
     */
    private String lib;
    /**
     * classes:表示加载的文件是单纯的class文件
     * 类似tomcat就是{PROJECT}/WEB-INF/classes/
     */
    private String classes;
    /**
     * 采取将所有的jar包中的class读取到内存中
     * 然后如果需要读取的时候,再从map中查找
     */
    private Map<String, byte[]> map;

    public MyClassLoader(String classpath) {
        lib = classpath + "lib/";
        classes = classpath + "classes/";
        map = new HashMap<String, byte[]>(64);

        preReadJarFile();
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            //如果已经加载过,则不需要从新加载
            Class<?> LoadedClass = findLoadedClass(name);
            if (Objects.nonNull(LoadedClass)) {
                return LoadedClass;
            }
            byte[] data = getClassFromFileOrMap(name);
            return defineClass(name, data, 0, data.length);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }
    }

    /**
     * 从指定的classes文件夹下找到文件
     *
     * @param name
     * @return
     */
    private byte[] getClassFromFileOrMap(String name) {
        String classPath = classes + name.replace('.', File.separatorChar) + ".class";
        File file = new File(classPath);
        if (file.exists()) {
            InputStream input = null;
            try {
                input = new FileInputStream(file);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                int bufferSize = 4096;
                byte[] buffer = new byte[bufferSize];
                int bytesNumRead = 0;
                while ((bytesNumRead = input.read(buffer)) != -1) {
                    baos.write(buffer, 0, bytesNumRead);
                }
                return baos.toByteArray();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (input != null) {
                    try {
                        input.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

        } else {
            if (map.containsKey(name)) {
                //去除map中的引用,避免GC无法回收无用的class文件
                return map.remove(name);
            }
        }
        return null;
    }

    /**
     * 预读lib下面的包
     */
    private void preReadJarFile() {
        List<File> list = scanDir();
        for (File f : list) {
            JarFile jar;
            try {
                jar = new JarFile(f);
                readJAR(jar);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 读取一个jar包内的class文件,并存在当前加载器的map中
     *
     * @param jar
     * @throws IOException
     */
    private void readJAR(JarFile jar) throws IOException {
        Enumeration<JarEntry> en = jar.entries();
        while (en.hasMoreElements()) {
            JarEntry je = en.nextElement();
            String name = je.getName();
            if (name.endsWith(".class")) {
                String clss = name.replace(".class", "").replaceAll("/", ".");
                if (this.findLoadedClass(clss) != null) {
                    continue;
                }

                InputStream input = jar.getInputStream(je);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                int bufferSize = 4096;
                byte[] buffer = new byte[bufferSize];
                int bytesNumRead = 0;
                while ((bytesNumRead = input.read(buffer)) != -1) {
                    baos.write(buffer, 0, bytesNumRead);
                }
                byte[] cc = baos.toByteArray();
                input.close();
                map.put(clss, cc);//暂时保存下来
            }
        }
    }

    /**
     * 扫描lib下面的所有jar包
     *
     * @return
     */
    private List<File> scanDir() {
        List<File> list = new ArrayList<File>();
        File[] files = new File(lib).listFiles();
        for (File f : files) {
            if (f.isFile() && f.getName().endsWith(".jar"))
                list.add(f);
        }
        return list;
    }

    /**
     * 添加一个jar包到加载器中去。
     *
     * @param jarPath
     * @throws IOException
     */
    public void addJar(String jarPath) throws IOException {
        File file = new File(jarPath);
        if (file.exists()) {
            JarFile jar = new JarFile(file);
            readJAR(jar);
        }
    }

    public static void main(String[] args) throws Exception {
        String classpath = "E:\\myclassloader\\";
        MyClassLoader myClassLoader = new MyClassLoader(classpath);

        Class<?> cl = myClassLoader.findClass("com.alibaba.fastjson.JSON");
        Class<?> c2 = myClassLoader.findClass("com.Del");
        System.out.println(cl.getClassLoader().getClass().getName());
        System.out.println(c2.getClassLoader().getClass().getName());
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值