JavaRush /Blog Java /Random-PL /Pomysł IntelliJ: Dekompilacja, Kompilacja, Podstawienie (...
Viacheslav
Poziom 3

Pomysł IntelliJ: Dekompilacja, Kompilacja, Podstawienie (czyli jak poprawić błędy innych)

Opublikowano w grupie Random-PL
„Nie wymyślaj koła na nowo” to jedna z głównych zasad udanej i wydajnej pracy. Co jednak zrobić, gdy nie chcesz wymyślać własnego koła na nowo, a kierownica kogoś innego okazuje się krzywa, a koła są kwadratowe? Celem tej recenzji jest jak najkrótsze wprowadzenie do techniki naprawiania bibliotek innych osób „w ostateczności” oraz sposobu rozszerzenia tej możliwości na swój komputer.
Pomysł IntelliJ: Dekompilacja, Kompilacja, Podstawienie (czyli jak poprawić błędy innych) - 1

Wstęp

Wszyscy używamy tego czy innego narzędzia. Czasami jednak narzędzia nie są w pełni odpowiednie lub zawierają błędy. Dzięki funkcjom języka Java możemy korygować zachowanie narzędzi tam, gdzie tego potrzebujemy. Dobrze, gdy współtworzymy projekty i wysyłamy pull requesty (więcej możesz przeczytać tutaj: „ GitHub – Współtworzenie projektów ”). Ale mogą nie zostać zaakceptowane natychmiast lub nawet nie zostać zaakceptowane. Ale na potrzeby projektu jest to konieczne teraz. I tutaj, mam nadzieję, ten artykuł pokaże narzędzia, którymi dysponujemy jako programiści. Będziemy musieli wykonać następujące kroki, o których będziemy mówić:
  • Przygotuj np. aplikację testową (na przykładzie projektu Hibernate)
  • Znalezienie zmiennej lokalizacji
  • Dokonywanie zmiany
  • Wdrażanie repozytorium
Wszystkie poniższe kroki dotyczą systemu operacyjnego Windows, ale mają odpowiedniki dla systemów Nix. Możesz więc je powtórzyć, jeśli zajdzie taka potrzeba.

Przygotowanie przedmiotu

Potrzebujemy więc projektu testowego. Hibernacja jest dla nas idealna, ponieważ... jest „stylowy, modny, nowoczesny”. Nie będę się wdawać w szczegóły, bo... Artykuł nie dotyczy hibernacji. Wszystko zrobimy szybko i na temat. A my, podobnie jak prawdziwi programiści, będziemy korzystać z systemu kompilacji. Na przykład odpowiedni jest dla nas Gradle, który należy zainstalować na potrzeby tego artykułu ( https://gradle.org/install/ ). Najpierw musimy stworzyć projekt. Maven ma do tego archetypy , a Gradle ma do tego specjalną wtyczkę: Gradle Init . Otwórz więc wiersz poleceń w dowolny znany ci sposób. Utwórz katalog dla projektu, przejdź do niego i wykonaj polecenie:

mkdir javarush 
cd javarush 
gradle init --type java-application
Pomysł IntelliJ: Dekompilacja, Kompilacja, Podstawienie (czyli jak poprawić błędy innych) - 2
Przed zaimportowaniem projektu wprowadźmy pewne zmiany w pliku opisującym sposób budowania. Plik ten nazywa się skryptem kompilacji i nosi nazwę build.gradle. Znajduje się w katalogu, w którym wykonaliśmy gradle init. Dlatego po prostu go otwieramy (na przykład w Windows za pomocą polecenia start build.gradle). Znajdujemy tam blok „ zależności ”, czyli tzw. zależności. Wszystkie słoiki innych firm, których będziemy używać, są opisane tutaj. Teraz musimy zrozumieć, co tu opisać. Przejdźmy do strony Hibernate ( http://hibernate.org/ ). Jesteśmy zainteresowani Hibernate ORM . Potrzebujemy najnowszej wersji. W menu po lewej stronie znajduje się podsekcja „Wydania”. Wybierz „najnowszą stabilną”. Przewiń w dół i znajdź „Wdrożenie podstawowe (w tym JPA)”. Wcześniej konieczne było osobne podłączenie obsługi JPA, ale teraz wszystko stało się prostsze i wystarczyła tylko jedna zależność. Będziemy także musieli pracować z bazą danych za pomocą Hibernacji. Aby to zrobić, weźmy najprostszą opcję - Baza danych H2 . Wybór został dokonany, oto nasze zależności:

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'
}
Świetnie, co dalej? Musimy skonfigurować Hibernację. Hibernate ma „ Przewodnik wprowadzający ”, ale jest on głupi i bardziej przeszkadza niż pomaga. Przejdźmy więc od razu do „ Podręcznika użytkownika ” jak właściwi ludzie. W spisie treści widzimy sekcję „ Bootstrap ”, co oznacza „Bootstrapping”. Tylko to, czego potrzebujesz. Jest tam napisanych sporo mądrych słów, ale chodzi o to, żeby w ścieżce klas znajdował się katalog META-INF i plik Persistence.xml. Zgodnie ze standardem ścieżka klas zawiera katalog „zasoby”. Dlatego tworzymy określony katalog: mkdir src\main\resources\META-INF Utwórz tam plik trwałości.xml i otwórz go. Tam w dokumentacji znajduje się przykład „Przykład 268. Plik konfiguracyjny META-INF/persistence.xml”, z którego pobierzemy zawartość i wrzucimy ją do pliku Persistence.xml. Następnie uruchamiamy IDE i importujemy do niego stworzony przez nas projekt. Teraz musimy zapisać coś w bazie danych. Jest to coś zwanego bytem. Podmioty reprezentują coś z tak zwanego modelu domeny. A w spisie treści widzimy „ 2. Model domeny ”. Schodzimy w dół tekstu i w rozdziale „2.1. Typy mapowania” widzimy prosty przykład encji. Weźmy to dla siebie, trochę skracając:
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;
    }
}
Mamy teraz klasę reprezentującą byt. Wróćmy do pliku trwałości.xml i poprawmy tam jedno miejsce: Tam, gdzie jest to wskazane, classwskażemy naszą klasę entity.Contact. Świetnie, pozostaje tylko uruchomić. Wróćmy do rozdziału Bootstrap . Ponieważ nie mamy serwera aplikacji, który udostępni nam specjalne środowisko EE (czyli środowisko realizujące za nas określone zachowanie systemu), pracujemy w środowisku SE. W tym celu odpowiedni jest dla nas tylko przykład „Przykład 269. Aplikacja ładowana EntityManagerFactory”. Zróbmy to na przykład:
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, nasz temat jest gotowy. Nie chciałem pominąć tej części , bo... W następnych rozdziałach wskazane jest zrozumienie, jak powstał nasz temat.

Znalezienie modyfikowalnego zachowania

Zajmijmy miejsce inicjalizacji pola licznikowego typu BigInteger i ustawmy tam punkty przerwania ( BreakPoint ). Po wstawieniu na żądaną linię można to zrobić za pomocą Ctrl+F8 lub poprzez menu Uruchom -> Przełącz punkt przerwania linii. Następnie uruchamiamy naszą główną metodę w debugowaniu (Uruchom -> Debuguj):
Pomysł IntelliJ: Dekompilacja, Kompilacja, Podstawienie (czyli jak poprawić błędy innych) - 3
Trochę niezdarny przykład, ale załóżmy, że chcemy zmienić liczbę spacji zapytań przy uruchomieniu. Jak widzimy, nasze zapytanie sqlQuery to NativeQueryImpl. Kliknij Ctrl+N, wpisz nazwę zajęć i przejdź do nich. Tak, że gdy udamy się na zajęcia, zostaniemy przeniesieni do miejsca, w którym znajdują się te zajęcia i włączymy autoprzewijanie:
Pomysł IntelliJ: Dekompilacja, Kompilacja, Podstawienie (czyli jak poprawić błędy innych) - 4
Od razu zaznaczmy, że Idea nie wie obecnie, gdzie znaleźć kod źródłowy programu (czyli kod źródłowy). Dlatego uprzejmie zdekompilowała dla nas zawartość pliku klasy:
Pomysł IntelliJ: Dekompilacja, Kompilacja, Podstawienie (czyli jak poprawić błędy innych) - 5
Zauważ też, że w tytule okna IntelliJ Idea jest napisane, gdzie Gradle zapisuje dla nas artefakt. Przejdźmy teraz do Idea, gdzie znajduje się nasz artefakt:
Pomysł IntelliJ: Dekompilacja, Kompilacja, Podstawienie (czyli jak poprawić błędy innych) - 6
Przejdźmy do tego katalogu w wierszu poleceń za pomocą polecenia cd sposób na каталогу. Od razu zaznaczę: jeśli można zbudować projekt ze źródeł, lepiej jest budować ze źródeł. Na przykład kod źródłowy Hibernate jest dostępny na oficjalnej stronie internetowej. Lepiej jest wybrać żądaną wersję, wprowadzić tam wszystkie zmiany i złożyć, korzystając ze skryptów kompilacji określonych w projekcie. W artykule przedstawiam najstraszniejszą opcję - jest słoik, ale nie ma kodu źródłowego. I uwaga nr 2: Gradle może uzyskać kod źródłowy za pomocą wtyczek. Aby uzyskać szczegółowe informacje, zobacz Jak pobrać dokumentację javadoc i źródła jar przy użyciu Gradle .

Dokonywanie zmiany

Musimy odtworzyć strukturę katalogów zgodnie z pakietem, w którym znajduje się klasa, którą zmieniamy. W tym przypadku: mkdir org\hibernate\query\internal, po czym tworzymy plik w tym katalogu NativeQueryImpl.java. Teraz otwieramy ten plik i kopiujemy tam całą zawartość klasy z IDE (tego samego, które zdekompilował dla nas Idea). Zmień niezbędne linie. Na przykład:
IntelliJ Idea: Dekompilacja, Kompilacja, Podstawienie (czyli jak poprawić błędy innych) - 7
Teraz skompilujmy plik. My robimy: javac org\hibernate\query\internal\NativeQueryImpl.java. Wow, nie można tego po prostu wziąć i skompilować bez błędów. Otrzymaliśmy kilka błędów związanych z brakiem możliwości znalezienia symbolu, ponieważ... klasa zmienna jest powiązana z innymi klasami, które IntelliJ Idea zwykle dodaje za nas do ścieżki klas. Czy czujesz całą użyteczność naszych IDE? =) No cóż, dodajmy to sami, my też możemy to zrobić. Skopiujmy ścieżki dla:
  • [1] - hibernate-core-5.2.17.Final.jar
  • [2] - hibernate-jpa-2.1-api-1.0.0.Final.jar
Tak jak my: w widoku „Projekt” w „Bibliotekach zewnętrznych” znajdujemy wymagany słoik i klikamy Ctrl+Shift+C. Utwórzmy teraz i wykonajmy następujące polecenie: javac -cp [1];[2] org\hibernate\query\internal\NativeQueryImpl.java W rezultacie obok pliku Java pojawią się nowe pliki klas, które należy zaktualizować w pliku jar:
Pomysł IntelliJ: Dekompilacja, Kompilacja, Podstawienie (czyli jak poprawić błędy innych) - 8
Hurra, teraz możesz przeprowadzić aktualizację słoika. Możemy kierować się oficjalnymi materiałami : jar uf hibernate-core-5.2.17.Final.jar org\hibernate\query\internal\*.class Open IntelliJ Idea najprawdopodobniej nie pozwoli na zmianę plików. Dlatego przed wykonaniem aktualizacji słoika najprawdopodobniej będziesz musiał zamknąć Idea, a po aktualizacji otworzyć go. Następnie możesz ponownie otworzyć IDE i ponownie uruchomić dubug. Punkty przerwania nie są resetowane pomiędzy ponownymi uruchomieniami IDE. Dlatego wykonywanie programu zatrzyma się tam, gdzie było wcześniej. Voila, widzimy, jak działają nasze zmiany:
Pomysł IntelliJ: Dekompilacja, Kompilacja, Podstawienie (czyli jak poprawić błędy innych) - 9
Świetnie. Ale tu pojawia się pytanie - z powodu czego? Po prostu dlatego, że kiedy Gradle buduje projekt, analizuje blok zależności i repozytoriów. Gradle ma określoną pamięć podręczną kompilacji, która znajduje się w określonej lokalizacji (zobacz „ Jak ustawić lokalizację pamięci podręcznej gradle? ” Jeśli w pamięci podręcznej nie ma zależności, Gradle pobierze ją z repozytorium. Ponieważ zmieniliśmy słoik w cache, wtedy Gradle myśli, że biblioteka znajduje się w pamięci podręcznej i niczego nie wypompowuje. Ale jakiekolwiek wyczyszczenie pamięci podręcznej doprowadzi do utraty naszych zmian. Poza tym nikt poza nami nie może po prostu iść i je pobrać. Ile niedogodności , prawda? Co robić. Hmm, pobieranie z repozytorium? Potrzebujemy więc naszego repozytorium, z preferencjami i poetkami. To jest kolejny krok.

Wdrażanie repozytorium

Istnieją różne bezpłatne rozwiązania do wdrażania repozytorium: jedno z nich to Artifactory , a drugie to Apache Archive . Artifactory wygląda modnie, stylowo, nowocześnie, ale miałem z nim trudności, nie chciałem poprawnie umieszczać artefaktów i generowałem błędne metadane mavena. Dlatego nieoczekiwanie dla mnie wersja Apache zadziałała dla mnie. Okazało się, że nie jest tak pięknie, ale działa niezawodnie. Na stronie pobierania znajdź wersję samodzielną i rozpakuj ją. Mają swój własny „ Szybki Start ”. Po uruchomieniu musisz poczekać na adres http://127.0.0.1:8080/#repositorylist. Następnie wybierz „Prześlij artefakt”:
Pomysł IntelliJ: Dekompilacja, Kompilacja, Podstawienie (czyli jak poprawić błędy innych) - 10
Kliknij „Rozpocznij przesyłanie”, a następnie „Zapisz pliki”. Następnie pojawi się zielony komunikat o powodzeniu, a artefakt stanie się dostępny w sekcji „Przeglądaj”. Należy to zrobić dla plików jar i pom:
Pomysł IntelliJ: Dekompilacja, Kompilacja, Podstawienie (czyli jak poprawić błędy innych) - 11
Wynika to z faktu, że w pliku pom określone są dodatkowe zależności hibernacji. A pozostał nam tylko 1 krok - określ repozytorium w naszym skrypcie kompilacji:

repositories {
    jcenter()
    maven {
        url "http://127.0.0.1:8080/repository/internal/"
    }
}
I odpowiednio wersja naszej hibernacji będzie: compile 'org.hibernate:hibernate-core:5.2.17.Final-JAVARUSH'. To wszystko, teraz nasz projekt korzysta z poprawionej przez nas wersji, a nie oryginalnej.

Wniosek

Wygląda na to, że się poznaliśmy. Mam nadzieję, że było ciekawie. Takie „sztuczki” zdarzają się rzadko, ale jeśli nagle wymagania biznesowe postawią warunki, których biblioteki, z których korzystasz, nie są w stanie spełnić, wiesz, co robić. I tak, oto kilka przykładów, które można poprawić w ten sposób:
  • Istnieje serwer WWW o nazwie Undertow. Do pewnego czasu występował błąd, który podczas korzystania z serwera proxy nie pozwalał nam poznać adresu IP użytkownika końcowego.
  • Na razie WildFly JPA poradziło sobie w pewien sposób z jednym momentem nieuwzględnionym w specyfikacji, z tego powodu rzucone zostały wyjątki. I nie było to konfigurowalne.
#Wiaczesław
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION