zoukankan      html  css  js  c++  java
  • JVM 自定义类加载器

    编写原则
    • 在JDK1.2之前,在自定义类加载器时,总会去重写loadClass方法,从而实现自定义的类加载类,但是JDK1.2之后已不再建议用户去覆盖loadClass方法,而是建议把自定义的类加载逻辑写在findClass方法中
    • 在编写自定义类加载器时,如果没有太过于复杂的需求,可以直接继承URLClassLoader类,这样就可以避免自已去编写findClass方法及获取字节码流的方式,使自定义类加载器编写更加简洁。

    示例:

    package com.classloader;
    
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    
    public class MyClassLoader extends ClassLoader {
    
        private static final String CLASS_PATH = System.getProperty("java.class.path"); // 编译生成的.class文件的bin目录
    
        public MyClassLoader() {
            super(ClassLoader.getSystemClassLoader());
        }
    
        @Override
        protected Class<?> findClass(String className) throws ClassNotFoundException {
            System.out.println("findClass ....");
            byte[] b = loadClassFile(className);
            return super.defineClass(className, b, 0, b.length);
        }
    
        
        private byte[] loadClassFile(String className) {
            System.out.println("loadClassFile ....");
            className = className.replace(".", "/");
            File file = new File(CLASS_PATH + "/" + className + ".class");
            try {
                FileInputStream fis = new FileInputStream(file);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                int b = 0;
                while ((b = fis.read()) != -1) {
                    baos.write(b);
                }
                fis.close();
                return baos.toByteArray();
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
            return null;
        }
    
    }

    说明:这个示例中MyClassLoader这个类没有重写loadClass方法,因此调用loadClass方法实际调用的是父类的loadClass方法,即loadClass 实质上是使用了系统类加载器加载的,而调用 findClass 方法才是真正意义上调用自定义类加载器加载

    (1)系统类加载器加载同一个类型时(即类的全限名一致),加载的Class实例只有一个,并且只加载一次。双亲委派模型

    MyClassLoader myClassLoader1 = new MyClassLoader();
    Class<?> clazz1 = myClassLoader1.loadClass(Sample.class.getName());
    MyClassLoader myClassLoader2 = new MyClassLoader();
    Class<?> clazz2 = myClassLoader2.loadClass(Sample.class.getName());
    System.out.println(myClassLoader1==myClassLoader2);// false
    System.out.println(clazz1==clazz2);// true
    下面这个实例也可以证明:
    同一个类的Class对象只有一个,当该类对象被加载后,就不会再去加载该类对应的Class对象,即使又执行了加载对象的操作;
    对于每一个Class对象,可以通过其getClassLoader()方法获得其类加载器的引用,所以Class对象内部有指向其类加载器的引用;
    Class<?> clazz1 = ClassLoader.getSystemClassLoader().loadClass(Sample.class.getName());
    Class<?> clazz2 = ClassLoader.getSystemClassLoader().loadClass(Sample.class.getName());
    System.out.println(clazz1.getClassLoader());// sun.misc.Launcher$AppClassLoader@2a139a55
    System.out.println(clazz1==clazz2);// true

    (2)用户自定义类加载器加载同一个类型时,可以加载多个Class实例。通过这种方式来破坏双亲委派模型,典型的应用如:Tomcat

    MyClassLoader myClassLoader1 = new MyClassLoader();
    Class<?> clazz1 = myClassLoader1.findClass(Sample.class.getName());
    MyClassLoader myClassLoader2 = new MyClassLoader();
    Class<?> clazz2 = myClassLoader2.findClass(Sample.class.getName());
    System.out.println(myClassLoader1==myClassLoader2);// false
    System.out.println(clazz1==clazz2);// false 

    调用 findClass 表示这个类是通过自定义类加载器加载的
    clazz1==clazz2 为false说明class实例在堆内存中属于不同的实例。

    备注:因为上面 MyClassLoader中的findClass方法是protected类型,可能在其它包下无法访问!因此,需要再重写一个public类型的 loadClass 方法

  • 相关阅读:
    PHP之旅3 php数组以及遍历数组 以及each() list() foreach()
    NSSetUncaughtExceptionHandler
    runtime
    Objective-C中的instancetype和id区别
    tableView 局部刷新
    CGAffineTransform
    iOS中文本属性Attributes
    ios 相机 自定义 相片的截取
    php程序的生命周期
    PHP代码执行流程
  • 原文地址:https://www.cnblogs.com/caoxb/p/12735536.html
Copyright © 2011-2022 走看看