Explication détaillée du chargeur de classe Java ClassLoader
Façons d'obtenir ClassLoader
1 Obtenez le ClassLoader de la classe actuelle
clazz.getClassLoader()
. 2. Obtenez le ClassLoader
Thread.currentThread().getContextClassLoader();
du contexte de thread actuel 3. Obtenez le ClassLoader
ClassLoader.getSystemClassLoader()
du système 4. Obtenez l'analyse du code source du ClassLoader
DriverManager.getCallerClassLoader
ClassLoader de l'appelant.
(Apprentissage recommandé : Tutoriel vidéo Java)
Présentation
Le chargeur de classe est utilisé pour charger des classes Object, ClassLoader est une classe abstraite. Si on nous donne le nom binaire d'une classe, le chargeur de classe doit essayer de localiser ou de générer les données qui composent la classe de définition. Une stratégie typique consiste à convertir un nom binaire donné en nom de fichier, puis à lire le fichier de classe correspondant à ce nom de fichier à partir du système de fichiers.
Chaque objet Class contiendra une référence au ClassLoader qui le définit.
L'objet Class de la classe array n'est pas créé par le chargeur de classe, mais est automatiquement créé par la JVM selon les besoins pendant l'exécution Java. Pour le chargeur de classe de la classe tableau, il est renvoyé via Class.getClassLoader(), qui est le même que le chargeur de classe du type d'élément dans le tableau si le type d'élément dans le tableau est un ; Type natif, La classe tableau n'a pas de chargeur de classe [Code 1].
L'application implémente une sous-classe de ClassLoader pour étendre la façon dont la JVM charge dynamiquement les classes.
Les chargeurs de classes sont généralement utilisés par les responsables de la sécurité pour identifier les problèmes du domaine de sécurité.
La classe ClassLoader utilise un modèle de délégation pour rechercher des classes et des ressources. Chaque instance de ClassLoader sera associée à un ClassLoader parent. Lorsque le ClassLoader est invité à trouver une classe ou une ressource, l'instance ClassLoader essaie celle-ci. own Avant de rechercher une classe ou une ressource, son chargeur de classe parent sera délégué pour la compléter. Le chargeur de classe intégré de la machine virtuelle, appelé chargeur de classe de démarrage, n'a pas de chargeur parent, mais il peut être utilisé comme chargeur de classe parent d'un chargeur de classe [Mécanisme de délégation parentale].
Un chargeur de classe qui prend en charge le chargement de classe simultané est appelé chargeur de classe parallèle. Il est nécessaire de s'enregistrer via la méthode ClassLoader.registerAsParallelCapable lors de l'initialisation. par défaut, mais si les sous-classes sont également chargées en parallèle, vous devez enregistrer les sous-classes séparément.
Dans un environnement où le modèle de délégation n'est pas strictement hiérarchique, les chargeurs de classes doivent être parallélisés, sinon le chargement de classe provoquera un blocage car le verrou du chargeur est toujours maintenu pendant le processus de chargement de classe.
Normalement, la machine virtuelle Java charge les classes à partir du système de fichiers local en fonction de la plate-forme. Par exemple, dans les systèmes UNIX, la machine virtuelle charge les classes à partir du répertoire défini par l'environnement CLASSPATH.
Cependant, certaines classes ne proviennent pas de fichiers ; elles sont obtenues à partir d'autres sources, comme le réseau, ou sont construites par l'application elle-même [proxy dynamiques]. La méthode définirClass convertira le tableau d'octets en une instance de Class. Cette instance de la classe nouvellement définie peut être créée par Class.newInstance.Les méthodes et constructeurs d'objets créés par un chargeur de classe peuvent référencer d'autres classes. Afin de déterminer la classe référencée, la machine virtuelle Java appelle la loadClass du chargeur de classe qui a initialement créé la classe. méthode de classe.
Nom binaire : tout nom de classe fourni à CalssLoader sous la forme d'un paramètre de chaîne doit être un nom binaire, comprenant les quatre cas suivants :
- "java.lang. String" Classe normale
- "javax.swing.JSpinner$DefaultEditor" Classe interne
- "java.security.KeyStore(Builder)FileBuilder$1" Classe interne de KeyStore Builder La classe interne de FileBuilder La première classe interne anonyme
- "java.net.URLClassLoader$3$1" La troisième classe interne anonyme de la classe URLClassLoader La première classe interne anonyme
Code 1 :
public class Test12 { public static void main(String[] args) { String[] strings = new String[6]; System.out.println(strings.getClass().getClassLoader()); // 运行结果:null Test12[] test12s = new Test12[1]; System.out.println(test12s.getClass().getClassLoader()); // 运行结果:sun.misc.Launcher$AppClassLoader@18b4aac2 int[] ints = new int[2]; System.out.println(ints.getClass().getClassLoader()); // 运行结果:null } }
Méthode LoadClass
Le code source de LoadClass est le suivant La méthode LoadClass charge une classe avec un nom binaire spécifié. Par défaut, elle recherche les classes dans. l'ordre suivant :
a) Appelez findLoadedClass(String) pour vérifier si cette classe est chargée
b) Appelez la méthode loadClass du chargeur de classe parent Si le chargeur de classe parent est nul, la classe de démarrage sera appelée. Le chargeur
c) appelle la méthode findClass(String) pour trouver
en utilisant les étapes ci-dessus. Si la classe est trouvée et que la résolution est vraie, la classe solveClass(Class. ) la méthode sera appelée
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); 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. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
méthode findClass
findClass的源码如下,findClass寻找拥有指定二进制名称的类,JVM鼓励我们重写此方法,需要自定义加载器遵循双亲委托机制,该方法会在检查完父类加载器之后被loadClass方法调用,默认返回ClassNotFoundException异常。
protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }
defineClass方法
defineClass的源码如下,defineClass方法将一个字节数组转换为Class的实例。
protected final Class<?> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain) throws ClassFormatError { protectionDomain = preDefineClass(name, protectionDomain); String source = defineClassSourceLocation(protectionDomain); Class<?> c = defineClass1(name, b, off, len, protectionDomain, source); postDefineClass(c, protectionDomain); return c; }
自定义类加载器
/** * 继承了ClassLoader,这是一个自定义的类加载器 * @author 夜的那种黑丶 */ public class ClassLoaderTest extends ClassLoader { public static void main(String[] args) throws Exception { ClassLoaderTest loader = new ClassLoaderTest("loader"); Class<?> clazz = loader.loadClass("classloader.Test01"); Object object = clazz.newInstance(); System.out.println(object); System.out.println(object.getClass().getClassLoader()); } //------------------------------以上为测试代码--------------------------------- /** * 类加载器名称,标识作用 */ private String classLoaderName; /** * 从磁盘读物字节码文件的扩展名 */ private String fileExtension = ".class"; /** * 创建一个类加载器对象,将系统类加载器当做该类加载器的父加载器 * @param classLoaderName 类加载器名称 */ private ClassLoaderTest(String classLoaderName) { // 将系统类加载器当做该类加载器的父加载器 super(); this.classLoaderName = classLoaderName; } /** * 创建一个类加载器对象,显示指定该类加载器的父加载器 * 前提是需要有一个类加载器作为父加载器 * @param parent 父加载器 * @param classLoaderName 类加载器名称 */ private ClassLoaderTest(ClassLoader parent, String classLoaderName) { // 显示指定该类加载器的父加载器 super(parent); this.classLoaderName = classLoaderName; } /** * 寻找拥有指定二进制名称的类,重写ClassLoader类的同名方法,需要自定义加载器遵循双亲委托机制 * 该方法会在检查完父类加载器之后被loadClass方法调用 * 默认返回ClassNotFoundException异常 * @param className 类名 * @return Class的实例 * @throws ClassNotFoundException 如果类不能被找到,抛出此异常 */ @Override protected Class<?> findClass(String className) throws ClassNotFoundException { byte[] data = this.loadClassData(className); /* * 通过defineClass方法将字节数组转换为Class * defineClass:将一个字节数组转换为Class的实例,在使用这个Class之前必须要被解析 */ return this.defineClass(className, data, 0 , data.length); } /** * io操作,根据类名找到对应文件,返回class文件的二进制信息 * @param className 类名 * @return class文件的二进制信息 * @throws ClassNotFoundException 如果类不能被找到,抛出此异常 */ private byte[] loadClassData(String className) throws ClassNotFoundException { InputStream inputStream = null; byte[] data; ByteArrayOutputStream byteArrayOutputStream = null; try { this.classLoaderName = this.classLoaderName.replace(".", "/"); inputStream = new FileInputStream(new File(className + this.fileExtension)); byteArrayOutputStream = new ByteArrayOutputStream(); int ch; while (-1 != (ch = inputStream.read())) { byteArrayOutputStream.write(ch); } data = byteArrayOutputStream.toByteArray(); } catch (Exception e) { throw new ClassNotFoundException(); } finally { try { if (inputStream != null) { inputStream.close(); } if (byteArrayOutputStream != null) { byteArrayOutputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } return data; } }
以上是一段自定义类加载器的代码,我们执行这段代码
classloader.Test01@7f31245a sun.misc.Launcher$AppClassLoader@18b4aac2
可以看见,这段代码中进行类加载的类加载器还是系统类加载器(AppClassLoader)。这是因为jvm的双亲委托机制造成的,private ClassLoaderTest(String classLoaderName)
将系统类加载器当做我们自定义类加载器的父加载器,jvm的双亲委托机制使自定义类加载器委托系统类加载器完成加载。
改造以下代码,添加一个path属性用来指定类加载位置:
public class ClassLoaderTest extends ClassLoader { public static void main(String[] args) throws Exception { ClassLoaderTest loader = new ClassLoaderTest("loader"); loader.setPath("/home/fanxuan/Study/java/jvmStudy/out/production/jvmStudy/"); Class<?> clazz = loader.loadClass("classloader.Test01"); System.out.println("class:" + clazz); Object object = clazz.newInstance(); System.out.println(object); System.out.println(object.getClass().getClassLoader()); } //------------------------------以上为测试代码--------------------------------- /** * 从指定路径加载 */ private String path; ...... /** * io操作,根据类名找到对应文件,返回class文件的二进制信息 * @param className 类名 * @return class文件的二进制信息 * @throws ClassNotFoundException 如果类不能被找到,抛出此异常 */ private byte[] loadClassData(String className) throws ClassNotFoundException { InputStream inputStream = null; byte[] data; ByteArrayOutputStream byteArrayOutputStream = null; className = className.replace(".", "/"); try { this.classLoaderName = this.classLoaderName.replace(".", "/"); inputStream = new FileInputStream(new File(this.path + className + this.fileExtension)); byteArrayOutputStream = new ByteArrayOutputStream(); int ch; while (-1 != (ch = inputStream.read())) { byteArrayOutputStream.write(ch); } data = byteArrayOutputStream.toByteArray(); } catch (Exception e) { throw new ClassNotFoundException(); } finally { try { if (inputStream != null) { inputStream.close(); } if (byteArrayOutputStream != null) { byteArrayOutputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } return data; } public void setPath(String path) { this.path = path; } }
运行一下
class:class classloader.Test01 classloader.Test01@7f31245a sun.misc.Launcher$AppClassLoader@18b4aac2
修改一下测试代码,并删除工程下的Test01.class文件
public static void main(String[] args) throws Exception { ClassLoaderTest loader = new ClassLoaderTest("loader"); loader.setPath("/home/fanxuan/桌面/"); Class<?> clazz = loader.loadClass("classloader.Test01"); System.out.println("class:" + clazz); Object object = clazz.newInstance(); System.out.println(object); System.out.println(object.getClass().getClassLoader()); }
运行一下
class:class classloader.Test01 classloader.Test01@135fbaa4 classloader.ClassLoaderTest@7f31245a
分析
改造后的两块代码,第一块代码中加载类的是系统类加载器AppClassLoader,第二块代码中加载类的是自定义类加载器ClassLoaderTest。是因为ClassLoaderTest会委托他的父加载器AppClassLoader加载class,第一块代码的path直接是工程下,AppClassLoader可以加载到,而第二块代码的path在桌面目录下,所以AppClassLoader无法加载到,然后ClassLoaderTest自身尝试加载并成功加载到。如果第二块代码工程目录下的Test01.class文件没有被删除,那么依然是AppClassLoader加载。
再来测试一块代码
public static void main(String[] args) throws Exception { ClassLoaderTest loader = new ClassLoaderTest("loader"); loader.setPath("/home/fanxuan/Study/java/jvmStudy/out/production/jvmStudy/"); Class<?> clazz = loader.loadClass("classloader.Test01"); System.out.println("class:" + clazz.hashCode()); Object object = clazz.newInstance(); System.out.println(object.getClass().getClassLoader()); ClassLoaderTest loader2 = new ClassLoaderTest("loader"); loader2.setPath("/home/fanxuan/Study/java/jvmStudy/out/production/jvmStudy/"); Class<?> clazz2 = loader2.loadClass("classloader.Test01"); System.out.println("class:" + clazz2.hashCode()); Object object2 = clazz2.newInstance(); System.out.println(object2.getClass().getClassLoader()); }
结果显而易见,类由系统类加载器加载,并且clazz和clazz2是相同的。
class:2133927002 sun.misc.Launcher$AppClassLoader@18b4aac2 class:2133927002 sun.misc.Launcher$AppClassLoader@18b4aac2
再改造一下
public static void main(String[] args) throws Exception { ClassLoaderTest loader = new ClassLoaderTest("loader"); loader.setPath("/home/fanxuan/桌面/"); Class<?> clazz = loader.loadClass("classloader.Test01"); System.out.println("class:" + clazz.hashCode()); Object object = clazz.newInstance(); System.out.println(object.getClass().getClassLoader()); ClassLoaderTest loader2 = new ClassLoaderTest("loader2"); loader2.setPath("/home/fanxuan/桌面/"); Class<?> clazz2 = loader2.loadClass("classloader.Test01"); System.out.println("class:" + clazz2.hashCode()); Object object2 = clazz2.newInstance(); System.out.println(object2.getClass().getClassLoader()); }
运行结果
class:325040804 classloader.ClassLoaderTest@7f31245a class:621009875 classloader.ClassLoaderTest@45ee12a7
ClassLoaderTest是显而易见,但是clazz和clazz2是不同的,这是因为类加载器的命名空间的原因。
我们可以通过设置父类加载器来让loader和loader2处于同一命名空间
public static void main(String[] args) throws Exception { ClassLoaderTest loader = new ClassLoaderTest("loader"); loader.setPath("/home/fanxuan/桌面/"); Class<?> clazz = loader.loadClass("classloader.Test01"); System.out.println("class:" + clazz.hashCode()); Object object = clazz.newInstance(); System.out.println(object.getClass().getClassLoader()); ClassLoaderTest loader2 = new ClassLoaderTest(loader, "loader2"); loader2.setPath("/home/fanxuan/桌面/"); Class<?> clazz2 = loader2.loadClass("classloader.Test01"); System.out.println("class:" + clazz2.hashCode()); Object object2 = clazz2.newInstance(); System.out.println(object2.getClass().getClassLoader()); }
运行结果
class:325040804 classloader.ClassLoaderTest@7f31245a class:325040804 classloader.ClassLoaderTest@7f31245a
扩展:命名空间
1. 每个类加载器都有自己的命名空间,命名空间由该加载器及所有的父加载器所加载的类组成
2. 在同一命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类
3. 在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类
php中文网,大量的免费Java入门教程,欢迎在线学习!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

Video Face Swap
Échangez les visages dans n'importe quelle vidéo sans effort grâce à notre outil d'échange de visage AI entièrement gratuit !

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Comment résoudre les conflits de chargeur de classe dans le développement Java Introduction : Dans le développement Java, les conflits de chargeur de classe sont un problème courant. Lorsque différents chargeurs de classes sont utilisés pour charger la même classe ou le même fichier de ressources, des conflits se produiront, entraînant un échec du programme. Cet article explique ce qu'est un conflit de chargeur de classe et comment le résoudre. 1. Qu'est-ce qu'un conflit de chargeur de classe ?Le mécanisme de chargement de classe en Java adopte le modèle de délégation parent. Chaque chargeur de classe a un chargeur de classe parent, et le chargeur de classe parent final est le chargeur de classe de démarrage. en cas de besoin

Lors du développement à l'aide de Java, nous rencontrons souvent des erreurs ClassLoader, provoquées par le fait que le chargeur de classe ne trouve pas ou ne charge pas la bonne classe. Ce type d'erreur empêchera le programme de s'exécuter normalement et entraînera des désagréments au développement. Par conséquent, nous devons comprendre les causes des erreurs ClassLoader et leurs solutions afin de mieux optimiser nos programmes Java. 1. Causes des erreurs ClassLoader : les classes sont chargées via ClassLoader en Java, ClassLoad

Contexte Récemment, les codes commerciaux clés ont été cryptés pour le cadre de l'entreprise afin d'empêcher la restauration facile du code d'ingénierie via des outils de décompilation tels que jd-gui. La configuration et l'utilisation du schéma d'obscurcissement associé sont relativement complexes et posent de nombreux problèmes. projet springboot, donc les fichiers de classe sont chiffrés puis transmis. Le classloder personnalisé est déchiffré et chargé. Cette solution n'est pas absolument sûre. Elle ne fait qu'augmenter la difficulté de la décompilation. Elle empêche les messieurs mais pas les méchants. dans la figure ci-dessous. Le plug-in maven chiffre la compilation à l'aide d'un plug-in maven personnalisé. Le fichier de classe spécifié est chiffré et le fichier de classe chiffré est copié ici dans le chemin spécifié.

Les principes et méthodes de chargement de classe à l'aide de la fonction ClassLoader en Java ont toujours été l'une des priorités des développeurs Java. La fonction ClassLoader fait partie de la bibliothèque de classes Java. Sa fonction principale est de charger les fichiers de classe Java dans la machine virtuelle Java (JVM) afin que le programme puisse s'exécuter normalement. La fonction ClassLoader est au cœur du chargement des classes Java. Dans l'environnement d'exécution Java, elle est responsable de la recherche et du chargement du bytecode des classes Java, donc comprenez-le et maîtrisez-le.

La fonction ClassLoader en Java peut charger dynamiquement des classes au moment de l'exécution, ce qui est très utile dans certaines applications nécessitant un déploiement et une modification flexibles du code. Grâce à la fonction de ClassLoader, le mécanisme de plug-in peut être implémenté pour améliorer l'évolutivité et la flexibilité du système. Cet article explique comment utiliser la fonction ClassLoader pour le chargement dynamique de classes. 1. Le rôle du ClassLoader Lors du démarrage de la Machine Virtuelle Java (JVM), trois ClassLoaders seront créés :

Présentation du chargement automatique de PHP Le chargement automatique de PHP signifie que lors de l'utilisation d'une classe, PHP chargera automatiquement le fichier de définition de la classe. Ceci est généralement réalisé via un chargeur de classe. Un chargeur de classe est un programme chargé de charger les fichiers de définition de classe. Il peut être intégré ou personnalisé. Types de chargeurs de classes Il existe deux types de chargeurs de classes intégrés à PHP : Chargeur de classes Zend : Il s'agit du chargeur de classes par défaut de PHP, qui charge les fichiers de définition de classes situés dans les bibliothèques intégrées de PHP. Chargeur de classe PSR-4 : PSR-4 est une norme de chargement automatique qui définit un ensemble de règles pour le chargement des fichiers de définition de classe. Le chargeur de classe PSR-4 charge les fichiers de définition de classe conformément à la norme PSR-4. De plus, les chargeurs de classes peuvent être personnalisés. Les chargeurs de classe personnalisés peuvent être personnalisés selon leurs propres

Le rôle du chargeur de classe : Chargement : Lire les fichiers de classe à partir de la source spécifiée. Vérification : confirmez que le fichier de classe est conforme à la spécification. Préparation : allouer de la mémoire et initialiser les variables statiques. Analyser : analyser les références de symboles. Initialisation : appelez des méthodes, exécutez des blocs d'initialisation statiques et allouez des objets de classe.

Le chargement automatique est une technique qui charge automatiquement les classes PHP sans qu'il soit nécessaire d'inclure manuellement chaque fichier. Il simplifie le développement et la maintenance des applications, améliorant ainsi les performances et la maintenabilité. Cet article explorera la technologie de chargement automatique couramment utilisée en PHP. SPLAutoloaderSPL (Standard PHP Library) contient un mécanisme de chargement automatique intégré appelé fonction spl_autoload_reGISter(). Cette fonction vous permet d'enregistrer une fonction de chargement qui sera appelée lors d'une tentative de chargement d'une classe qui n'existe pas. L'exemple suivant montre comment utiliser SPLAutoloader : spl_autoload_register(function($class){
