JavaRush /Blog Java /Random-FR /IntelliJ Idea : Décompilation, Compilation, Substitution ...
Viacheslav
Niveau 3

IntelliJ Idea : Décompilation, Compilation, Substitution (ou comment corriger les erreurs des autres)

Publié dans le groupe Random-FR
« Ne réinventez pas la roue » est l’une des principales règles pour un travail réussi et efficace. Mais que faire quand on ne veut pas réinventer sa propre roue, mais que le volant de quelqu'un d'autre s'avère tordu et que les roues sont carrées ? Cette revue est destinée à fournir une introduction aussi brève que possible à la technique de réparation des bibliothèques d'autres personnes « en dernier recours » et à la manière de l'étendre à votre ordinateur.
IntelliJ Idea : Décompilation, Compilation, Substitution (ou comment corriger les erreurs des autres) - 1

Introduction

Nous utilisons tous un outil ou un autre. Mais parfois, les outils ne sont pas totalement adaptés ou comportent des erreurs. Grâce aux fonctionnalités du langage Java, nous pouvons corriger le comportement des outils là où nous en avons besoin. C'est bien quand nous contribuons à des projets et envoyons des pull request (vous pouvez en savoir plus ici : « GitHub - Contribuer aux projets »). Mais il se peut qu’ils ne soient pas acceptés immédiatement, ou même qu’ils ne le soient pas. Mais pour les besoins du projet, c'est nécessaire maintenant. Et ici, j'espère, cet article montrera les outils dont nous disposons en tant que développeurs. Nous devrons effectuer les étapes suivantes dont nous parlerons :
  • Préparer une application de test par exemple (en utilisant l'exemple d'un projet Hibernate)
  • Trouver un emplacement modifiable
  • Faire un changement
  • Déployer le référentiel
Toutes les étapes ci-dessous sont indiquées pour le système d'exploitation Windows, mais ont des analogues pour les systèmes nix. Vous pourrez donc les répéter si nécessaire.

Préparation du sujet

Nous avons donc besoin d'un projet de test. Hibernate est idéal pour nous, car... c'est "élégant, à la mode, moderne". Je n'entrerai pas dans les détails, car... L'article ne concerne pas Hibernate. Nous ferons tout rapidement et précisément. Et nous, en tant que véritables développeurs, utiliserons le système de construction. Par exemple, Gradle nous convient également, qui doit être installé pour cet article ( https://gradle.org/install/ ). Tout d’abord, nous devons créer un projet. Maven a des archétypes pour cela , et Gradle a un plugin spécial pour cela : Gradle Init . Alors, ouvrez la ligne de commande de la manière que vous connaissez. Créez un répertoire pour le projet, accédez-y et exécutez la commande :

mkdir javarush 
cd javarush 
gradle init --type java-application
IntelliJ Idea : Décompilation, Compilation, Substitution (ou comment corriger les erreurs des autres) - 2
Avant d'importer le projet, apportons quelques modifications au fichier qui décrit comment construire. Ce fichier est appelé script de construction et est nommé build.gradle. Il se trouve dans le répertoire dans lequel nous avons exécuté gradle init. Par conséquent, nous l'ouvrons simplement (par exemple, sous Windows avec la commande start build.gradle). On y retrouve le bloc « dépendances », c'est à dire dépendances. Tous les pots tiers que nous utiliserons sont décrits ici. Nous devons maintenant comprendre ce qu'il faut décrire ici. Allons sur le site Web Hibernate ( http://hibernate.org/ ). Nous sommes intéressés par Hibernate ORM . Nous avons besoin de la dernière version. Dans le menu de gauche, il y a une sous-section « Releases ». Sélectionnez « dernière version stable ». Faites défiler vers le bas et recherchez « Implémentation de base (inclut JPA) ». Auparavant, il était nécessaire de connecter le support JPA séparément, mais maintenant tout est devenu plus simple et une seule dépendance suffit. Nous devrons également travailler avec la base de données en utilisant Hibernate. Pour ce faire, prenons l'option la plus simple - Base de données H2 . Le choix est fait, voici nos dépendances :

dependencies {
    // Базовая зависимость для Hibernate (новые версии включают и JPA)
    compile 'org.hibernate:hibernate-core:5.2.17.Final'
    // База данных, к которой мы будем подключаться
    compile 'com.h2database:h2:1.4.197'
    // Use JUnit test framework
    testCompile 'junit:junit:4.12'
}
Super, quelle est la prochaine étape ? Nous devons configurer Hibernate. Hibernate a un " Guide de démarrage ", mais c'est stupide et constitue plus un obstacle qu'une aide. Passons donc directement au « Guide de l’utilisateur » comme les bonnes personnes. Dans la table des matières, nous voyons la section « Bootstrap », qui se traduit par « Bootstrapping ». Juste ce dont vous avez besoin. Il y a beaucoup de mots intelligents qui y sont écrits, mais le fait est qu'il devrait y avoir un répertoire META-INF sur le chemin de classe et qu'il devrait y avoir un fichier persistence.xml. Selon la norme, le classpath contient le répertoire « resources ». Par conséquent, nous créons le répertoire spécifié : mkdir src\main\resources\META-INF créez-y le fichier persistence.xml et ouvrez-le. Dans la documentation, il y a un exemple « Exemple 268. Fichier de configuration META-INF/persistence.xml » dont nous allons prendre le contenu et l'insérer dans le fichier persistence.xml. Ensuite, lancez l'IDE et importez-y notre projet créé. Nous devons maintenant enregistrer quelque chose dans la base de données. C'est ce qu'on appelle une entité. Les entités représentent quelque chose du soi-disant modèle de domaine. Et dans la table des matières, et voilà, nous voyons « 2. Modèle de domaine ». Nous parcourons le texte et voyons dans le chapitre « 2.1. Types de mappage » un exemple simple d'entité. Prenons-le pour nous, en le raccourcissant un peu :
package entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity(name = "Contact")
public class Contact {

    @Id
    @GeneratedValue
    private Integer id;

    private String name;

    public Contact(String name) {
        this.name = name;
    }
}
Nous avons maintenant une classe qui représente une entité. Revenons à persistence.xml et corrigeons-y un endroit : là où cela est indiqué, classnous indiquerons notre classe entity.Contact. Génial, il ne reste plus qu'à se lancer. Revenons au chapitre Bootstrap . Puisque nous n'avons pas de serveur d'applications qui nous fournirait un environnement EE spécial (c'est-à-dire un environnement qui implémente certains comportements du système pour nous), nous travaillons dans un environnement SE. Pour cela, seul l'exemple « Exemple 269. Application bootstrapée EntityManagerFactory » nous convient. Par exemple, faisons ceci :
public class App {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("CRM");
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        Contact contact = new Contact("Vasya");
        em.persist(contact);
        em.getTransaction().commit();
        Query sqlQuery = em.createNativeQuery("select count(*) from contact");
        BigInteger count = (BigInteger) sqlQuery.getSingleResult();
        emf.close();
        System.out.println("Entiries count: " + count);
    }
}
Hourra, notre sujet est prêt. Je ne voulais pas omettre cette partie , parce que... Pour les chapitres suivants, il convient de comprendre comment est né notre sujet.

Trouver un comportement modifiable

Remplaçons l'initialisation du champ count de type BigInteger et y définissons des points d'arrêt ( BreakPoint ). Après avoir inséré la ligne souhaitée, cela peut être fait en utilisant Ctrl+F8 ou via le menu Exécuter -> Basculer le point d'arrêt de ligne. Ensuite, nous exécutons notre méthode principale dans le débogage (Exécuter -> Debug) :
IntelliJ Idea : Décompilation, Compilation, Substitution (ou comment corriger les erreurs des autres) - 3
Un exemple un peu maladroit, mais disons que nous souhaitons modifier le nombre d'espaces de requête au démarrage. Comme nous pouvons le voir, notre sqlQuery est NativeQueryImpl. Cliquez sur Ctrl+N, écrivez le nom de la classe et accédez-y. Pour que lorsque nous allons dans un cours, nous soyons transférés à l'endroit où se trouve ce cours et activons le défilement automatique :
IntelliJ Idea : Décompilation, Compilation, Substitution (ou comment corriger les erreurs des autres) - 4
Notons tout de suite qu'Idea ne sait pas pour l'instant où trouver le code source du programme (c'est-à-dire le code source). Elle a donc gentiment décompilé le contenu du fichier de classe pour nous :
IntelliJ Idea : Décompilation, Compilation, Substitution (ou comment corriger les erreurs des autres) - 5
Notez également que dans le titre de la fenêtre IntelliJ Idea, il est écrit où Gradle enregistre l'artefact pour nous. Maintenant, voyons dans Idea le chemin où se trouve notre artefact :
IntelliJ Idea : Décompilation, Compilation, Substitution (ou comment corriger les erreurs des autres) - 6
Allons dans ce répertoire sur la ligne de commande en utilisant la commande cd way to каталогу. Je fais une remarque tout de suite : s'il est possible de construire un projet à partir des sources, il vaut mieux construire à partir des sources. Par exemple, le code source d'Hibernate est disponible sur le site officiel. Il est préférable de le récupérer pour la version souhaitée, d'y apporter toutes les modifications et de l'assembler à l'aide des scripts de construction spécifiés dans le projet. Je présente dans l'article l'option la plus terrible - il y a un pot, mais pas de code source. Et note numéro 2 : Gradle peut obtenir le code source à l’aide de plugins. Voir Comment télécharger des javadocs et des sources pour Jar à l'aide de Gradle pour plus de détails .

Faire un changement

Nous devons recréer la structure des répertoires en fonction du package dans lequel se trouve la classe que nous modifions. Dans ce cas : mkdir org\hibernate\query\internal, après quoi nous créons un fichier dans ce répertoire NativeQueryImpl.java. Maintenant, nous ouvrons ce fichier et y copions tout le contenu de la classe depuis l'EDI (le même que celui qu'Idea a décompilé pour nous). Modifiez les lignes nécessaires. Par exemple:
IntelliJ Idea : Décompilation, Compilation, Substitution (ou comment corriger les erreurs des autres) - 7
Maintenant, compilons le fichier. Nous faisons: javac org\hibernate\query\internal\NativeQueryImpl.java. Wow, vous ne pouvez pas simplement le prendre et le compiler sans erreurs. Nous avons reçu un certain nombre d'erreurs Impossible de trouver le symbole, car... la classe mutable est liée à d'autres classes, qu'IntelliJ Idea ajoute généralement au chemin de classe pour nous. Ressentez-vous toute l’utilité de nos IDE ? =) Eh bien, ajoutons-le nous-mêmes, nous pouvons le faire aussi. Copions les chemins pour :
  • [1] - mise en veille prolongée-core-5.2.17.Final.jar
  • [2] - mise en veille prolongée-jpa-2.1-api-1.0.0.Final.jar
