首页 > 其他分享 >ClassLoader

ClassLoader

时间:2022-11-29 20:35:07浏览次数:54  
标签:name ClassLoader String ous null Class 加载

ClassLoader:

用来加载 Class 的。负责将 Class 的字节码形式转换成内存形式的 Class 对象。字节码可以来自于磁盘文件 *.class,也可以是 jar 包里的 *.class,也可以来自远程服务器提供的字节流,字节码的本质就是一个字节数组 []byte,它有特定的复杂的内部格式。

JVM 中内置了三个重要的 ClassLoader,分别是 BootstrapClassLoader、ExtensionClassLoader 和 AppClassLoader。

AppClassLoader 是直接面向我们用户的加载器 。

AppClassLoader 可以由 ClassLoader 类提供的静态方法 getSystemClassLoader() 得到,它就是我们所说的「系统类加载器」

当调用函数getClassLoader()函数返回一个null,说明它调用了一个BootstrapClassLoader(由c++语言编写)。

ClassLoader类有如下核心方法:

  1. loadClass(加载指定的Java类)
  2. findClass(查找指定的Java类)
  3. findLoadedClass(查找JVM已经加载过的类)
  4. defineClass(定义一个Java类)
  5. resolveClass(链接指定的Java类)

常见类动态加载的方式:Class.forName,ClassLoader.loadClass

class A{
    public A(){
        System.out.println("A...");
    }
    public int add(int a,int b){
        return a+b;
    }
}
public class example {
    public static void main(String[] args) {
        try {
        
        	//反射加载
            Class aclass = Class.forName("dtjz.A",;
            System.out.println(aclass);
            example a = new example();
            //classloader加载
            Class<?>  bclass = a.getClass().getClassLoader().loadClass("dtjz.A");
            System.out.println(bclass);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

}

双亲委派

双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载 器。不过这里类加载器之间的父子关系一般不是以继承的关系来实现的,而是通常使用 组合关系来复用父加载器的代码。

双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加 载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的 加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请 求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。

使用双亲委派模型来组织类加载器之间的关系,一个显而易见的好处就是Java中的类随着它的类 加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在rt.jar之中,无论哪一 个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类 在程序的各种类加载器环境中都能够保证是同一个类。反之,如果没有使用双亲委派模型,都由各个 类加载器自行去加载的话,如果用户自己也编写了一个名为java.lang.Object的类,并放在程序的 ClassPath中,那系统中就会出现多个不同的Object类,Java类型体系中最基础的行为也就无从保证,应用程序将会变得一片混乱。如果读者有兴趣的话,可以尝试去写一个与rt.jar类库中已有类重名的Java 类,将会发现它可以正常编译,但永远无法被加载运行

双亲委派模型的实现 :

protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
// 首先,检查请求的类是否已经被加载过了
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 如果父类加载器抛出ClassNotFoundException
// 说明父类加载器无法完成加载请求
}
if (c == null) {
// 在父类加载器无法加载时
// 再调用本身的findClass方法来进行类加载
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}

自定义ClassLoader:

java.lang.ClassLoader是所有的类加载器的父类,我们来写一个类加载器来实现加载自定义的字节码 ,重写findClass方法从而实现了加载目录class文件甚至是远程资源文件。

这里我自己写了一个类加载器,我将加载文件放在了D盘,TestHello的hello方法就是返回hello字符串

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;

public class MyClassLoader extends ClassLoader{
    private static String testClassName= "hello.TestHello";

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] testClassBytes = getDataFromFile(name);
        return defineClass(testClassName,testClassBytes,0,testClassBytes.length);
    }

    private byte[] getDataFromFile(String name){
        InputStream ins = null;
        ByteArrayOutputStream ous = null;
        byte[] result = null;
        int num = -1;
        try {
            ins = new FileInputStream("D:/" +name.substring(6) + ".class");
            ous = new ByteArrayOutputStream();
            byte[] buffer = new byte[10];
            while((num = ins.read(buffer))!= -1) {
                ous.write(buffer, 0, num);
            }
            result = ous.toByteArray();

        }catch(Exception e){
            e.printStackTrace();
        }finally {
            try {
                if(ins != null) ins.close();
                if(ous != null) ous.close();
            }catch(Exception e){
                e.printStackTrace();
            }
            return result;
        }
    }


    public static void main(String[] args) {
        MyClassLoader loader = new MyClassLoader();
        try{
            Class testClass = loader.loadClass(testClassName);
            Object obj = testClass.newInstance();
            Method method = obj.getClass().getMethod("hello");
            String str = (String)method.invoke(obj);

            System.out.println(str);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

这里就成功实现了我们自己的类加载器

这里我用

System.out.println(testClass.getClassLoader());

可以看到用的就是我们自己的类加载器。

类加载隔离

创建类加载器的时候可以指定该类加载的父类加载器,ClassLoader是有隔离机制的,不同的ClassLoader可以加载相同的Class(两则必须是非继承关系),同级ClassLoader跨类加载器调用方法时必须使用反射。

这里以ClassLoaderA和ClassLoaderB两个不同的加载器加载同一个class文件,他们

的class对象却不是同一个对象,只能通过反射相互调用

这里我根据前面的代码写了两个类加载器

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


class ClassLoaderA extends ClassLoader{
        private String testClassName= "hello.TestHello";

        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            byte[] testClassBytes = getDataFromFile(name);
            return defineClass(testClassName,testClassBytes,0,testClassBytes.length);
        }

        private byte[] getDataFromFile(String name){
            InputStream ins = null;
            ByteArrayOutputStream ous = null;
            byte[] result = null;
            int num = -1;
            try {
                ins = new FileInputStream("D:/" +name.substring(6) + ".class");
                ous = new ByteArrayOutputStream();
                byte[] buffer = new byte[10];
                while((num = ins.read(buffer))!= -1) {
                    ous.write(buffer, 0, num);
                }
                result = ous.toByteArray();

            }catch(Exception e){
                e.printStackTrace();
            }finally {
                try {
                    if(ins != null) ins.close();
                    if(ous != null) ous.close();
                }catch(Exception e){
                    e.printStackTrace();
                }
                return result;
            }
        }
    }


    class ClassLoaderB extends ClassLoader{
        private  String testClassName= "hello.TestHello";

        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            byte[] testClassBytes = getDataFromFile(name);
            return defineClass(testClassName,testClassBytes,0,testClassBytes.length);
        }

        private byte[] getDataFromFile(String name){
            InputStream ins = null;
            ByteArrayOutputStream ous = null;
            byte[] result = null;
            int num = -1;
            try {
                ins = new FileInputStream("D:/" +name.substring(6) + ".class");
                ous = new ByteArrayOutputStream();
                byte[] buffer = new byte[10];
                while((num = ins.read(buffer))!= -1) {
                    ous.write(buffer, 0, num);
                }
                result = ous.toByteArray();

            }catch(Exception e){
                e.printStackTrace();
            }finally {
                try {
                    if(ins != null) ins.close();
                    if(ous != null) ous.close();
                }catch(Exception e){
                    e.printStackTrace();
                }
                return result;
            }
        }
    }

public class gl {
    public static void main(String[] args) {
        try {

            ClassLoaderA classLoaderA = new ClassLoaderA();
            ClassLoaderB classLoaderB = new ClassLoaderB();
            Class<?> aclass = Class.forName("hello.TestHello",true,classLoaderA);
            Class<?> aaclass = Class.forName("hello.TestHello",true,classLoaderA);
            Class<?> bclass = Class.forName("hello.TestHello",true,classLoaderB);
            System.out.println(aclass == aaclass);
            System.out.println(aaclass == bclass);
            Object instanceA = aclass.newInstance();
            Method helloMethod = aclass.getMethod("hello");
            String result = (String)helloMethod.invoke(instanceA);
            System.out.println(result);

        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

从结果中可以看到,不同加载器加载同一个class文件生成的类对象不同。

标签:name,ClassLoader,String,ous,null,Class,加载
From: https://www.cnblogs.com/lscnnn/p/16936584.html

相关文章