Heim  >  Artikel  >  Java  >  Java Virtual Machine Learning – Klassenlader (ClassLoader)

Java Virtual Machine Learning – Klassenlader (ClassLoader)

黄舟
黄舟Original
2017-02-17 10:31:061552Durchsuche

Klassenlader

Der Klassenlader (ClassLoader) wird verwendet, um Klassenbytecode in die Java Virtual Machine zu laden. Im Allgemeinen verwendet die Java Virtual Machine Java-Klassen wie folgt: Java-Quelldateien werden nach dem Durchlaufen von Javac in Java-Bytecode-Dateien (.class-Dateien) konvertiert. Der Klassenlader ist dafür verantwortlich, Java-Bytecode zu lesen und ihn in eine Instanz der Klasse java.lang.Class zu konvertieren. Jede dieser Instanzen repräsentiert eine Java-Klasse. Die tatsächliche Situation kann komplizierter sein. Beispielsweise kann Java-Bytecode dynamisch über Tools generiert oder über das Netzwerk heruntergeladen werden.


Klassen und Klassenlader

Obwohl der Klassenlader nur zum Implementieren der Ladeaktion von Klassen verwendet wird, wird er in verwendet Java-Programme Die Rolle, die dabei gespielt wird, beschränkt sich bei weitem nicht auf die Phase des Klassenladens. Für jede Klasse müssen der Klassenlader, der sie lädt, und die Klasse selbst ihre Einzigartigkeit in der virtuellen Java-Welt feststellen. Um es einfacher auszudrücken: Der Vergleich zweier Klassen, um festzustellen, ob sie „gleich“ sind, ist nur dann sinnvoll, wenn die beiden Klassen von demselben Klassenlader geladen werden. Ansonsten gilt, selbst wenn die beiden Klassen aus derselben Klassendatei stammen, sofern dies der Fall ist wird geladen. Wenn die Klassenlader unterschiedlich sind, dürfen die beiden Klassen nicht gleich sein. Die hier erwähnte „Gleichheit“ umfasst die Methode equal, isAssignableFrom(), die Methode isInstance() und das Ergebnis, das vom Schlüsselwort „instance“ des Klassenobjekts zurückgegeben wird, das die Klasse darstellt.

Klassenladerklassifizierung:



Hauptsächlich unterteilt in Bootstrap ClassLoader, Extension ClassLoader, Application ClassLoader und User Defined ClassLoader.

Bootstrap ClassLoader:

Dieser Klassenlader ist in der Sprache C++ implementiert und keine Unterklasse von ClassLoader. Hauptverantwortlich für das Laden aller in JAVA_HOME/jre/lib/rt.jar gespeicherten Klassendateien oder Dateien mit dem Namen rt.jar in dem durch den Parameter -Xbootclasspath angegebenen Pfad.

Erweiterung ClassLoader:

Dieser Loader wird von sun.misc.Launcher$ExtClassLoader implementiert, der für das Laden des Verzeichnisses AVA_HOME/lib/ext oder aller Klassen verantwortlich ist Bibliotheken in dem durch die Systemvariable java.ext.dirs angegebenen Pfad.

Application ClassLoader:

Dieser Loader wird von sun.misc.Launcher$AppClassLoader implementiert, der für das Laden des JAR und des Verzeichnisses entsprechend dem Klassenpfad verantwortlich ist. Normalerweise ist dies der Standard-Klassenlader im Programm.

Benutzerdefinierter Klassenlader (Benutzerdefinierter ClassLoader):

Entwickler erben die abstrakte Klasse ClassLoader und implementieren ihren eigenen Klassenlader Wenn Sie den Klassenpfad laden (z. B. ein aus dem Internet heruntergeladenes JAR oder einen binären Bytecode), können Sie vor dem Laden der Klassendatei auch einige kleine Aktionen ausführen, z. B. Verschlüsselung usw.

Parentales Delegationsmodell:

Die in der obigen Abbildung gezeigte hierarchische Beziehung zwischen Klassenladern wird als übergeordnetes Delegationsmodell von Klassenladern bezeichnet. Das übergeordnete Delegationsmodell erfordert, dass zusätzlich zum Startklassenlader der obersten Ebene alle anderen Klassenlader über eigene übergeordnete Klassenlader verfügen. Die Eltern-Kind-Beziehung zwischen Klassenladern wird hier im Allgemeinen nicht durch Vererbung implementiert. Stattdessen wird eine Kombinationsbeziehung verwendet, um den Code des Elternladers wiederzuverwenden.


public abstract class ClassLoader {

    private static native void registerNatives();
    static {
        registerNatives();
    }

    // The parent class loader for delegation
    private ClassLoader parent;

    // Hashtable that maps packages to certs
    private Hashtable package2certs = new Hashtable(11);
}


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

使用双亲委托模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,例如java.lang.Object存放在rt.jar之中,无论那个类加载器要加载这个类,最终都是委托给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类,相反,如果没有双亲委托模型,由各个类加载器去完成的话,如果用户自己写一个名为java.lang.Object的类,并放在classpath中,应用程序中可能会出现多个不同的Object类,java类型体系中最基本安全行为也就无法保证。

类加载器SPI:

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对象的链接,如果链接过,则直接返回。


Häufige Ausnahmen:

ClassNotFoundException Dies ist die häufigste Ausnahme. Der Grund für diese Ausnahme ist, dass die Klassendatei beim Laden der Klasse im aktuellen ClassLoader nicht gefunden wurde >NoClassDefFoundError Diese Ausnahme liegt daran, dass eine andere Klasse, auf die in der geladenen Klasse verwiesen wird, nicht vorhanden ist. Wenn beispielsweise A geladen werden soll und B von A gestohlen wird und B nicht vorhanden ist oder der aktuelle ClassLoader B nicht laden kann, wird dies zu dieser Ausnahme führen geworfen werden.

LinkageError Diese Ausnahme tritt eher auf, wenn ein benutzerdefinierter ClassLoader verwendet wird. Der Hauptgrund dafür ist, dass diese Klasse bereits in den ClassLoader geladen wurde und ein wiederholtes Laden diese Ausnahme verursacht.


Das Obige ist der Inhalt von Java Virtual Machine Learning – ClassLoader. Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website (www.php.cn). ) !


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn