JavaRush /Java Blog /Random-IT /IntelliJ Idea: decompilazione, compilazione, sostituzione...
Viacheslav
Livello 3

IntelliJ Idea: decompilazione, compilazione, sostituzione (o come correggere gli errori di altre persone)

Pubblicato nel gruppo Random-IT
“Non reinventare la ruota” è una delle regole principali per un lavoro efficiente e di successo. Ma cosa fare quando non vuoi reinventare il tuo volante, ma il volante di qualcun altro risulta storto e le ruote sono quadrate? Questa recensione ha lo scopo di fornire un'introduzione quanto più breve possibile alla tecnica di correggere le librerie di altre persone "come ultima risorsa" e come estenderla al tuo computer.
IntelliJ Idea: decompilazione, compilazione, sostituzione (o come correggere gli errori di altre persone) - 1

introduzione

Usiamo tutti uno strumento o un altro. Ma a volte gli strumenti non sono completamente adatti o presentano errori. Grazie alle funzionalità del linguaggio Java possiamo correggere il comportamento degli strumenti dove ne abbiamo bisogno. È positivo quando contribuiamo ai progetti e inviamo richieste pull (puoi leggere di più qui: " GitHub - Contribuire ai progetti "). Ma potrebbero non essere accettati subito, o addirittura non essere accettati. Ma per le esigenze del progetto è necessario adesso. E qui, spero, questo articolo mostrerà gli strumenti a nostra disposizione come sviluppatori. Dovremo eseguire i seguenti passaggi di cui parleremo:
  • Preparare ad esempio un'applicazione di prova (utilizzando l'esempio di un progetto Hibernate)
  • Trovare una posizione mutevole
  • Fare un cambiamento
  • Distribuzione del repository
Tutti i passaggi seguenti sono indicati per il sistema operativo Windows, ma hanno analoghi per i sistemi nix. Quindi puoi ripeterli se necessario.

Preparazione del soggetto

Quindi, abbiamo bisogno di un progetto di prova. L'ibernazione è l'ideale per noi, perché... è "elegante, alla moda, moderno". Non entrerò troppo nei dettagli perché... L'articolo non riguarda l'ibernazione. Faremo tutto in modo rapido e mirato. E noi, come veri sviluppatori, utilizzeremo il sistema di compilazione. Ad esempio, per noi è adatto anche Gradle, che deve essere installato per questo articolo ( https://gradle.org/install/ ). Per prima cosa dobbiamo creare un progetto. Maven ha archetipi per questo e Gradle ha un plugin speciale per questo: Gradle Init . Quindi, apri la riga di comando in qualsiasi modo tu sappia. Crea una directory per il progetto, vai su di essa ed esegui il comando:

mkdir javarush 
cd javarush 
gradle init --type java-application
IntelliJ Idea: decompilazione, compilazione, sostituzione (o come correggere gli errori di altre persone) - 2
Prima di importare il progetto, apportiamo alcune modifiche al file che descrive come costruire. Questo file è chiamato script di build e si chiama build.gradle. Si trova nella directory in cui abbiamo eseguito gradle init. Pertanto, lo apriamo semplicemente (ad esempio, in Windows con il comando start build.gradle). Troviamo lì il blocco “ dipendenze ”, cioè dipendenze. Tutti i jar di terze parti che utilizzeremo sono descritti qui. Ora dobbiamo capire cosa descrivere qui. Andiamo al sito web di Hibernate ( http://hibernate.org/ ). Siamo interessati all'ORM di ibernazione . Abbiamo bisogno dell'ultima versione. Nel menu a sinistra c'è una sottosezione “Versioni”. Seleziona "ultima stabile". Scorri verso il basso e trova "Implementazione core (include JPA)". In precedenza, era necessario connettere il supporto JPA separatamente, ma ora tutto è diventato più semplice ed è sufficiente una sola dipendenza. Dovremo anche lavorare con il database utilizzando Hibernate. Per fare ciò, prendiamo l'opzione più semplice: H2 Database . La scelta è fatta, ecco le nostre dipendenze:

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'
}
Ottimo, cosa c'è dopo? Dobbiamo configurare l'ibernazione. Hibernate ha una " Guida introduttiva ", ma è stupida e più un ostacolo che un aiuto. Andiamo quindi direttamente alla “ Guida per l’utente ” come fanno le persone giuste. Nel sommario vediamo la sezione “ Bootstrap ”, che si traduce come “Bootstrapping”. Proprio quello di cui hai bisogno. Ci sono molte parole intelligenti scritte lì, ma il punto è che dovrebbe esserci una directory META-INF sul classpath e dovrebbe esserci un file persistence.xml. Secondo lo standard, il classpath contiene la directory “resources”. Creiamo quindi la directory specificata: mkdir src\main\resources\META-INF creiamo lì il file persistence.xml e lo apriamo. Lì nella documentazione c'è un esempio “Esempio 268. File di configurazione META-INF/persistence.xml” dal quale prenderemo il contenuto e lo inseriremo nel file persistence.xml. Successivamente, avvia l'IDE e importa al suo interno il nostro progetto creato. Ora dobbiamo salvare qualcosa nel database. Questo è qualcosa chiamato entità. Le entità rappresentano qualcosa del cosiddetto modello di dominio. E nel sommario, ecco, vediamo “ 2. Modello di dominio ”. Scendiamo nel testo e vediamo nel capitolo "2.1. Tipi di mappatura" un semplice esempio di entità. Prendiamolo per noi, abbreviandolo un po':
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;
    }
}
Ora abbiamo una classe che rappresenta un'entità. Torniamo a persistence.xml e correggiamo un punto lì: dove indicato, classindicheremo la nostra classe entity.Contact. Ottimo, non resta che lanciarlo. Torniamo al capitolo Bootstrap . Poiché non disponiamo di un server applicativo che ci fornisca uno speciale ambiente EE (ovvero un ambiente che implementa per noi determinati comportamenti del sistema), lavoriamo in un ambiente SE. Per questo, per noi è adatto solo l’esempio “Esempio 269. Applicazione EntityManagerFactory bootstrap”. Ad esempio, facciamo questo:
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);
    }
}
Evviva, il nostro argomento è pronto. Non volevo omettere questa parte perché... Per i capitoli successivi è opportuno capire come è nato il nostro argomento.

Trovare un comportamento modificabile

Prendiamo il posto dell'inizializzazione del campo count di tipo BigInteger e impostiamo lì dei punti di interruzione ( BreakPoint ). Dopo aver inserito sulla riga desiderata, è possibile farlo utilizzando Ctrl+F8 o tramite il menu Esegui -> Attiva/disattiva punto di interruzione riga. Quindi eseguiamo il nostro metodo principale nel debug (Esegui -> Debug):
IntelliJ Idea: decompilazione, compilazione, sostituzione (o come correggere gli errori di altre persone) - 3
Un esempio un po' goffo, ma diciamo che vogliamo cambiare il numero di spazi di query all'avvio. Come possiamo vedere, la nostra sqlQuery è NativeQueryImpl. Fare clic su Ctrl+N, scrivere il nome della classe e accedervi. In modo che quando andiamo a una lezione, verremo trasferiti nel luogo in cui si trova questa classe e attiveremo lo scorrimento automatico:
IntelliJ Idea: decompilazione, compilazione, sostituzione (o come correggere gli errori di altre persone) - 4
Notiamo subito che Idea al momento non sa dove trovare il codice sorgente del programma (codice sorgente, intendo). Pertanto, ha gentilmente decompilato per noi il contenuto del file di classe:
IntelliJ Idea: decompilazione, compilazione, sostituzione (o come correggere gli errori di altre persone) - 5
Da notare anche che nel titolo della finestra di IntelliJ Idea è scritto dove Gradle ci salva l'artefatto. Ora, inseriamo in Idea il percorso in cui si trova il nostro artefatto:
IntelliJ Idea: decompilazione, compilazione, sostituzione (o come correggere gli errori di altre persone) - 6
Andiamo in questa directory sulla riga di comando utilizzando il comando cd way to каталогу. Prendo subito nota: se è possibile costruire un progetto dal sorgente, è meglio costruire dal sorgente. Ad esempio, il codice sorgente di Hibernate è disponibile sul sito ufficiale. È meglio prenderlo per la versione desiderata e apportare tutte le modifiche lì e assemblarle utilizzando gli script di build specificati nel progetto. Presento nell'articolo l'opzione più terribile: c'è un jar, ma nessun codice sorgente. E nota numero 2: Gradle può ottenere il codice sorgente utilizzando i plugin. Vedi Come scaricare javadoc e sorgenti per jar utilizzando Gradle per i dettagli .

Fare un cambiamento

Dobbiamo ricreare la struttura delle directory in base al pacchetto in cui si trova la classe che stiamo modificando. In questo caso: mkdir org\hibernate\query\internal, dopodiché creiamo un file in questa directory NativeQueryImpl.java. Ora apriamo questo file e copiamo tutto il contenuto della classe dall'IDE lì (lo stesso che Idea ha decompilato per noi). Modificare le righe necessarie. Per esempio:
IntelliJ Idea: decompilazione, compilazione, sostituzione (o come correggere gli errori di altre persone) - 7
Ora compiliamo il file. Noi facciamo: javac org\hibernate\query\internal\NativeQueryImpl.java. Wow, non puoi semplicemente prenderlo e compilarlo senza errori. Abbiamo ricevuto un sacco di errori Impossibile trovare il simbolo, perché... la classe mutabile è legata ad altre classi, che IntelliJ Idea di solito aggiunge al classpath per noi. Senti tutta l'utilità dei nostri IDE? =) Bene, aggiungiamolo noi stessi, possiamo farlo anche noi. Copiamo i percorsi per:
  • [1] - hibernate-core-5.2.17.Final.jar
  • [2] - hibernate-jpa-2.1-api-1.0.0.Final.jar
Proprio come abbiamo fatto noi: nella vista “Progetto” in “Librerie esterne” troviamo il jar richiesto e facciamo clic su Ctrl+Shift+C. Ora creiamo ed eseguiamo il seguente comando: javac -cp [1];[2] org\hibernate\query\internal\NativeQueryImpl.java Di conseguenza, accanto al file java appariranno nuovi file di classe, che dovranno essere aggiornati nel file jar:
IntelliJ Idea: decompilazione, compilazione, sostituzione (o come correggere gli errori di altre persone) - 8
Evviva, ora puoi eseguire l'aggiornamento del jar. Possiamo farci guidare dai materiali ufficiali : jar uf hibernate-core-5.2.17.Final.jar org\hibernate\query\internal\*.class Open IntelliJ Idea molto probabilmente non ti consentirà di modificare i file. Pertanto, prima di eseguire l'aggiornamento del jar, molto probabilmente dovrai chiudere Idea e, dopo l'aggiornamento, aprirlo. Successivamente, puoi riaprire l'IDE ed eseguire nuovamente dubug. I punti di interruzione non vengono reimpostati tra i riavvii dell'IDE. Pertanto, l'esecuzione del programma si fermerà dove si trovava prima. Voilà, vediamo come funzionano le nostre modifiche:
IntelliJ Idea: decompilazione, compilazione, sostituzione (o come correggere gli errori di altre persone) - 9
Grande. Ma qui sorge la domanda: a causa di cosa? Semplicemente perché quando Gradle crea un progetto, analizza le dipendenze e il blocco dei repository. Gradle ha una determinata cache di build, che si trova in una determinata posizione (vedi " Come impostare la posizione della cache di Gradle? " Se non c'è dipendenza nella cache, Gradle la scaricherà dal repository. Poiché abbiamo cambiato il jar nel cache stessa, Gradle pensa che la libreria sia nella cache e non pompa nulla. Ma qualsiasi svuotamento della cache porterà alla perdita delle nostre modifiche. Inoltre, nessuno tranne noi può andare a prenderle. Quanti disagi , non è vero? Cosa fare. Hmm, download dal repository? Quindi abbiamo bisogno del nostro repository, con preferenze e poetesse. Questo è il passo successivo.

Distribuzione del repository

Esistono diverse soluzioni gratuite per distribuire il tuo repository: una di queste è Artifactory e l'altra è Apache Archive . Artifactory sembra alla moda, elegante, moderno, ma ho avuto difficoltà con esso, non volevo posizionare correttamente gli artefatti e ho generato metadati Maven errati. Pertanto, inaspettatamente per me, la versione Apache ha funzionato per me. Si è rivelato non così bello, ma funziona in modo affidabile. Nella pagina di download , cerca la versione standalone e scompattala. Hanno il loro " Avvio rapido ". Dopo il lancio, è necessario attendere fino all'indirizzo http://127.0.0.1:8080/#repositorylist. Successivamente, seleziona "Carica artefatto":
IntelliJ Idea: decompilazione, compilazione, sostituzione (o come correggere gli errori di altre persone) - 10
Fare clic su "Avvia caricamento", quindi su "Salva file". Successivamente, verrà visualizzato un messaggio verde di successo e l'artefatto diventerà disponibile nella sezione "Sfoglia". Questo dovrebbe essere fatto per i file jar e pom:
IntelliJ Idea: decompilazione, compilazione, sostituzione (o come correggere gli errori di altre persone) - 11
Ciò è dovuto al fatto che nel file pom sono specificate ulteriori dipendenze di ibernazione. E ci resta solo 1 passaggio: specificare il repository nel nostro script di build:

repositories {
    jcenter()
    maven {
        url "http://127.0.0.1:8080/repository/internal/"
    }
}
E, di conseguenza, la versione del nostro letargo diventerà: compile 'org.hibernate:hibernate-core:5.2.17.Final-JAVARUSH'. Questo è tutto, ora il nostro progetto utilizza la versione che abbiamo corretto e non quella originale.

Conclusione

Sembra che ci siamo conosciuti. Spero che sia stato interessante. Tali "trucchi" vengono utilizzati raramente, ma se all'improvviso i requisiti aziendali impongono condizioni che le librerie che utilizzi non possono soddisfare, sai cosa fare. E sì, ecco un paio di esempi che possono essere corretti in questo modo:
  • C'è un server web chiamato Undertow. Fino a qualche tempo fa c'era un bug che, utilizzando un proxy, non ci permetteva di conoscere l'IP dell'utente finale.
  • Per il momento, WildFly JPA ha gestito in un certo modo un momento non preso in considerazione dalle specifiche, per questo motivo sono state lanciate delle eccezioni. E non era configurabile.
#Viacheslav
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION