클래스 로더(ClassLoader)는 클래스 바이트코드를 Java 가상 머신에 로드하는 데 사용됩니다. 일반적으로 JVM(Java Virtual Machine)은 다음과 같이 Java 클래스를 사용합니다. Java 소스 파일은 Javac를 거친 후 Java 바이트코드 파일(.class 파일)로 변환됩니다. 클래스 로더는 Java 바이트코드를 읽고 이를 java.lang.Class 클래스의 인스턴스로 변환하는 일을 담당합니다. 이러한 각 인스턴스는 Java 클래스를 나타냅니다. 실제 상황은 더 복잡할 수 있습니다. 예를 들어 Java 바이트 코드는 도구를 통해 동적으로 생성되거나 네트워크를 통해 다운로드될 수 있습니다.
클래스 로더는 클래스의 로딩 동작을 구현하는 데만 사용되지만, Java 프로그램 여기서 수행되는 역할은 클래스 로딩 단계에만 국한되지 않습니다. 모든 클래스에 대해 이를 로드하는 클래스 로더와 클래스 자체는 Java 가상 세계에서 고유성을 설정해야 합니다. 더 간단히 말하면, 두 클래스가 동일한 클래스 로더에 의해 로드되는 경우에만 두 클래스가 "동등"한지 비교하는 것이 의미가 있습니다. 그렇지 않으면 두 클래스가 동일한 클래스 파일에서 나온 경우에도 로드되는 한입니다. 클래스 로더가 다르면 두 클래스가 동일하면 안 됩니다. 여기서 말하는 "동등성"에는 equal 메소드, isAssignableFrom(), isInstance() 메소드와 클래스를 대표하는 Class 객체의 인스턴스 키워드가 반환하는 결과가 포함된다.
크게 Bootstrap ClassLoader, Extension ClassLoader, Application ClassLoader, User Defined ClassLoader로 나누어집니다.
Bootstrap ClassLoader:
이 클래스 로더는 C++ 언어로 구현되며 ClassLoader의 하위 클래스가 아닙니다. JAVA_HOME/jre/lib/rt.jar에 저장된 모든 클래스 파일 또는 -Xbootclasspath 매개변수로 지정된 경로에 있는 rt.jar 파일을 로드하는 일을 주로 담당합니다.
확장 클래스 로더:
이 로더는 sun.misc.Launcher$ExtClassLoader에 의해 구현되며 AVA_HOME/lib/ext 디렉토리 또는 모든 클래스를 로드하는 역할을 담당합니다. java.ext.dirs 시스템 변수로 지정된 경로에 있는 라이브러리.
애플리케이션 ClassLoader:
이 로더는 sun.misc.Launcher$AppClassLoader에 의해 구현되며, 클래스 경로에 해당하는 jar 및 디렉토리를 로드하는 역할을 담당합니다. 일반적으로 이는 프로그램의 기본 클래스 로더입니다.
커스텀 클래스 로더(User Defined ClassLoader):
개발자는 ClassLoader 추상 클래스를 상속받아 자체 클래스 로더를 구현합니다. 클래스 경로(예: 인터넷에서 다운로드한 jar 또는 이진 바이트 코드)를 로드하지 않는 데 사용되는 경우 클래스 파일을 로드하기 전에 암호화 등과 같은 몇 가지 작은 작업을 수행할 수도 있습니다.
위 그림에 표시된 클래스 로더 간의 계층 관계를 클래스 로더의 부모 위임 모델이라고 합니다. 상위 위임 모델에서는 최상위 시작 클래스 로더 외에 다른 모든 클래스 로더에도 자체 상위 클래스 로더가 있어야 합니다. 여기서 클래스 로더 간의 상위-하위 관계는 일반적으로 상속을 통해 구현되지 않으며 대신 상위 로더의 코드를 재사용하기 위해 조합 관계가 사용됩니다.
双亲委托的工作过程:如果一个类加载器收到了一个类加载请求,它首先不会自己去加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成加载请求(它管理的范围之中没有这个类)时,子加载器才会尝试着自己去加载。
使用双亲委托模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,例如java.lang.Object存放在rt.jar之中,无论那个类加载器要加载这个类,最终都是委托给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类,相反,如果没有双亲委托模型,由各个类加载器去完成的话,如果用户自己写一个名为java.lang.Object的类,并放在classpath中,应用程序中可能会出现多个不同的Object类,java类型体系中最基本安全行为也就无法保证。
java.lang.ClassLoader 类提供的几个关键方法:
loadClass: 此方法负责加载指定名字的类,首先会从已加载的类中去寻找,如果没有找到;从parent ClassLoader[ExtClassLoader]中加载;如果没有加载到,则从Bootstrap ClassLoader中尝试加载(findBootstrapClassOrNull方法), 如果还是加载失败,则抛出异常ClassNotFoundException, 在调用自己的findClass方法进行加载。如果要改变类的加载顺序可以覆盖此方法;如果加载顺序相同,则可以通过覆盖findClass方法来做特殊处理,例如:解密,固定路径寻找等。当通过整个寻找类的过程仍然未获取Class对象,则抛出ClassNotFoundException异常。
如果类需要resolve,在调用resolveClass进行链接。
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { 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. c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }
findLoadedClass 此方法负责从当前ClassLoader实例对象的缓存中寻找已加载的类,调用的为native方法。
protected final Class<?> findLoadedClass(String name) { if (!checkName(name)) return null; return findLoadedClass0(name); } private native final Class findLoadedClass0(String name);
findClass 此方法直接抛出ClassNotFoundException异常,因此要通过覆盖loadClass或此方法来以自定义的方式加载相应的类。
protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }
findSystemClass 此方法是从sun.misc.Launcher$AppClassLoader中寻找类,如果未找到,则继续从BootstrapClassLoader中寻找,如果仍然未找到,返回null
protected final Class<?> findSystemClass(String name) throws ClassNotFoundException { ClassLoader system = getSystemClassLoader(); if (system == null) { if (!checkName(name)) throw new ClassNotFoundException(name); Class cls = findBootstrapClass(name); if (cls == null) { throw new ClassNotFoundException(name); } return cls; } return system.loadClass(name); }
defineClass 此方法负责将二进制字节流转换为Class对象,这个方法对于自定义类加载器而言非常重要。如果二进制的字节码的格式不符合jvm class文件格式规范,则抛出ClassFormatError异常;如果生成的类名和二进制字节码不同,则抛出NoClassDefFoundError;如果加载的class是受保护的、采用不同签名的,或者类名是以java.开头的,则抛出SecurityException异常。
protected final Class<?> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain) throws ClassFormatError { return defineClassCond(name, b, off, len, protectionDomain, true); } // Private method w/ an extra argument for skipping class verification private final Class<?> defineClassCond(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain, boolean verify) throws ClassFormatError { protectionDomain = preDefineClass(name, protectionDomain); Class c = null; String source = defineClassSourceLocation(protectionDomain); try { c = defineClass1(name, b, off, len, protectionDomain, source, verify); } catch (ClassFormatError cfe) { c = defineTransformedClass(name, b, off, len, protectionDomain, cfe, source, verify); } postDefineClass(c, protectionDomain); return c; }
resolveClass 此方法负责完成Class对象的链接,如果链接过,则直接返回。
ClassNotFoundException 이 예외가 발생하는 이유는 현재 ClassLoader에서 클래스를 로드할 때 클래스 파일을 찾을 수 없기 때문입니다. >NoClassDefFoundError 이 예외는 로드된 클래스에 참조된 다른 클래스가 존재하지 않기 때문에 발생합니다. 예를 들어 A가 로드되고 B가 A에서 도난당했으며 B가 존재하지 않거나 현재 ClassLoader가 B를 로드할 수 없는 경우 이 예외는 발생합니다. 던져진다.
LinkageError 이 예외는 사용자 정의 ClassLoader를 사용할 때 발생할 가능성이 더 높습니다. 주된 이유는 이 클래스가 이미 ClassLoader에 로드되어 있으며 반복적인 로드로 인해 이 예외가 발생하기 때문입니다.
위는 Java 가상 머신러닝 - ClassLoader의 내용입니다. 더 많은 관련 내용은 PHP 중국어 홈페이지(www.php.cn)를 참고해주세요. ) !