Tout comme nous l'avons fait : dans la vue « Projet » dans « Bibliothèques externes », nous trouvons le fichier jar requis et cliquons sur Ctrl+Shift+C. Créons et exécutons maintenant la commande suivante : javac -cp [1];[2] org\hibernate\query\internal\NativeQueryImpl.java En conséquence, de nouveaux fichiers de classe apparaîtront à côté du fichier java, qui doivent être mis à jour dans le fichier jar :
IntelliJ Idea : Décompilation, Compilation, Substitution (ou comment corriger les erreurs des autres) - 8
Hourra, vous pouvez maintenant effectuer la mise à jour du pot. Nous pouvons nous laisser guider par les documents officiels : jar uf hibernate-core-5.2.17.Final.jar org\hibernate\query\internal\*.class Open IntelliJ Idea ne vous permettra probablement pas de modifier les fichiers. Par conséquent, avant d'effectuer la mise à jour du pot, vous devrez probablement fermer Idea et, après la mise à jour, l'ouvrir. Après cela, vous pouvez rouvrir l'IDE et réexécuter dubug. Les points d'arrêt ne sont pas réinitialisés entre les redémarrages de l'EDI. Par conséquent, l’exécution du programme s’arrêtera là où elle était auparavant. Voila, nous voyons comment fonctionnent nos changements :
IntelliJ Idea : Décompilation, Compilation, Substitution (ou comment corriger les erreurs des autres) - 9
Super. Mais ici la question se pose : à cause de quoi ? Tout simplement parce que lorsque Gradle construit un projet, il analyse le bloc des dépendances et des référentiels. Gradle dispose d'un certain cache de build, qui est situé à un certain emplacement (voir « Comment définir l'emplacement du cache Gradle ? » S'il n'y a pas de dépendance dans le cache, Gradle le téléchargera à partir du référentiel. Puisque nous avons modifié le fichier jar dans le cache lui-même, alors Gradle pense que la bibliothèque est dans le cache et ne pompe rien. Mais tout effacement du cache entraînera la perte de nos modifications. De plus, personne d'autre que nous ne peut simplement aller les chercher. Quels inconvénients , n'est-ce pas ? Que faire. Hmm, téléchargements depuis le référentiel ? Nous avons donc besoin de notre référentiel, avec des préférences et des poétesses. C'est la prochaine étape.

Déployer le référentiel

Il existe différentes solutions gratuites pour déployer votre référentiel : l'une d'elles est Artifactory et l'autre est Apache Archive . Artifactory a l'air à la mode, élégant, moderne, mais j'ai eu des difficultés avec cela, je ne voulais pas placer correctement les artefacts et j'ai généré des métadonnées Maven erronées. Par conséquent, de manière inattendue pour moi, la version Apache a fonctionné pour moi. Il s'est avéré pas si beau, mais fonctionne de manière fiable. Sur la page de téléchargement , recherchez la version autonome et décompressez-la. Ils ont leur propre « Quick Start ». Après le lancement, vous devez attendre l'adresse http://127.0.0.1:8080/#repositorylist. Après cela, sélectionnez « Télécharger l'artefact » :
IntelliJ Idea : Décompilation, Compilation, Substitution (ou comment corriger les erreurs des autres) - 10
Cliquez sur « Démarrer le téléchargement », puis sur « Enregistrer les fichiers ». Après cela, un message de réussite vert apparaîtra et l'artefact deviendra disponible dans la section « Parcourir ». Cela devrait être fait pour les fichiers jar et pom :
IntelliJ Idea : Décompilation, Compilation, Substitution (ou comment corriger les erreurs des autres) - 11
Cela est dû au fait que des dépendances de mise en veille prolongée supplémentaires sont spécifiées dans le fichier pom. Et il ne nous reste qu'une étape : spécifier le référentiel dans notre script de build :

repositories {
    jcenter()
    maven {
        url "http://127.0.0.1:8080/repository/internal/"
    }
}
Et, en conséquence, la version de notre mise en veille prolongée deviendra : compile 'org.hibernate:hibernate-core:5.2.17.Final-JAVARUSH'. C'est tout, notre projet utilise désormais la version que nous avons corrigée, et non la version originale.

Conclusion

Il semblerait que nous ayons fait connaissance. J'espère que c'était intéressant. De telles « astuces » sont rarement utilisées, mais si soudainement les exigences de votre entreprise établissent des conditions que les bibliothèques que vous utilisez ne peuvent pas satisfaire, vous savez quoi faire. Et oui, voici quelques exemples qui peuvent être corrigés de cette façon :
  • Il existe un serveur Web appelé Undertow. Jusqu'à un certain temps, il y avait un bug qui, lors de l'utilisation d'un proxy, ne nous permettait pas de connaître l'adresse IP de l'utilisateur final.
  • Pour le moment, WildFly JPA a géré d'une certaine manière un moment non pris en compte par la spécification, à cause de cela des exceptions ont été levées. Et ce n'était pas configurable.
#Viacheslav
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION