Java的类加载器机制和实战

类加载器这个名字自始至终一直接触,今天趁着公司空调记录下吧。

一,首先是JVM的类加载器,包含以下三种:

  (1):BootStrap 主要加载%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar等。用  System.getProperty("sun.boot.class.path")查看加载类文件的路径;

    (2):Extention ClassLoader:主要加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。也可以通过System.getProperty("java.ext.dirs")查看加载类文件的路径。

       (3) AppClassLoader:主要加载当前应用下的classpath路径下的类。

    其中,这是三个的关系为:AppClassLoder继承URLClassLoader,而URLClassLoader继承ClassLoader,BoopStrap ClassLoder,因为它是由C/C++编写的,它本身是虚拟机的一部分。

二:JVM加载类的规则如下:
   (1)首先会到自定义加载器中查找,看是否已经加载过,如果已经加载过,则返回字节码。

   (2)如果自定义加载器没有加载过,则询问上一层加载器(即AppClassLoader)是否已经加载过Test.class。

   (3)如果没有加载过,则询问上一层加载器(ExtClassLoader)是否已经加载过。

   (4)如果没有加载过,则继续询问上一层加载(BoopStrap ClassLoader)是否已经加载过。

  (5)如果BoopStrap ClassLoader依然没有加载过,则到自己指定类加载路径下("sun.boot.class.path")查看是否有Test.class字节码,有则返回,没有通知下一层加载器ExtClassLoader到自己指定的类加载路径下(java.ext.dirs)查看。

  (6)依次类推,最后到自定义类加载器指定的路径还没有找到Test.class字节码,则抛出异常ClassNotFoundException.

  三:自定义类加载器实战

    (1)在指定一个目录,用javac生产加载类的class文件,本文的类(com.test.Test)为如下:

package com.test;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test {

    public String dateToStr(Date date) {
        if (date == null) {
            return "";
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
    }
}

 (2) 代码中自定义自己的类加载器,继承ClassLoader,其中重写其findClass()方法:如下:

public class MyClassLoader extends ClassLoader {
    private String classpath;

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


    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] bytes = getBytesCodeFormPath(name);
        if (bytes != null) {
            return super.defineClass(name, bytes, 0, bytes.length);
        }
        return super.findClass(name);
    }

    private byte[] getBytesCodeFormPath(String className) {
        String path = classpath + File.separatorChar + className.replace('.', File.separatorChar) + ".class";
        try (InputStream in = new FileInputStream(path)) {
            try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
                byte[] buffers = new byte[2048];
                int len = 0;
                while ((len = in.read(buffers)) != -1) {
                    out.write(buffers, 0, len);
                }
                return out.toByteArray();
            }
        } catch (Exception ex) {
            //NOOP
        }
        return null;
    }

(3)通过自定义的类加载器,加载类,然后获得Test对象,然后反射调用器方法:

   public static void main(String[] args) throws Exception {
        MyClassLoader myClassLoader = new MyClassLoader("D:\\file");
        Class clazz = myClassLoader.loadClass("com.test.Test");
        if (clazz != null) {
            Object obj = clazz.newInstance();
            Method method = clazz.getMethod("dateToStr", new Class[]{Date.class});
            String dateStr = (String) method.invoke(obj, new Date());
            System.out.println(dateStr);
            System.out.println(clazz.getClassLoader().toString());
        }
    }

(4)验证输入结果:

2018-08-04 16:33:49
com.XXXX.com20180719.classloader.MyClassLoader@ae4cf81

猜你喜欢

转载自blog.csdn.net/qq_21190979/article/details/81412513