Maison > Java > javaDidacticiel > Apprentissage de la machine virtuelle Java – Chargeur de classe (ClassLoader)

Apprentissage de la machine virtuelle Java – Chargeur de classe (ClassLoader)

黄舟
Libérer: 2017-02-17 10:31:06
original
1660 Les gens l'ont consulté

Class Loader

Le chargeur de classe (ClassLoader) est utilisé pour charger le bytecode de classe dans la machine virtuelle Java. De manière générale, la manière dont la machine virtuelle Java utilise les classes Java est la suivante : les fichiers sources Java sont convertis en fichiers bytecode Java (fichiers .class) après passage via Javac. Le chargeur de classe est chargé de lire le bytecode Java et de le convertir en une instance de la classe java.lang.Class. Chacune de ces instances représente une classe Java. La situation réelle peut être plus compliquée. Par exemple, le byte code Java peut être généré dynamiquement via des outils ou téléchargé via le réseau.


Classes et chargeurs de classes

Bien que le chargeur de classe ne soit utilisé que pour implémenter l'action de chargement des classes, il est utilisé dans Programmes Java Le rôle qui y est joué est loin de se limiter à l'étape de chargement des classes. Pour toute classe, le chargeur de classe qui la charge et la classe elle-même doivent établir son caractère unique dans la virtualisation Java. Pour faire plus simple, comparer deux classes pour voir si elles sont "égales" n'a de sens que si les deux classes sont chargées par le même chargeur de classe. Sinon, même si les deux classes proviennent du même fichier de classe, du moment que celui-ci est chargé. est chargé Si les chargeurs de classes sont différents, alors les deux classes ne doivent pas être égales. L'« égalité » mentionnée ici inclut la méthode equal, la méthode isAssignableFrom(), la méthode isInstance() et le résultat renvoyé par le mot-clé instance de l'objet Class représentant la classe.

Classification du chargeur de classe :



Principalement divisé en Bootstrap ClassLoader, Extension ClassLoader, Application ClassLoader et User Defined ClassLoader.

Bootstrap ClassLoader :

Ce chargeur de classe est implémenté en langage C et n'est pas une sous-classe de ClassLoader. Principalement responsable du chargement de tous les fichiers de classe stockés dans JAVA_HOME/jre/lib/rt.jar, ou des fichiers nommés rt.jar dans le chemin spécifié par le paramètre -Xbootclasspath.

Extension ClassLoader :

Ce chargeur est implémenté par sun.misc.Launcher$ExtClassLoader, qui est responsable du chargement du répertoire AVA_HOME/lib/ext, ou de toutes les classes bibliothèques dans le chemin spécifié par la variable système java.ext.dirs.

Application ClassLoader :

Ce chargeur est implémenté par sun.misc.Launcher$AppClassLoader, qui est responsable du chargement du jar et du répertoire correspondant au classpath . Normalement, il s'agit du chargeur de classe par défaut du programme.

Chargeur de classe personnalisé (User Defined ClassLoader) :

Les développeurs héritent de la classe abstraite ClassLoader et implémentent leur propre chargeur de classe Basé sur le ClassLoader auto-développé, il peut. être utilisé pour ne pas charger le chemin de classe (comme un jar ou un bytecode binaire téléchargé depuis Internet), vous pouvez également effectuer quelques petites actions avant de charger le fichier de classe, comme le cryptage, etc.

Modèle de délégation parentale :

La relation hiérarchique entre les chargeurs de classe illustrée dans la figure ci-dessus est appelée le modèle de délégation parentale des chargeurs de classe. Le modèle de délégation parent exige qu'en plus du chargeur de classe de démarrage de niveau supérieur, tous les autres chargeurs de classe disposent de leurs propres chargeurs de classe parent. La relation parent-enfant entre les chargeurs de classe n'est généralement pas implémentée ici par héritage, mais une relation de combinaison est utilisée pour réutiliser le code du chargeur parent.


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);
}
Copier après la connexion


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

使用双亲委托模型来组织类加载器之间的关系,有一个显而易见的好处就是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;
    }
Copier après la connexion
findLoadedClass 此方法负责从当前ClassLoader实例对象的缓存中寻找已加载的类,调用的为native方法。



    protected final Class<?> findLoadedClass(String name) {
	if (!checkName(name))
	    return null;
	return findLoadedClass0(name);
    }

    private native final Class findLoadedClass0(String name);
Copier après la connexion


findClass 此方法直接抛出ClassNotFoundException异常,因此要通过覆盖loadClass或此方法来以自定义的方式加载相应的类。


    protected Class<?> findClass(String name) throws ClassNotFoundException {
	throw new ClassNotFoundException(name);
    }
Copier après la connexion


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);
    }
Copier après la connexion


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;
    }
Copier après la connexion
resolveClass 此方法负责完成Class对象的链接,如果链接过,则直接返回。


Exceptions courantes :

ClassNotFoundException Il s'agit de l'exception la plus courante. La raison de cette exception est que le fichier de classe n'a pas été trouvé lors du chargement de la classe dans le ClassLoader actuel. >NoClassDefFoundError Cette exception est due au fait qu'une autre classe référencée dans la classe chargée n'existe pas. Par exemple, si A doit être chargé et que B est volé à A et que B n'existe pas ou que le ClassLoader actuel ne peut pas charger B, cette exception se produira. être jeté.

LinkageError Cette exception est plus susceptible de se produire lorsqu'un ClassLoader personnalisé est utilisé. La raison principale est que cette classe a déjà été chargée dans le ClassLoader et un chargement répété provoquera cette exception.


Ce qui précède est le contenu de l'apprentissage de la machine virtuelle Java - ClassLoader Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois (www.php.cn. ) !


Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal