JavaRush /Java Blog /Random-TW /IntelliJ Idea:反編譯、編譯、替換(或如何修正別人的錯誤)
Viacheslav
等級 3

IntelliJ Idea:反編譯、編譯、替換(或如何修正別人的錯誤)

在 Random-TW 群組發布
「不要重新發明輪子」是成功且有效率地工作的主要規則之一。但是,當你不想重新發明自己的輪子,但別人的方向盤是彎的,而輪子是方的時,該怎麼辦呢?這篇評論的目的是盡可能簡要地介紹「作為最後手段」修復其他人的庫的技術,以及如何將這個問題擴展到你的電腦之外。
IntelliJ Idea:反編譯、編譯、替換(或如何修正別人的錯誤) - 1

介紹

我們都使用一種或另一種工具。但有時這些工具並不完全適合或有錯誤。由於 Java 語言的特性,我們可以在需要的地方修正工具的行為。當我們為專案做出貢獻並發送拉取請求時,這很好(您可以在此處閱讀更多內容:「GitHub - 為專案做出貢獻」)。但他們可能不會立即被接受,甚至可能不被接受。但出於專案的需要,現在是有必要的。我希望本文能向我們展示作為開發人員可以使用的工具。我們需要執行我們將要討論的以下步驟:
  • 準備一個測試應用程式例如(使用Hibernate專案的範例)
  • 尋找可變動的地點
  • 做出改變
  • 部署儲存庫
以下所有步驟均適用於 Windows 作業系統,但也有適用於 nix 系統的類似步驟。因此,如有必要,您可以重複這些操作。

科目準備

所以,我們需要一個測試項目。Hibernate 對我們來說是理想的選擇,因為... 它是「時尚、時尚、現代」。我不會講太多細節,因為… 這篇文章不是關於 Hibernate 的。我們將迅速、切中要害地做每件事。我們,像適當的開發人員一樣,將使用建置系統。例如Gradle也適合我們,本文必須安裝它(https://gradle.org/install/)。首先,我們需要建立一個專案。Maven 有這方面的原型,Gradle 有一個專門的插件:Gradle Init。因此,請以您已知的任何方式開啟命令列。為專案建立一個目錄,進入該目錄並執行指令:

mkdir javarush 
cd javarush 
gradle init --type java-application
IntelliJ Idea:反編譯、編譯、替換(或如何修正別人的錯誤)- 2
在導入專案之前,讓我們對描述如何建置的檔案進行一些更改。該檔案稱為建置腳本,名為 build.gradle。它位於我們執行gradle init的目錄中。因此,我們只需打開它(例如,在 Windows 中使用 start build.gradle 命令)。我們在那裡找到「依賴項」區塊,即 依賴關係。這裡描述了我們將使用的所有第三方 jar。現在我們需要了解這裡要描述什麼。讓我們造訪 Hibernate 網站 ( http://hibernate.org/ )。我們對Hibernate ORM感興趣。我們需要最新版本。在左側選單中有一個「發布」小節。選擇“最新穩定版”。向下捲動並找到“核心實作(包括 JPA)”。以前需要單獨連接JPA支持,但現在一切都變得簡單了,只需要一個依賴就足夠了。我們還需要使用 Hibernate 來處理資料庫。為此,我們採用最簡單的選項 - H2 資料庫。選擇已做出,這是我們的依賴項:

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'
}
太好了,下一步是什麼?我們需要設定 Hibernate。Hibernate 有一個“入門指南”,但它很愚蠢,而且更多的是阻礙而不是幫助。因此,讓我們像合適的人一樣直接進入「使用者指南」。在目錄中,我們看到「Bootstrap」部分,翻譯為「Bootstrapping」。正是您所需要的。那裡寫了很多聰明的話,但重點是類路徑上應該有一個 META-INF 目錄,並且應該有一個 persistence.xml 檔案。根據標準,類路徑包含“resources”目錄。因此,我們創建指定的目錄:mkdir src\main\resources\META-INF 在那裡創建 persistence.xml 檔案並打開它。文件中有一個範例“範例 268. META-INF/persistence.xml 設定檔”,我們將從中取得內容並將其插入到 persistence.xml 檔案中。接下來,啟動 IDE 並將我們建立的專案匯入其中。現在我們需要將一些內容保存到資料庫中。這就是所謂的實體。實體代表所謂的領域模型中的某些東西。在目錄中,你瞧,我們看到「2.領域模型」。我們繼續閱讀正文,在「2.1.映射類型」一章中看到一個簡單的實體範例。讓我們把它簡化一下:
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;
    }
}
現在我們有一個代表實體的類別。讓我們回到 persistence.xml 並更正那裡的一個地方:在指示的地方,class我們將指示我們的 class entity.Contact。太棒了,剩下的就是啟動了。讓我們回到Bootstrap章節。由於我們沒有應用伺服器為我們提供特殊的EE環境(即為我們實現某些系統行為的環境),所以我們在SE環境中工作。為此,只有範例「範例 269. 應用程式引導的 EntityManagerFactory」適合我們。例如,讓我們這樣做:
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);
    }
}
萬歲,我們的主題已經準備好了。我不想省略這部分,因為...... 在接下來的章節中,建議您了解我們的主題是如何產生的。

尋找可修改的行為

讓我們取代 BigInteger 類型的 count 欄位的初始化並在那裡設定斷點 ( BreakPoint )。插入到所需的行後,可以使用 Ctrl+F8 或透過選單「運行」->「切換行斷點」來完成。然後我們在調試中運行 main 方法(運行 -> 調試):
IntelliJ Idea:反編譯、編譯、替換(或如何修正別人的錯誤)- 3
這是一個有點笨拙的例子,但假設我們想在啟動時更改查詢空間的數量。正如我們所看到的,我們的sqlQuery是NativeQueryImpl。點擊Ctrl+N,輸入班級名稱,然後轉到該班級。這樣當我們去某個班級的時候,就會轉移到這個班級所在的地方,並開啟自動滾動:
IntelliJ Idea:反編譯、編譯、替換(或如何修正別人的錯誤)- 4
讓我們立即註意,Idea 目前不知道在哪裡可以找到程式的原始碼(即原始碼)。於是,她好心地幫我們反編譯了類別文件中的內容:
IntelliJ Idea:反編譯、編譯、替換(或如何修正別人的錯誤)- 5
另請注意,在 IntelliJ Idea 視窗的標題中,寫著 Gradle 為我們儲存工件的位置。現在,讓我們在 Idea 中取得我們的工件所在的路徑:
IntelliJ Idea:反編譯、編譯、替換(或如何修正別人的錯誤)- 6
讓我們使用命令在命令列上進入該目錄cd way to каталогу。我會立即記下:如果可以從原始碼建置項目,那麼最好從原始碼建置。例如,Hibernate原始碼可以在官方網站上找到。最好選擇所需的版本並進行所有更改並使用專案中指定的建置腳本進行組裝。我在文章中提出了最可怕的選擇 - 有一個 jar,但沒有原始碼。注意事項 2:Gradle 可以使用插件取得原始程式碼。有關詳細信息,請參閱如何使用 Gradle 下載 javadoc 和 jar 來源

做出改變

我們需要根據我們要更改的類別所在的套件重新建立目錄結構。在本例中:mkdir org\hibernate\query\internal,之後我們在此目錄中建立一個檔案NativeQueryImpl.java。現在我們打開這個文件,並從 IDE 複製該類別的所有內容(與 Idea 為我們反編譯的內容相同)。更改必要的行。例如:
IntelliJ Idea:反編譯、編譯、替換(或如何修正別人的錯誤)- 7
現在,讓我們編譯該檔案。我們的確是:javac org\hibernate\query\internal\NativeQueryImpl.java。哇,你不能直接拿走它並毫無錯誤地編譯它。我們收到了一堆“找不到符號”錯誤,因為... 可變類別與其他類別相關聯,IntelliJ Idea 通常將其新增至我們的類別路徑。您感受到我們 IDE 的全部用處了嗎?=) 好吧,我們自己加吧,我們也可以做到。讓我們複製以下路徑:
  • [1] - hibernate-core-5.2.17.Final.jar
  • [2] - hibernate-jpa-2.1-api-1.0.0.Final.jar
就像我們所做的那樣:在“外部庫”的“項目”視圖中,我們找到所需的 jar 並單擊Ctrl+Shift+C。現在讓我們建立並執行以下命令: javac -cp [1];[2] org\hibernate\query\internal\NativeQueryImpl.java 結果,java檔案旁邊會出現新的類別文件,需要在jar檔案中更新:
IntelliJ Idea:反編譯、編譯、替換(或如何修正別人的錯誤) - 8
萬歲,現在您可以執行 jar 更新了。我們可以參考官方資料jar uf hibernate-core-5.2.17.Final.jar org\hibernate\query\internal\*.class Open IntelliJ Idea 很可能不允許您更改檔案。因此,在執行jar更新之前,您很可能必須關閉Idea,並在更新後開啟它。之後,您可以重新開啟 IDE 並再次執行 dubug。IDE 重新啟動之間不會重設斷點。因此,程式執行將停止在先前的位置。瞧,我們來看看我們的改變是如何運作的:
IntelliJ Idea:反編譯、編譯、替換(或如何修正別人的錯誤)- 9
偉大的。但這裡出現了一個問題——因為什麼?原因很簡單,當 gradle 建置專案時,它會分析依賴項和儲存庫區塊。Gradle有一定的建置緩存,它位於某個位置(請參閱「如何設定gradle快取位置?」如果快取中沒有依賴項,那麼Gradle會從儲存庫下載它。由於我們更改了快取本身,那麼Gradle 會認為庫在快取中,不會抽出任何東西。但是任何清除快取都會導致我們的更改丟失。而且,除了我們之外,沒有人可以直接去獲取它們。多麼不便是嗎?該怎麼辦。嗯,從存儲庫下載?所以我們需要我們的儲存庫,其中包含首選項和女詩人。這是下一步。

部署儲存庫

有不同的免費解決方案可用於部署儲存庫:其中之一是 Artifactory,另一個是Apache Archive。Artifactory 看起來時尚、時尚、現代,但我在使用它時遇到了困難,我不想正確放置工件並產生錯誤的 Maven 元資料。因此,出乎我意料的是,Apache 版本對我有用。事實證明它並不那麼漂亮,但工作可靠。在下載頁面上,找到獨立版本並解壓縮。他們有自己的“快速入門”。啟動後,需要等待地址http://127.0.0.1:8080/#repositorylist。之後,選擇“上傳工件”:
IntelliJ Idea:反編譯、編譯、替換(或如何修正別人的錯誤) - 10
點擊“開始上傳”,然後點擊“儲存檔案”。此後,將出現綠色成功訊息,並且該工件將在「瀏覽」部分中可用。應該對 jar 和 pom 檔案執行此操作:
IntelliJ Idea:反編譯、編譯、替換(或如何修正別人的錯誤) - 11
這是因為 pom 檔案中指定了額外的 hibernate 依賴項。我們只剩下 1 步了 - 在建置腳本中指定儲存庫:

repositories {
    jcenter()
    maven {
        url "http://127.0.0.1:8080/repository/internal/"
    }
}
因此,我們的 hibernate 版本將變為:compile 'org.hibernate:hibernate-core:5.2.17.Final-JAVARUSH'。就這樣,現在我們的專案使用了我們更正的版本,而不是原來的版本。

結論

看來我們是熟人了。我希望這很有趣。這種「技巧」很少被採用,但如果您的業務需求突然設定了您使用的庫無法滿足的條件,您就知道該怎麼做。是的,這裡有幾個可以透過這種方式修正的範例:
  • 有一個名為 Undertow 的 Web 伺服器。直到一段時間,出現了一個錯誤,當使用代理時,我們無法找到最終用戶的 IP。
  • 目前,WildFly JPA 以某種方式處理,規範未考慮到這一點,因此拋出了異常。而且它是不可配置的。
#維亞切斯拉夫
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION