JavaRush /Java-Blog /Random-DE /IntelliJ-Idee: Dekompilierung, Kompilierung, Substitution...
Viacheslav
Level 3

IntelliJ-Idee: Dekompilierung, Kompilierung, Substitution (oder wie man die Fehler anderer Leute korrigiert)

Veröffentlicht in der Gruppe Random-DE
„Das Rad nicht neu erfinden“ ist eine der Grundregeln für erfolgreiches und effizientes Arbeiten. Aber was tun, wenn man sein eigenes Rad nicht neu erfinden möchte, sich aber herausstellt, dass das Lenkrad eines anderen schief ist und die Räder eckig sind? Diese Rezension soll eine möglichst kurze Einführung in die Technik geben, die Bibliotheken anderer Leute „als letzten Ausweg“ zu reparieren und wie man diese Technik auf Ihren Computer ausweitet.
IntelliJ-Idee: Dekompilierung, Kompilierung, Substitution (oder wie man die Fehler anderer Leute korrigiert) – 1

Einführung

Wir alle nutzen das eine oder andere Tool. Aber manchmal sind die Tools nicht vollständig geeignet oder weisen Fehler auf. Dank der Funktionen der Java-Sprache können wir das Verhalten von Tools dort korrigieren, wo wir es benötigen. Es ist gut, wenn wir zu Projekten beitragen und Pull-Requests senden (mehr dazu lesen Sie hier: „ GitHub – Zu Projekten beitragen “). Aber sie werden möglicherweise nicht sofort oder gar nicht akzeptiert. Aber für die Bedürfnisse des Projekts ist es jetzt notwendig. Und hier hoffe ich, dass dieser Artikel die Tools zeigt, die uns als Entwickler zur Verfügung stehen. Wir müssen die folgenden Schritte ausführen, über die wir sprechen werden:
  • Bereiten Sie beispielsweise eine Testanwendung vor (am Beispiel eines Hibernate-Projekts)
  • Einen veränderlichen Standort finden
  • Eine Veränderung bewirken
  • Bereitstellen des Repositorys
Alle folgenden Schritte gelten für Windows-Betriebssysteme, es gibt jedoch Analogien für Nix-Systeme. Sie können sie also bei Bedarf wiederholen.

Fachvorbereitung

