Pourquoi les gens utilisent-ils le chargement de classe?

Ainsi, chaque manuel Java explique à quel point Java est flexible car il peut charger des classes au moment de l’exécution. Rassemblez simplement une chaîne et donnez-la à Class.forName() , interceptez le ClassNotFoundException et gérez-le. Voilà pour la théorie.

Pouvez-vous donner des exemples de la manière dont vous utilisez le chargement de classes Java pour obtenir une fonctionnalité qui, autrement, n’aurait pas été possible ou facile? Notez que je ne demande pas “quelles bonnes choses pourrions- nous faire?” – Je cherche des exemples concrets, que ce soit une application open-source ou – si vous pouvez décrire cela sans donner trop de détails – une application propriétaire.

Edit: Bien sûr, la VM charge les classes paresseusement comme elle en a besoin. C’est une chose en coulisse tant que je suis sûr que toutes les classes dont j’ai besoin sont là. Comment gérer une ClassNotFoundException ? Supposons que j’ai écrit dix pages de texte et que la classe PrinterDriver soit introuvable.

Les plugins sont la première chose qui vient à l’esprit. Le chargement de classes Java le rend très facile par rapport aux langages comme C ++.

Un point que vous ne connaissez peut-être pas est que toute machine virtuelle Java dépend fortement du chargement de classe en interne. Chaque fois qu’une référence à, disons, une méthode est vue par l’interpréteur de bytecode, elle vérifie si la classe à laquelle appartient la méthode est déjà chargée et, si ce n’est pas le cas, la charge en utilisant le même mécanisme que Class.forName() la méthode. Ce mécanisme est très puissant, car toute application Java agit réellement comme un ensemble de composants remplaçables qui sont tous chargés dynamicment. Si la machine virtuelle est bien écrite, elle peut par exemple charger des classes via un chargeur de classe personnalisé qui extrait les classes du réseau au lieu des fichiers normaux.

Le temps de chargement de la classe dépend de l’implémentation de la machine virtuelle, mais la plupart dépendent de ce mécanisme de liaison tardive qui charge une classe la première fois que la machine virtuelle le rencontre.

Les serveurs d’applications s’appuient également fortement sur ClassLoaders pour isoler les différents modules déployés. Par exemple

  • vous pouvez déployer la même application Web deux fois sous un chemin différent
  • deux applications peuvent dépendre de deux versions différentes de la même bibliothèque sans conflit.

Merci à la magie des chargeurs de classe …

“PLUGIN” et c’est le grand mot.

En gros, vous pouvez charger une classe que vous ne savez pas quand ou n’existe pas lorsque vous écrivez et comstackz votre programme.

Par exemple, si vous souhaitez qu’un programme vérifie l’orthographe, vous pouvez écrire une interface SpellChecker puis charger une classe à partir d’un fichier de configuration qui implémente l’interface SpellChecker . Après cela, vous pouvez écrire n’importe quel SpellChecker et définir dans le fichier de configuration le nom de fichier actuel. De cette façon, votre programme n’a pas besoin de savoir quelle classe effectuera la vérification orthographique.

Le pilote de firebase database, le plug-in Eclipse, le langage de script, les méthodes de cryptographie sont tous faits de cette manière, car l’auteur original ne sait pas (et dans certains cas, n’a aucune idée) quelle classe sera réellement utilisée.

J’espère que cela t’aides.

Eh bien, je l’ai utilisé pour charger dynamicment des pilotes JDBC dans une application J2EE. Quoi qu’il en soit, cela aurait pu être mieux fait, je n’en ai aucune idée.

C’était juste plus facile à l’époque de faire l’appel à forName() .

Je suis sûr que le chargement de plug-in en Java dépend beaucoup de cela.

L’application vérifie les fonctions nommées et les exécute

Eclipse utilise cela pour les plugins

L’idée clé est d’exécuter du code qui n’était pas prévu au moment du développement.

Cela peut être extrêmement utile lorsque vous utilisez une API et que les concepteurs de l’API ont déconseillé certaines classes d’une version à l’autre (par exemple, les contacts dans Android).

Sans reflection et chargement de classe dynamic basé sur le nom de la chaîne, il serait impossible dans ce cas d’exécuter le même programme sur les deux versions de la plate-forme sans obtenir d’exception de classe non trouvée au moment de l’exécution. Mais avec cela, le même programme a été légèrement modifié et peut ensuite être exécuté sur les deux plates-formes.

Le ClassLoader est également utilisé pour les ressources non classées. Les fichiers de configuration viennent à l’esprit. Comme il y a un ordre de recherche bien défini, il est facile de déposer vos propres “log4j.xml” ou “hibernate.properties”, et l’application les trouvera et les utilisera.

Je me souviens d’avoir créé un chargeur de classe pour charger des classes à distance. L’application s’exécutait sur un nœud pendant que les classes étaient stockées sur un autre nœud.

Et également en personnalisant le chargeur de classes, vous pouvez transformer les classes au fur et à mesure de leur chargement. Ceci est utilisé par certains frameworks ORM ainsi que par certains frameworks AOP.

L’API JDBC en est un excellent exemple. De cette façon, vous pouvez configurer le pilote JDBC en externe, par exemple dans un fichier de propriétés:

 driver = com.dbvendor.jdbc.Driver
 url = jdbc: dbvendor: // localhost / nom_bd
 nom d'utilisateur = stackoverflow
 mot de passe = youneverguess

..qui vous pouvez utiliser comme:

 Properties properties = new Properties(); properties.load(Thread.currentThread().getResourceAsStream("jdbc.properties")); Ssortingng driver = properties.getProperty("driver"); Ssortingng url = properties.getProperty("url"); Ssortingng username = properties.getProperty("username"); Ssortingng password = properties.getProperty("password"); Class.forName(driver); Connection connection = DriverManager.getConnection(url, username, password); 

Chaque implémentation de pilote JDBC s’enregistre dans le DriverManager intérieur d’un bloc d’initialisation static . C’est à savoir celui qui est exécuté pendant la Class#forName() .

 package com.dbvendor.jdbc; public class Driver implements java.sql.Driver { static { java.sql.DriverManager.registerDriver(new Driver()); } private Driver() { // ... } public boolean acceptsURL(Ssortingng url) { return url.startsWith("jdbc:dbvendor"); } } 

Étant donné que le DriverManager ressemble à peu près à ceci (il utilise en fait le Vector ancienne)

 private static final Set drivers = new HashSet(); public static void registerDriver(Driver driver) { drivers.add(driver); } public static Connection getConnection(Ssortingng url, Ssortingng username, Ssortingng password) throws SQLException { for (Driver driver : drivers) { if (driver.acceptsURL(url)) { return driver.connect(url, username, password); } } throw new SQLException("No suitable driver"); } 

… vous pouvez obtenir une connexion sans avoir à instancier le pilote lui-même!

De cette façon, le code JDBC est hautement portable. Vous pouvez modifier la firebase database ou dissortingbuer le code parmi les utilisateurs ayant des bases de données différentes sans avoir à modifier / pirater / reconstruire le code lui-même.

Ce n’est pas seulement JDBC qui utilise cette approche, mais d’autres API telles que l’API Servlet, les ORM comme Hibernate / JPA, les structures d’dependency injection, etc. utilisent la reflection pour charger les classes basées sur des fichiers de propriétés, des fichiers de configuration XML et des annotations Tout cela rend le code beaucoup plus portable et connectable.

Je pense que JUnit peut également utiliser beaucoup de fonctionnalités de reflection pour rendre le cadre de test générique.

Le mécanisme de chargement de classe Java est puissant car il fournit un point d’abstraction exactement au point où le code est chargé, ce qui vous permet de faire des choses telles que:

  • trouver les bits de classe ailleurs que dans le classpath (db, url distant, système de fichiers, etc.)
  • chargement du code source que vous venez de créer et de comstackr vous-même (avec l’api javac)
  • chargement du code d’octet que vous venez de générer (par exemple avec ASM)
  • charger le code et le MODIFIER avant de l’utiliser (avec ASM, agents Java, etc.)
  • Rechargez le code à la volée
  • des chargeurs de chaînes dans des arbres (délégation normale) ou des webs (style OSGi basé sur la frasortinge) ou ce que vous voulez

Sur le sharepoint modifier le code pendant le chargement, vous pouvez faire un tas de choses intéressantes pour remixer votre code: AOP, profilage, traçage, modifications de comportement, etc. Chez Terracotta, nous avons utilisé l’abstraction de classloader pour charger dynamicment une classe. intercepter tous les access aux champs et append dynamicment la possibilité de charger ultérieurement l’état du même object sur un nœud distant du cluster. Truc cool.

Je l’utilise lorsque je crée une application standard qui doit être adaptée par moi-même ou par le client pour répondre aux exigences spécifiques du client.

Consultez le support pour la gestion Oracle LOB dans Spring Framework par exemple. Ce n’est que parce que le framework offre un support spécifique pour Oracle que vous ne souhaitez probablement pas déployer une source de données Oracle en tant que dépendance pour vos projets MySQL par exemple. Par conséquent, vous chargez les pilotes Oracle de manière réfléchie dans la scope d’une instance du gestionnaire LOB.

Les conteneurs de servlets tels que Tomcat lisent votre fichier de configuration war / webapp à partir de WEB-INF / web.xml et chargent votre Servlet / Filter / etc. des sous-classes basées sur les valeurs Ssortingng que vous avez insérées dans le fichier XML. Pour les connexions à la firebase database, ils extraient le nom de la classe à charger depuis votre configuration, par exemple “com.mysql.jdbc.Driver” pour MySQL.

Exemple concret (comme demandé dans votre question), application propriétaire (explicitement autorisée par votre question) …

Au démarrage, le logiciel côté client contacte nos serveurs et dit: «L’implémentation par défaut de la barre d’interface que j’ai est Foo (parce que toutes les versions 1.03, par exemple, utilisent Foo), en avez-vous une meilleure? Si en attendant nous écrivions une meilleure implémentation, nous répondons “Ouais, Bar est vieux, utilisez Buz, c’est mieux”.

Ensuite, du côté du client, un chargeur de classe est utilisé pour charger la dernière implémentation.

C’est simplifié, mais c’est un exemple du monde réel. Ce n’est pas tout à fait différent de l’exemple cité par JRL: les classes obsolètes sont automatiquement remplacées par des classes plus récentes.

L’utilisation du chargement de classe dynamic est également très utile pour charger des fichiers de configuration comme le mentionne Thilo. Plus généralement, le chargement dynamic de classes peut constituer une couche d’abstraction de système de fichiers agréable dans de nombreuses situations, simplifiant ainsi l’écriture des préférences et le code sensible à la configuration. Assurez-vous simplement que la ressource dont vous avez besoin se trouve sur le chemin de classe et chargez-la en tant que InputStream.

De plus, avec un gestionnaire de protocole personnalisé en Java, il est possible d’accéder aux éléments du chemin de classe via une URL. Ce n’est pas un avantage spécifique au chargement de classe dynamic, mais il montre comment accéder aux ressources de chemin de classe via la même API que les autres ressources (même distantes). http://java.sun.com/developer/onlineTraining/protocolhandlers/

Tout framework basé sur la configuration (struts, jsf, spring, hibernate, etc.) utilise ce mécanisme. Tout produit basé sur l’architecture de plug-in utilise également cette fonctionnalité.

Vous pouvez utiliser la méthode Class :: forName si la classe est dans le chemin de la classe. Cependant, si vous avez besoin de donner un chemin avec le nom de la classe, à savoir c: \ document \ xyz.class, vous devrez utiliser la classe URLClassLoader.