portaldacalheta.pt
  • Principal
  • Science Des Données Et Bases De Données
  • Conception Mobile
  • Design De Marque
  • Personnes Et Équipes Produit
Interface Web

Didacticiel de classe Java avancé: un guide sur le rechargement de classe



Dans Projets de développement Java , un flux de travail typique implique le redémarrage du serveur à chaque changement de classe, et personne ne s'en plaint. C'est un fait sur le développement Java. Nous avons travaillé comme ça depuis notre premier jour avec Java. Mais le rechargement des classes Java est-il si difficile à réaliser? Et ce problème pourrait-il être à la fois difficile et passionnant à résoudre développeurs Java qualifiés ? Dans ce didacticiel de classe Java, j'essaierai de résoudre le problème, de vous aider à profiter de tous les avantages du rechargement de classe à la volée et d'augmenter considérablement votre productivité.

Le rechargement des classes Java n'est pas souvent abordé et il existe très peu de documentation explorant ce processus. Je suis ici pour changer cela. Ce tutoriel sur les classes Java fournira une explication étape par étape de ce processus et vous aidera à maîtriser cette technique incroyable. Gardez à l'esprit que la mise en œuvre du rechargement de classe Java nécessite beaucoup de soin, mais apprendre à le faire vous placera dans la cour des grands, à la fois en tant que développeur Java et en tant qu'architecte logiciel. Cela ne fera pas de mal non plus de comprendre comment éviter les 10 erreurs Java les plus courantes .



Configuration de l'espace de travail

Tout le code source de ce didacticiel est téléchargé sur GitHub Ici .



Pour exécuter le code pendant que vous suivez ce tutoriel, vous aurez besoin Maven , Aller et soit Éclipse ou IDÉE IntelliJ .



Si vous utilisez Eclipse:

  • Exécutez la commande mvn eclipse:eclipse pour générer les fichiers de projet d'Eclipse.
  • Chargez le projet généré.
  • Définissez le chemin de sortie sur target/classes.

Si vous utilisez IntelliJ:

  • Importez le projet pom fichier.
  • IntelliJ ne se compilera pas automatiquement lorsque vous exécutez un exemple, vous devez donc soit:
  • Exécutez les exemples dans IntelliJ, puis chaque fois que vous souhaitez compiler, vous devrez appuyer sur Alt+B E
  • Exécutez les exemples en dehors d'IntelliJ avec le run_example*.bat. Définissez la fonction de compilation automatique du compilateur IntelliJ sur true. Ensuite, chaque fois que vous modifiez un fichier java, IntelliJ le compilera automatiquement.

Exemple 1: rechargement d'une classe avec Java Class Loader

Le premier exemple vous donnera une compréhension générale du chargeur de classe Java. Voici le code source.

Compte tenu de ce qui suit User définition de classe:



public static class User { public static int age = 10; }

Nous pouvons faire ce qui suit:

public static void main(String[] args) { Class userClass1 = User.class; Class userClass2 = new DynamicClassLoader('target/classes') .load('qj.blog.classreloading.example1.StaticInt$User'); ...

Dans cet exemple de didacticiel, il y aura deux User classes chargées dans la mémoire. userClass1 sera chargé par le chargeur de classe par défaut de la JVM, et userClass2 en utilisant le DynamicClassLoader, un chargeur de classe personnalisé dont le code source est également fourni dans le projet GitHub, et que je décrirai en détail ci-dessous.



Voici le reste des main méthode:

out.println('Seems to be the same class:'); out.println(userClass1.getName()); out.println(userClass2.getName()); out.println(); out.println('But why there are 2 different class loaders:'); out.println(userClass1.getClassLoader()); out.println(userClass2.getClassLoader()); out.println(); User.age = 11; out.println('And different age values:'); out.println((int) ReflectUtil.getStaticFieldValue('age', userClass1)); out.println((int) ReflectUtil.getStaticFieldValue('age', userClass2)); }

Et la sortie:



Seems to be the same class: qj.blog.classreloading.example1.StaticInt$User qj.blog.classreloading.example1.StaticInt$User But why there are 2 different class loaders: [email protected] [email protected] And different age values: 11 10

Comme vous pouvez le voir ici, bien que le User les classes ont le même nom, ce sont en fait deux classes différentes, et elles peuvent être gérées et manipulées indépendamment. La valeur d'âge, bien que déclarée comme statique, existe en deux versions, attachées séparément à chaque classe, et peut également être modifiée indépendamment.

Dans un programme Java normal, ClassLoader est le portail qui introduit les classes dans la JVM. Lorsqu'une classe nécessite le chargement d'une autre classe, c'est la tâche de ClassLoader de faire le chargement.



Cependant, dans cet exemple de classe Java, le paramètre personnalisé ClassLoader nommé DynamicClassLoader est utilisé pour charger la deuxième version de User classe. Si au lieu de DynamicClassLoader, nous devions utiliser à nouveau le chargeur de classe par défaut (avec la commande StaticInt.class.getClassLoader()), alors le même User class sera utilisée, car toutes les classes chargées sont mises en cache.

Examiner le fonctionnement du ClassLoader Java par défaut par rapport à DynamicClassLoader est essentiel pour profiter de ce didacticiel sur les classes Java.



Le DynamicClassLoader

Il peut y avoir plusieurs chargeurs de classe dans un programme Java normal. Celui qui charge votre classe principale, ClassLoader, est celui par défaut, et à partir de votre code, vous pouvez créer et utiliser autant de chargeurs de classe que vous le souhaitez. C'est donc la clé du rechargement de classe en Java. Le DynamicClassLoader est probablement la partie la plus importante de tout ce didacticiel, nous devons donc comprendre le fonctionnement du chargement dynamique des classes avant de pouvoir atteindre notre objectif.

Contrairement au comportement par défaut de ClassLoader, notre DynamicClassLoader hérite d'une stratégie plus agressive. Un chargeur de classe normal donnerait à son parent ClassLoader la priorité et ne charge que les classes que son parent ne peut pas charger. Cela convient aux circonstances normales, mais pas dans notre cas. Au lieu de cela, le DynamicClassLoader essaiera de parcourir tous ses chemins de classe et de résoudre la classe cible avant qu'elle n'abandonne le droit à son parent.

Dans notre exemple ci-dessus, le DynamicClassLoader est créé avec un seul chemin de classe: 'target/classes' (dans notre répertoire actuel), il est donc capable de charger toutes les classes qui résident à cet emplacement. Pour toutes les classes absentes, il devra faire référence au chargeur de classe parent. Par exemple, nous devons charger le String classe dans notre StaticInt classe, et notre chargeur de classe n'a pas accès à rt.jar dans notre dossier JRE, donc le String la classe du chargeur de classe parent sera utilisée.

Le code suivant provient de AggressiveClassLoader , la classe parente de DynamicClassLoader, et montre où ce comportement est défini.

byte[] newClassData = loadNewClass(name); if (newClassData != null) { loadedClasses.add(name); return loadClass(newClassData, name); } else { unavaiClasses.add(name); return parent.loadClass(name); }

Prenez note des propriétés suivantes de DynamicClassLoader:

  • Les classes chargées ont les mêmes performances et autres attributs que les autres classes chargées par le chargeur de classe par défaut.
  • Le DynamicClassLoader peuvent être récupérés avec toutes ses classes et objets chargés.

Avec la possibilité de charger et d'utiliser deux versions de la même classe, nous pensons maintenant à vider l'ancienne version et à charger la nouvelle pour la remplacer. Dans l'exemple suivant, nous ferons exactement cela… en continu.

Exemple 2: recharger une classe en continu

Cet exemple Java suivant vous montrera que le JRE peut charger et recharger des classes pour toujours, avec d'anciennes classes vidées et récupérées, et une toute nouvelle classe chargée depuis le disque dur et utilisée. Voici le code source.

Voici la boucle principale:

public static void main(String[] args) { for (;;) { Class userClass = new DynamicClassLoader('target/classes') .load('qj.blog.classreloading.example2.ReloadingContinuously$User'); ReflectUtil.invokeStatic('hobby', userClass); ThreadUtil.sleep(2000); } }

Toutes les deux secondes, l'ancien User classe sera sauvegardée, une nouvelle sera chargée et sa méthode hobby invoqué.

Voici le User définition de classe:

@SuppressWarnings('UnusedDeclaration') public static class User { public static void hobby() { playFootball(); // will comment during runtime // playBasketball(); // will uncomment during runtime } // will comment during runtime public static void playFootball() { System.out.println('Play Football'); } // will uncomment during runtime // public static void playBasketball() { // System.out.println('Play Basketball'); // } }

Lors de l'exécution de cette application, vous devez essayer de commenter et de décommenter le code indiqué dans le champ User classe. Vous verrez que la définition la plus récente sera toujours utilisée.

faut-il apprendre le c avant le c++

Voici un exemple de sortie:

... Play Football Play Football Play Football Play Basketball Play Basketball Play Basketball

Chaque fois qu'une nouvelle instance de DynamicClassLoader est créé, il chargera le User classe de target/classes dossier, où nous avons défini Eclipse ou IntelliJ pour générer le dernier fichier de classe. Tous les anciens DynamicClassLoader s et les anciens User les classes seront dissociées et soumises au ramasse-miettes.

Il est essentiel que les développeurs Java avancés comprennent le rechargement dynamique des classes, qu

Si vous êtes familier avec JVM HotSpot, il convient de noter ici que la structure de classe peut également être modifiée et rechargée: le playFootball doit être supprimée et le playBasketball méthode ajoutée. Ceci est différent de HotSpot, qui permet uniquement de modifier le contenu de la méthode, ou la classe ne peut pas être rechargée.

Maintenant que nous sommes capables de recharger une classe, il est temps d'essayer de recharger plusieurs classes à la fois. Essayons-le dans l'exemple suivant.

Exemple 3: rechargement de plusieurs classes

La sortie de cet exemple sera la même que celle de l'exemple 2, mais montrera comment implémenter ce comportement dans une structure plus semblable à une application avec des objets de contexte, de service et de modèle. Le code source de cet exemple est assez volumineux, je n’en ai donc montré que des parties ici. Le code source complet est Ici .

Voici le main méthode:

public static void main(String[] args) { for (;;) { Object context = createContext(); invokeHobbyService(context); ThreadUtil.sleep(2000); } }

Et la méthode createContext:

private static Object createContext() { Class contextClass = new DynamicClassLoader('target/classes') .load('qj.blog.classreloading.example3.ContextReloading$Context'); Object context = newInstance(contextClass); invoke('init', context); return context; }

La méthode invokeHobbyService:

private static void invokeHobbyService(Object context) { Object hobbyService = getFieldValue('hobbyService', context); invoke('hobby', hobbyService); }

Et voici le Context classe:

public static class Context { public HobbyService hobbyService = new HobbyService(); public void init() { // Init your services here hobbyService.user = new User(); } }

Et le HobbyService classe:

public static class HobbyService { public User user; public void hobby() { user.hobby(); } }

Le Context classe dans cet exemple est beaucoup plus compliquée que la classe User classe dans les exemples précédents: elle a des liens vers d'autres classes, et elle a le init méthode à appeler chaque fois qu'elle est instanciée. Fondamentalement, il est très similaire aux classes de contexte d'application du monde réel (qui garde la trace des modules de l'application et effectue l'injection de dépendances). Donc pouvoir recharger ceci Context la classe avec toutes ses classes liées est un grand pas vers l’application de cette technique à la vie réelle.

Le rechargement des classes Java est difficile, même pour les ingénieurs Java avancés.

Au fur et à mesure que le nombre de classes et d'objets augmente, notre étape de «supprimer les anciennes versions» deviendra également plus compliquée. C'est aussi la principale raison pour laquelle le rechargement de classe est si difficile. Pour éventuellement supprimer d'anciennes versions, nous devrons nous assurer qu'une fois le nouveau contexte créé, tout les références aux anciennes classes et objets sont supprimées. Comment gérer cela avec élégance?

Le main ici aura une prise sur l'objet de contexte, et c'est le seul lien à toutes les choses qui doivent être abandonnées. Si nous rompons ce lien, l'objet de contexte et la classe de contexte, et l'objet de service… seront tous soumis au ramasse-miettes.

Une petite explication sur la raison pour laquelle normalement les classes sont si persistantes et ne sont pas ramassées:

  • Normalement, nous chargeons toutes nos classes dans le chargeur de classe Java par défaut.
  • La relation classe-chargeur de classe est une relation bidirectionnelle, le chargeur de classe mettant également en cache toutes les classes qu'il a chargées.
  • Ainsi, tant que le chargeur de classe est toujours connecté à un thread en direct, tout (toutes les classes chargées) sera immunisé contre le ramasse-miettes.
  • Cela dit, à moins que nous ne puissions séparer le code que nous voulons recharger du code déjà chargé par le chargeur de classe par défaut, nos nouvelles modifications de code ne seront jamais appliquées pendant l'exécution.

Avec cet exemple, nous voyons que recharger toutes les classes de l’application est en fait assez simple. Le but est simplement de conserver une connexion mince et supprimable entre le thread en direct et le chargeur de classe dynamique utilisé. Mais que se passe-t-il si nous voulons que certains objets (et leurs classes) ne pas être rechargée et réutilisée entre les cycles de rechargement? Regardons l'exemple suivant.

Exemple 4: Séparation des espaces de classe persistants et rechargés

Voici le code source. .

Le main méthode:

public static void main(String[] args) { ConnectionPool pool = new ConnectionPool(); for (;;) { Object context = createContext(pool); invokeService(context); ThreadUtil.sleep(2000); } }

Vous pouvez donc voir que le truc ici est de charger le ConnectionPool classe et l'instanciant en dehors du cycle de rechargement, en le gardant dans l'espace persistant et en passant la référence à Context objets

Le createContext La méthode est également un peu différente:

private static Object createContext(ConnectionPool pool) { ExceptingClassLoader classLoader = new ExceptingClassLoader( (className) -> className.contains('.crossing.'), 'target/classes'); Class contextClass = classLoader.load('qj.blog.classreloading.example4.reloadable.Context'); Object context = newInstance(contextClass); setFieldValue(pool, 'pool', context); invoke('init', context); return context; }

Désormais, nous appellerons les objets et les classes rechargés à chaque cycle «l'espace rechargeable» et autres - les objets et classes non recyclés et non renouvelés lors des cycles de rechargement - «l'espace persistant». Nous devrons être très clairs sur quels objets ou classes restent dans quel espace, dessinant ainsi une ligne de séparation entre ces deux espaces.

À moins d

Comme on le voit sur l'image, non seulement les Context objet et le UserService objet faisant référence au ConnectionPool objet, mais le Context et UserService Les classes font également référence à ConnectionPool classe. Il s'agit d'une situation très dangereuse qui conduit souvent à la confusion et à l'échec. Le ConnectionPool classe ne doit pas être chargée par notre DynamicClassLoader, il ne doit y en avoir qu'un seul ConnectionPool classe dans la mémoire, qui est celle chargée par défaut ClassLoader. C'est un exemple de la raison pour laquelle il est si important d'être prudent lors de la conception d'une architecture de rechargement de classe en Java.

Et si notre DynamicClassLoader charge accidentellement le ConnectionPool classe? Puis le ConnectionPool objet de l'espace persistant ne peut pas être passé à Context objet, car le Context object attend un objet d'une classe différente, qui est également nommé ConnectionPool, mais est en fait une classe différente!

Alors, comment pouvons-nous empêcher notre DynamicClassLoader depuis le chargement du ConnectionPool classe? Au lieu d'utiliser DynamicClassLoader, cet exemple utilise une sous-classe de celui-ci nommée: ExceptingClassLoader, qui transmettra le chargement au super chargeur de classe en fonction d'une fonction de condition:

(className) -> className.contains('$Connection')

Si nous n’utilisons pas ExceptingClassLoader ici, puis le DynamicClassLoader chargerait le ConnectionPool classe car cette classe réside dans le 'target/classes' dossier. Une autre façon d'éviter les ConnectionPool cours repris par notre DynamicClassLoader est de compiler le ConnectionPool class dans un dossier différent, peut-être dans un module différent, et il sera compilé séparément.

Règles de choix de l'espace

Maintenant, le travail de chargement de classe Java devient vraiment déroutant. Comment déterminer quelles classes doivent être dans l'espace persistant et quelles classes dans l'espace rechargeable? Voici les règles:

  1. Une classe dans l'espace rechargeable peut référencer une classe dans l'espace persistant, mais une classe dans l'espace persistant peut jamais référencer une classe dans l'espace rechargeable. Dans l'exemple précédent, l'élément rechargeable Context classe fait référence à la persistance ConnectionPool classe, mais ConnectionPool n'a pas de référence à Context
  2. Une classe peut exister dans l'un ou l'autre espace si elle ne fait référence à aucune classe dans l'autre espace. Par exemple, une classe utilitaire avec toutes les méthodes statiques comme StringUtils peut être chargé une fois dans l'espace persistant et chargé séparément dans l'espace rechargeable.

Vous voyez donc que les règles ne sont pas très restrictives. À l'exception des classes de croisement qui ont des objets référencés dans les deux espaces, toutes les autres classes peuvent être librement utilisées dans l'espace persistant ou dans l'espace rechargeable ou les deux. Bien sûr, seules les classes de l'espace rechargeable apprécieront d'être rechargées avec des cycles de rechargement.

Le problème le plus difficile du rechargement de classe est donc résolu. Dans l'exemple suivant, nous essaierons d'appliquer cette technique à une simple application Web et apprécierons de recharger les classes Java comme n'importe quel langage de script.

Exemple 5: Petit annuaire téléphonique

Voici le code source. .

Cet exemple sera très similaire à ce à quoi devrait ressembler une application Web normale. Il s'agit d'une application à page unique avec AngularJS, SQLite, Maven et Serveur Web intégré Jetty .

Voici l’espace rechargeable dans la structure du serveur Web:

Une compréhension approfondie de l

Le serveur Web ne contiendra pas de références aux véritables servlets, qui doivent rester dans l'espace rechargeable, afin d'être rechargées. Ce qu'il contient, ce sont des servlets stub, qui, à chaque appel à sa méthode de service, résoudront le servlet réel dans le contexte réel à exécuter.

Cet exemple présente également un nouvel objet ReloadingWebContext, qui fournit au serveur Web toutes les valeurs comme un contexte normal, mais contient en interne des références à un objet de contexte réel qui peut être rechargé par un DynamicClassLoader. C'est ça ReloadingWebContext qui fournissent des servlets de stub au serveur Web.

ReloadingWebContext gère les servlets stub vers le serveur Web dans le processus de rechargement de la classe Java.

Le ReloadingWebContext sera le wrapper du contexte réel, et:

  • Rechargera le contexte réel lorsqu'un HTTP GET vers «/» est appelé.
  • Fournira des servlets stub au serveur Web.
  • Définira des valeurs et invoquera des méthodes chaque fois que le contexte réel est initialisé ou détruit.
  • Peut être configuré pour recharger le contexte ou non, et quel chargeur de classe est utilisé pour le rechargement. Cela vous aidera lors de l'exécution de l'application en production.

Comme il est très important de comprendre comment nous isolons l'espace persistant et l'espace rechargeable, voici les deux classes qui se croisent entre les deux espaces:

comment effectuer un test d'utilisabilité

Classe qj.util.funct.F0 pour objet public F0 connF dans Context

  • Objet Function, retournera une connexion chaque fois que la fonction est appelée. Cette classe réside dans le package qj.util, qui est exclu de DynamicClassLoader.

Classe java.sql.Connection pour objet public F0 connF dans Context

  • Objet de connexion SQL normal. Cette classe ne réside pas dans notre chemin de classe DynamicClassLoader, elle ne sera donc pas récupérée.

Sommaire

Dans ce didacticiel sur les classes Java, nous avons vu comment recharger une seule classe, recharger une seule classe en continu, recharger un espace entier de plusieurs classes et recharger plusieurs classes séparément des classes qui doivent être persistantes. Avec ces outils, le facteur clé pour obtenir un rechargement de classe fiable est d'avoir un design super propre. Ensuite, vous pouvez manipuler librement vos classes et l'ensemble de la JVM.

L'implémentation du rechargement de classe Java n'est pas la chose la plus simple au monde. Mais si vous essayez, et à un moment donné, vous trouvez que vos classes sont chargées à la volée, alors vous y êtes presque déjà. Il vous restera très peu à faire avant de pouvoir réaliser une conception parfaitement propre pour votre système.

Bonne chance mes amis et profitez de votre nouvelle superpuissance!

Réduire les coûts dans un avenir pétrolier et gazier numérique

Processus Financiers

Réduire les coûts dans un avenir pétrolier et gazier numérique
Conception de l'interface utilisateur TV: utilisation de l'espace blanc

Conception de l'interface utilisateur TV: utilisation de l'espace blanc

Conception De L'interface Utilisateur

Articles Populaires
Explorer la fonctionnalité Get & Transform d'Excel
Explorer la fonctionnalité Get & Transform d'Excel
Comment aborder les wrappers pour les propriétés Swift
Comment aborder les wrappers pour les propriétés Swift
ApeeScape s'associe à Guidant Global pour offrir un accès à la demande au réseau Elite de pigistes
ApeeScape s'associe à Guidant Global pour offrir un accès à la demande au réseau Elite de pigistes
Tutoriel Apache Spark Streaming: Identifier les hashtags Twitter tendances
Tutoriel Apache Spark Streaming: Identifier les hashtags Twitter tendances
Conception accessible vs conception inclusive (avec infographie)
Conception accessible vs conception inclusive (avec infographie)
 
L'intégration continue d'iOS avec le serveur Xcode expliquée
L'intégration continue d'iOS avec le serveur Xcode expliquée
Meilleurs éditeurs de programmation? Une bataille sans fin sans vainqueur clair
Meilleurs éditeurs de programmation? Une bataille sans fin sans vainqueur clair
Comment GWT déverrouille la réalité augmentée dans votre navigateur
Comment GWT déverrouille la réalité augmentée dans votre navigateur
Webpack ou Browserify & Gulp: quel est le meilleur?
Webpack ou Browserify & Gulp: quel est le meilleur?
Business Analyst - Stratégie & Analytics
Business Analyst - Stratégie & Analytics
Articles Populaires
  • s corp et c corp
  • se couvrir contre le risque de change
  • valeur nette d'elon musc 2010
  • que puis-je faire avec ruby ​​sur rails
  • la technologie pendant la guerre froide
  • l'expérience est impossible à mesurer parce que c'est un sentiment.
  • est .net open source
Catégories
  • Science Des Données Et Bases De Données
  • Conception Mobile
  • Design De Marque
  • Personnes Et Équipes Produit
  • © 2022 | Tous Les Droits Sont Réservés

    portaldacalheta.pt