Wir brauchen also ein Testprojekt. Der Ruhezustand ist für uns ideal, weil... es ist „stilvoll, modisch, modern“. Ich werde nicht zu sehr ins Detail gehen, weil... In dem Artikel geht es nicht um Hibernate. Wir erledigen alles schnell und auf den Punkt. Und wir werden, wie richtige Entwickler, das Build-System verwenden. Für uns eignet sich beispielsweise auch Gradle, das für diesen Artikel installiert werden muss ( https://gradle.org/install/ ). Zuerst müssen wir ein Projekt erstellen. Maven hat dafür Archetypen und Gradle hat dafür ein spezielles Plugin: Gradle Init . Öffnen Sie also die Befehlszeile auf eine Ihnen bekannte Weise. Erstellen Sie ein Verzeichnis für das Projekt, gehen Sie dorthin und führen Sie den Befehl aus:

mkdir javarush 
cd javarush 
gradle init --type java-application
IntelliJ-Idee: Dekompilierung, Kompilierung, Substitution (oder wie man die Fehler anderer Leute korrigiert) – 2
Bevor wir das Projekt importieren, nehmen wir einige Änderungen an der Datei vor, die die Erstellung beschreibt. Diese Datei wird als Build-Skript bezeichnet und trägt den Namen build.gradle. Es befindet sich in dem Verzeichnis, in dem wir gradle init ausgeführt haben. Deshalb öffnen wir es einfach (zum Beispiel in Windows mit dem Befehl start build.gradle). Dort finden wir den Block „ Abhängigkeiten “, d.h. Abhängigkeiten. Alle von uns verwendeten Jars von Drittanbietern werden hier beschrieben. Jetzt müssen wir verstehen, was wir hier beschreiben sollen. Gehen wir zur Hibernate-Website ( http://hibernate.org/ ). Wir sind an Hibernate ORM interessiert . Wir brauchen die neueste Version. Im Menü links gibt es einen Unterpunkt „Releases“. Wählen Sie „Neueste stabile Version“. Scrollen Sie nach unten und suchen Sie nach „Kernimplementierung (einschließlich JPA)“. Früher war es notwendig, die JPA-Unterstützung separat anzubinden, aber jetzt ist alles einfacher geworden und nur eine Abhängigkeit reicht aus. Wir müssen auch mit der Datenbank im Ruhezustand arbeiten. Nehmen wir dazu die einfachste Option – H2 Database . Die Wahl ist getroffen, hier sind unsere Abhängigkeiten:

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'
}
Großartig, was kommt als nächstes? Wir müssen den Ruhezustand konfigurieren. Hibernate hat einen „ Getting Started Guide “, aber dieser ist dumm und eher ein Hindernis als eine Hilfe. Gehen wir daher wie die richtigen Leute direkt zum „ Benutzerhandbuch “. Im Inhaltsverzeichnis sehen wir den Abschnitt „ Bootstrap “, was übersetzt „Bootstrapping“ bedeutet. Genau das, was Sie brauchen. Dort sind viele kluge Worte geschrieben, aber der Punkt ist, dass es im Klassenpfad ein META-INF-Verzeichnis und eine persistence.xml-Datei geben sollte. Laut Standard enthält der Klassenpfad das Verzeichnis „resources“. Daher erstellen wir das angegebene Verzeichnis: mkdir src\main\resources\META-INF Erstellen Sie dort die Datei persistence.xml und öffnen Sie diese. Dort in der Dokumentation gibt es ein Beispiel „Beispiel 268. META-INF/persistence.xml-Konfigurationsdatei“, aus dem wir den Inhalt nehmen und in die Datei persistence.xml einfügen. Starten Sie als Nächstes die IDE und importieren Sie unser erstelltes Projekt darin. Jetzt müssen wir etwas in der Datenbank speichern. Dies ist etwas, das als Entität bezeichnet wird. Entitäten stellen etwas aus dem sogenannten Domänenmodell dar. Und siehe da, im Inhaltsverzeichnis sehen wir „ 2. Domain Model “. Wir gehen den Text durch und sehen im Kapitel „2.1. Mapping-Typen“ ein einfaches Beispiel einer Entität. Nehmen wir es uns vor und kürzen es ein wenig:
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;
    }
}
Jetzt haben wir eine Klasse, die eine Entität darstellt. Gehen wir zurück zu persistence.xml und korrigieren dort eine Stelle: Wo angegeben, classgeben wir unsere Klasse an entity.Contact. Großartig, es bleibt nur noch der Start. Kehren wir zum Bootstrap- Kapitel zurück . Da wir keinen Anwendungsserver haben, der uns eine spezielle EE-Umgebung (also eine Umgebung, die bestimmte Systemverhalten für uns umsetzt) ​​zur Verfügung stellt, arbeiten wir in einer SE-Umgebung. Hierfür eignet sich für uns lediglich das Beispiel „Example 269. Application bootstrapped EntityManagerFactory“. Machen wir zum Beispiel Folgendes:
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);
    }
}
Hurra, unser Thema ist fertig. Ich wollte diesen Teil nicht weglassen , weil... Für die folgenden Kapitel ist es ratsam zu verstehen, wie unser Thema entstanden ist.

Veränderbares Verhalten finden

Nehmen wir die Initialisierung des Zählfelds vom Typ BigInteger und setzen dort Haltepunkte ( BreakPoint ). Nach dem Einfügen in die gewünschte Zeile kann dies mit Strg+F8 oder über das Menü Ausführen -> Zeilenhaltepunkt umschalten erfolgen. Dann führen wir unsere Hauptmethode im Debug aus (Ausführen -> Debug):
IntelliJ-Idee: Dekompilierung, Kompilierung, Substitution (oder wie man die Fehler anderer Leute korrigiert) – 3
Ein etwas umständliches Beispiel, aber nehmen wir an, wir möchten die Anzahl der Abfragebereiche beim Start ändern. Wie wir sehen können, ist unsere sqlQuery NativeQueryImpl. Klicken Sie auf Ctrl+N, geben Sie den Namen der Klasse ein und gehen Sie dorthin. Wenn wir also zu einer Klasse gehen, werden wir an den Ort weitergeleitet, an dem sich diese Klasse befindet, und aktivieren den automatischen Bildlauf:
IntelliJ-Idee: Dekompilierung, Kompilierung, Substitution (oder wie man die Fehler anderer Leute korrigiert) – 4
Wir stellen sofort fest, dass Idea derzeit nicht weiß, wo der Quellcode des Programms (also der Quellcode) zu finden ist. Deshalb hat sie freundlicherweise den Inhalt aus der Klassendatei für uns dekompiliert:
IntelliJ-Idee: Dekompilierung, Kompilierung, Substitution (oder wie man die Fehler anderer Leute korrigiert) – 5
Beachten Sie auch, dass im Titel des IntelliJ Idea-Fensters steht, wo Gradle das Artefakt für uns speichert. Lassen Sie uns nun herausfinden, wo sich unser Artefakt befindet:
IntelliJ-Idee: Dekompilierung, Kompilierung, Substitution (oder wie man die Fehler anderer Leute korrigiert) – 6
Gehen wir in der Befehlszeile mit dem Befehl zu diesem Verzeichnis cd Weg zu каталогу. Ich mache gleich eine Anmerkung: Wenn es möglich ist, ein Projekt aus dem Quellcode zu erstellen, ist es besser, aus dem Quellcode zu erstellen. Der Hibernate-Quellcode ist beispielsweise auf der offiziellen Website verfügbar. Es ist besser, es für die gewünschte Version auszuwählen, dort alle Änderungen vorzunehmen und es mithilfe der im Projekt angegebenen Build-Skripte zusammenzustellen. Ich stelle in dem Artikel die schrecklichste Option vor – es gibt ein JAR, aber keinen Quellcode. Und Hinweis Nummer 2: Gradle kann den Quellcode mithilfe von Plugins erhalten. Weitere Informationen finden Sie unter So laden Sie Javadocs und Quellen für JAR mit Gradle herunter .

Eine Veränderung bewirken

Wir müssen die Verzeichnisstruktur entsprechend dem Paket neu erstellen, in dem sich die Klasse befindet, die wir ändern. In diesem Fall: mkdir org\hibernate\query\internal, danach erstellen wir eine Datei in diesem Verzeichnis NativeQueryImpl.java. Jetzt öffnen wir diese Datei und kopieren den gesamten Inhalt der Klasse aus der IDE dorthin (die gleiche, die Idea für uns dekompiliert hat). Ändern Sie die erforderlichen Zeilen. Zum Beispiel:
IntelliJ-Idee: Dekompilierung, Kompilierung, Substitution (oder wie man die Fehler anderer Leute korrigiert) – 7
Jetzt kompilieren wir die Datei. Wir machen: javac org\hibernate\query\internal\NativeQueryImpl.java. Wow, man kann es nicht einfach nehmen und fehlerfrei kompilieren. Wir haben eine Reihe von Fehlermeldungen „Symbol konnte nicht gefunden werden“ erhalten, weil ... Die veränderliche Klasse ist an andere Klassen gebunden, die IntelliJ Idea normalerweise für uns zum Klassenpfad hinzufügt. Spüren Sie den Nutzen unserer IDEs? =) Nun, fügen wir es selbst hinzu, wir können es auch tun. Kopieren wir die Pfade für:
  • [1] – hibernate-core-5.2.17.Final.jar
  • [2] – hibernate-jpa-2.1-api-1.0.0.Final.jar
So wie wir es gemacht haben: In der „Projekt“-Ansicht unter „Externe Bibliotheken“ suchen wir das benötigte JAR und klicken auf Ctrl+Shift+C. Lassen Sie uns nun den folgenden Befehl erstellen und ausführen: javac -cp [1];[2] org\hibernate\query\internal\NativeQueryImpl.java Als Ergebnis erscheinen neben der Java-Datei neue Klassendateien, die in der JAR-Datei aktualisiert werden müssen:
IntelliJ-Idee: Dekompilierung, Kompilierung, Substitution (oder wie man die Fehler anderer Leute korrigiert) – 8
Hurra, jetzt können Sie das JAR-Update durchführen. Wir können uns an offiziellen Materialien orientieren : jar uf hibernate-core-5.2.17.Final.jar org\hibernate\query\internal\*.class Mit Open IntelliJ Idea können Sie höchstwahrscheinlich keine Dateien ändern. Daher müssen Sie Idea höchstwahrscheinlich schließen und nach dem Update öffnen, bevor Sie das JAR-Update durchführen. Danach können Sie die IDE erneut öffnen und dubug erneut ausführen. Haltepunkte werden zwischen IDE-Neustarts nicht zurückgesetzt. Daher wird die Programmausführung dort angehalten, wo sie zuvor war. Voila, wir sehen, wie unsere Änderungen funktionieren:
IntelliJ-Idee: Dekompilierung, Kompilierung, Substitution (oder wie man die Fehler anderer Leute korrigiert) – 9
Großartig. Aber hier stellt sich die Frage – weshalb? Einfach aufgrund der Tatsache, dass Gradle beim Erstellen eines Projekts die Abhängigkeiten und den Repository-Block analysiert. Gradle verfügt über einen bestimmten Build-Cache, der sich an einem bestimmten Ort befindet (siehe „ Wie lege ich den Gradle-Cache-Speicherort fest? “). Wenn im Cache keine Abhängigkeit besteht, lädt Gradle ihn aus dem Repository herunter. Da wir das JAR in geändert haben Cache selbst, dann denkt Gradle, dass sich die Bibliothek im Cache befindet und pumpt nichts aus. Aber jedes Löschen des Caches führt dazu, dass unsere Änderungen verloren gehen. Außerdem kann niemand außer uns sie einfach holen. Was für eine Unannehmlichkeit , nicht wahr? Was zu tun ist. Hmm, Downloads aus dem Repository? Wir brauchen also unser Repository, mit Präferenzen und Dichtern. Das ist der nächste Schritt.

Bereitstellen des Repositorys

Es gibt verschiedene kostenlose Lösungen für die Bereitstellung Ihres Repositorys: eine davon ist Artifactory und die andere ist Apache Archive . Artifactory sieht modisch, stilvoll und modern aus, aber ich hatte Schwierigkeiten damit, ich wollte Artefakte nicht richtig platzieren und generierte fehlerhafte Maven-Metadaten. Daher hat die Apache-Version unerwarteterweise für mich funktioniert. Es stellte sich heraus, dass es nicht so schön war, aber es funktioniert zuverlässig. Suchen Sie auf der Download- Seite nach der Standalone-Version und entpacken Sie sie. Sie haben ihren eigenen „ Quick Start “. Nach dem Start müssen Sie warten, bis die Adresse angezeigt wird http://127.0.0.1:8080/#repositorylist. Wählen Sie anschließend „Artefakt hochladen“:
IntelliJ-Idee: Dekompilierung, Kompilierung, Substitution (oder wie man die Fehler anderer Leute korrigiert) – 10
Klicken Sie auf „Upload starten“ und dann auf „Dateien speichern“. Danach erscheint eine grüne Erfolgsmeldung und das Artefakt wird im Abschnitt „Durchsuchen“ verfügbar. Dies sollte für JAR- und POM-Dateien durchgeführt werden:
IntelliJ-Idee: Dekompilierung, Kompilierung, Substitution (oder wie man die Fehler anderer Leute korrigiert) – 11
Dies liegt daran, dass in der POM-Datei zusätzliche Hibernate-Abhängigkeiten angegeben sind. Und uns bleibt nur noch ein Schritt: Geben Sie das Repository in unserem Build-Skript an:

repositories {
    jcenter()
    maven {
        url "http://127.0.0.1:8080/repository/internal/"
    }
}
Und dementsprechend wird die Version unseres Ruhezustands wie folgt aussehen: compile 'org.hibernate:hibernate-core:5.2.17.Final-JAVARUSH'. Das ist alles, jetzt verwendet unser Projekt die von uns korrigierte Version und nicht die Originalversion.

Abschluss

Es scheint, als hätten wir uns kennengelernt. Ich hoffe, es war interessant. Solche „Tricks“ werden selten durchgeführt, aber wenn Ihre Geschäftsanforderungen plötzlich Bedingungen vorgeben, die die von Ihnen verwendeten Bibliotheken nicht erfüllen können, wissen Sie, was zu tun ist. Und ja, hier sind ein paar Beispiele, die auf diese Weise korrigiert werden können:
  • Es gibt einen Webserver namens Undertow. Bis vor einiger Zeit gab es einen Fehler, der es uns bei Verwendung eines Proxys nicht ermöglichte, die IP des Endbenutzers herauszufinden.
  • Vorerst behandelte WildFly JPA einen Moment, der in der Spezifikation nicht berücksichtigt wurde, auf eine bestimmte Art und Weise, weshalb Ausnahmen ausgelöst wurden. Und es war nicht konfigurierbar.
#Wjatscheslaw
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION