Sous-système de chargeur de classe JVM et SPI

Chargeur de classe

Il existe deux types de chargeurs en JVM, l'un est écrit en C ++, l'autre est écrit en JAVA, à l'exception du Bootstrap Class Loader (Bootstrap Class Loader) est écrit en C ++, les autres sont écrits en JAVA, Les chargeurs écrits en JAVA héritent de java.lang.ClassLoader. JVM prend également en charge le chargeur personnalisé. L'utilisation typique du chargeur personnalisé consiste à détruire la délégation parentale de JVM. Pourquoi devrions-nous détruire la délégation parentale et vouloir détruire les deux parents? Le projet délégué doit avoir cette exigence. Par exemple, notre serveur Web tomcat a un chargeur de classe personnalisé. Non seulement le serveur Web, mais les cadres technologiques courants actuels personnaliseront le chargeur de classe et le chargeur de classe personnalisé pourra être chargé. La classe que vous souhaitez charger peut non seulement charger des fichiers de classe locaux, mais également des fichiers de classe à partir d'Internet.
Les chargeurs de classe de JVM incluent: le chargeur de classe de démarrage, le chargeur de classe étendu, le chargeur de classe d'application, le chargeur de classe personnalisé; le chargeur de classe JVM appartient au mode de délégation parent, c'est-à-dire lorsque mon chargeur de classe doit en charger un Lorsque la classe, il ne terminera pas cette opération par lui-même. Il déléguera la demande vers le haut jusqu'à ce que le chargeur de classe commence à se charger. Si le chargement échoue à la fin, c'est-à-dire lorsque la classe pleinement qualifiée n'est pas trouvée, la machine virtuelle Java lèvera Une exception ClassNotFound, la figure suivante montre la structure de notre chargeur de classe: la
Insérez la description de l'image ici
figure ci-dessus est le diagramme de structure du chargeur de classe, à partir de la figure, nous pouvons voir que nous voulons charger notre propre classe est déléguée à notre chargeur de classe parent up Terminé; le chargeur de classe de démarrage de niveau supérieur est écrit en C ++, et nous ne pouvons pas vérifier son implémentation. Les autres sont tous des chargeurs de classe implémentés par Java. Par conséquent, le chargeur de classe de démarrage ne peut pas être appelé par le programme Java
. Comme tout autre chargeur de classe a une entité, il n'a pas d'entité. La JVM définit un ensemble de logique que C ++ gère le chargement de classe en tant que chargeur de classe de démarrage.

Démarrez le chargeur de classe

Afficher le chemin de chargement du chargeur de classe de démarrage

URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
for (URL urL : urLs) {
    
    
    System.out.println(urL);
}

Sortie:
fichier: / D: /tools/java8_212/jre/lib/resources.jar
fichier: / D: /tools/java8_212/jre/lib/rt.jar
fichier: / D: / tools / java8_212 / jre / lib /
Fichier sunrsasign.jar : / D: /tools/java8_212/jre/lib/jsse.jar
fichier: / D: /tools/java8_212/jre/lib/jce.jar
fichier: / D: / tools / java8_212 / jre / lib
Fichier /charsets.jar : / D: /tools/java8_212/jre/lib/jfr.jar
fichier: / D: / tools / java8_212 / jre / classes
Vous pouvez voir que le chargeur de classe de démarrage charge principalement le noyau sous notre jre paquet

Chargeur de classe étendue

  ClassLoader classLoader = ClassLoader.getSystemClassLoader().getParent();

        URLClassLoader urlClassLoader = (URLClassLoader) classLoader;

        URL[] urls = urlClassLoader.getURLs();
        for (URL url : urls) {
    
    
            System.out.println(url);
        }

fichier: / D: /tools/java8_212/jre/lib/ext/access-bridge-64.jar
fichier: / D: /tools/java8_212/jre/lib/ext/cldrdata.jar
fichier: / D: / tools / Fichier java8_212 / jre / lib / ext / dnsns.jar
: / D: /tools/java8_212/jre/lib/ext/jaccess.jar
fichier: / D: /tools/java8_212/jre/lib/ext/jfxrt.jar
fichier : / D: /tools/java8_212/jre/lib/ext/localedata.jar
fichier: / D: /tools/java8_212/jre/lib/ext/nashorn.jar
fichier: / D: / tools / java8_212 / jre / lib
Fichier /ext/sunec.jar : / D: /tools/java8_212/jre/lib/ext/sunjce_provider.jar
fichier: / D: /tools/java8_212/jre/lib/ext/sunmscapi.jar
fichier: / D: / tools / java8_212 / jre / lib / ext / sunpkcs11.jar
fichier: / D: /tools/java8_212/jre/lib/ext/zipfs.jar
加载 ext 扩展 目录 的 jar

Chargeur de classe d'application

Chargeur de classe qui charge les programmes utilisateur par défaut

Afficher le chemin chargé par le chargeur de classe

 String[] urls = System.getProperty("java.class.path").split(":");

        for (String url : urls) {
    
    
            System.out.println(url);
        }

        System.out.println("================================");

        URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();

        URL[] urls1 = classLoader.getURLs();
        for (URL url : urls1) {
    
    
            System.out.println(url);
        }

Comment stocker la classe chargée par le chargeur de classe

Insérez la description de l'image ici
On peut voir sur la figure ci-dessus que différents chargeurs de classe ouvriront une partie de leur propre espace dans la zone de méthode après avoir chargé la classe dans la zone de méthode. C'est-à-dire, après que différents chargeurs de classe ont chargé la même classe dans la zone de méthode, leur mémoire L'adresse est différente, car l'adresse dans la zone de méthode est différente et le processus de chargement de la JVM est une délégation parentale, pourquoi avez-vous besoin d'une délégation parentale? ? Une partie de la raison est d'empêcher notre même classe d'être chargée par différents chargeurs, elle aura donc plusieurs copies en mémoire, donc l'utilisation du mécanisme de délégation parent peut éviter cette situation.
Lorsque nous voulons obtenir un objet d'une classe, nous devons obtenir la classe de la zone de méthode, qui est le processus de chargement de classe (via une série de processus), puis initialiser l'espace de création dans la zone de tas pour stocker les objets que nous avons créés.

Délégation parentale

Qu'est-ce que la délégation parente? La délégation parente est un mécanisme de JVM lors du chargement de classes. Chaque fois qu'il y a une demande de chargement de classe, elle délègue la demande à sa classe parente et laisse la classe parente terminer le chargement de la classe pour elle-même. Délégation en ligne, jusqu'à ce qu'elle soit déléguée au chargement de la classe de démarrage, si elle n'est pas chargée à la fin, une couche ClassNotFound sera levée; la figure suivante montre la description de la délégation parente:
Insérez la description de l'image ici

Rompre la délégation parentale

Parce que dans certains cas, le chargeur de classe parent doit déléguer au chargeur de classe enfant pour charger le fichier de classe. En raison de la limitation de la plage de chargement, le chargeur de classe parent ne peut pas charger le fichier requis. Prenons l'interface du pilote comme exemple. Comme l'interface du pilote est définie dans jdk, son implémentation est fournie par le fournisseur de services de chaque base de données, tel que mysql. MySQL Connector, puis le problème vient. DriverManager (également fourni par jdk) doit charger chaque classe d'implémentation qui implémente l'interface Driver, puis la gérer, mais DriverManager est chargé par le chargeur de classe de démarrage et ne peut enregistrer les fichiers que sous la lib de JAVA_HOME. L'implémentation est fournie par le fournisseur de services et chargée par le chargeur de classe système. À ce stade, vous devez démarrer le chargeur de classe pour déléguer la sous-classe au chargement de l'implémentation du pilote, ce qui interrompt la délégation parente.

Dans de telles situations, il est nécessaire de rompre la délégation parentale.

Briser la délégation parentale signifie en fait ne pas déléguer, déléguer

Mécanisme SPI

Un autre mécanisme qui rompt la délégation parentale est le mécanisme SPI.
Qu'est-ce que SPI SPI, le nom complet est Service Provider Interface, est un mécanisme de découverte de services

Il recherche les fichiers dans le dossier META-INF / services sous le chemin ClassPath et charge automatiquement les classes définies dans les fichiers.
Ce mécanisme offre la possibilité de nombreuses extensions de framework, comme le mécanisme SPI utilisé dans Dubbo et JDBC. Voyons d'abord comment il est utilisé à travers un exemple très simple:

Je crée d'abord 4 projets:
xxx-core Créer une interface PayService
xxx-dev1 Créer une classe de test
xxx-dev2 Créer une classe de paiement WeChat pour implémenter PayService
xxx-dev3 Créer une classe de paiement Alipay pour implémenter PayService

Comme suit:
xxx-core:
PayService:

public interface PayService {
    
    
    void pay();
}

xxx-dev2:
WxPayService:

public class WxPayService implements PayService {
    
    
    @Override
    public void pay() {
    
    
        System.out.println("Wxpay....");
    }
}

xxx-dev3:
AliPayService

public class AliPayService implements PayService {
    
    
    @Override
    public void pay() {
    
    
        System.out.println("AliPay....");
    }
}

Nous devons également créer un fichier de service sous les ressources des projets dev2 et dev3:
Insérez la description de l'image ici

Où com.xxx. **. PayService est votre classe d'implémentation


Écrivons notre classe de test: xxx-dev1:

public class SpiTest {
    
    

    public static void main(String[] args) {
    
    
        ServiceLoader<PayService> services = ServiceLoader.load(PayService.class);
        services.forEach(item ->{
    
    
            item.pay();
        });
    }
}

La découverte de service est réalisée via le mécanisme SPI ServiceLoader fourni par jdk;
cette opération ne peut rien afficher, mais lorsque nous introduisons xxx-dev2 dans le projet dev1, elle affichera Wxpay ...,
lorsque nous changerons le projet dev1 en dev2, cela Sortie AliPay ...
Si nous introduisons à la fois dev1 et dev2, alors les deux seront trouvés et enregistrés, alors il affichera:
Wxpay ...
AliPay ...

Je suppose que tu aimes

Origine blog.csdn.net/scjava/article/details/108249563
conseillé
Classement