JavaRush /Blog Java /Random-VI /Ý tưởng IntelliJ: Dịch ngược, Biên dịch, Thay thế (hoặc c...
Viacheslav
Mức độ

Ý tưởng IntelliJ: Dịch ngược, Biên dịch, Thay thế (hoặc cách sửa lỗi của người khác)

Xuất bản trong nhóm
“Đừng phát minh lại cái bánh xe” là một trong những nguyên tắc chính để làm việc thành công và hiệu quả. Nhưng phải làm gì khi bạn không muốn tự chế lại bánh xe của mình mà vô lăng của người khác lại bị cong và bánh xe lại vuông? Đánh giá này nhằm mục đích cung cấp phần giới thiệu ngắn gọn nhất có thể về kỹ thuật sửa thư viện của người khác "như là phương sách cuối cùng" và cách mở rộng kỹ thuật này sang máy tính của bạn.
IntelliJ Idea: Decompilation, Compilation, Substitution (hoặc cách sửa lỗi của người khác) - 1

Giới thiệu

Tất cả chúng ta đều sử dụng công cụ này hay công cụ khác. Nhưng đôi khi các công cụ đó không hoàn toàn phù hợp hoặc có lỗi. Nhờ các tính năng của ngôn ngữ Java, chúng ta có thể điều chỉnh hoạt động của các công cụ khi cần. Thật tốt khi chúng tôi đóng góp cho dự án và gửi yêu cầu kéo (bạn có thể đọc thêm tại đây: “ GitHub - Đóng góp cho dự án ”). Nhưng chúng có thể không được chấp nhận ngay lập tức, hoặc thậm chí có thể không được chấp nhận. Nhưng đối với nhu cầu của dự án thì điều đó là cần thiết ngay bây giờ. Và ở đây, tôi hy vọng bài viết này sẽ hiển thị các công cụ có sẵn cho chúng ta với tư cách là nhà phát triển. Chúng ta sẽ cần thực hiện các bước sau mà chúng ta sẽ nói đến:
  • Chuẩn bị một ứng dụng thử nghiệm chẳng hạn (dùng ví dụ về dự án Hibernate)
  • Tìm một vị trí có thể thay đổi
  • Thực hiện một sự thay đổi
  • Triển khai kho lưu trữ
Tất cả các bước dưới đây được đưa ra cho hệ điều hành Windows, nhưng có các bước tương tự cho hệ thống nix. Vì vậy, bạn có thể lặp lại chúng nếu cần thiết.

Chuẩn bị môn học

Vì vậy, chúng ta cần một dự án thử nghiệm. Hibernate là lý tưởng cho chúng tôi, bởi vì... đó là "phong cách, thời trang, hiện đại." Tôi sẽ không đi sâu vào chi tiết vì... Bài viết không nói về Hibernate. Chúng tôi sẽ làm mọi thứ một cách nhanh chóng và chính xác. Và chúng tôi, giống như những nhà phát triển phù hợp, sẽ sử dụng hệ thống xây dựng. Ví dụ, Gradle cũng phù hợp với chúng ta, phải được cài đặt cho bài viết này ( https://gradle.org/install/ ). Đầu tiên chúng ta cần tạo một dự án. Maven có nguyên mẫu cho việc này và Gradle có một plugin đặc biệt cho việc này: Gradle Init . Vì vậy, hãy mở dòng lệnh theo bất kỳ cách nào bạn biết. Tạo một thư mục cho dự án, vào đó và thực hiện lệnh:

mkdir javarush 
cd javarush 
gradle init --type java-application
IntelliJ Idea: Decompilation, Compilation, Substitution (hoặc cách sửa lỗi của người khác) - 2
Trước khi nhập dự án, hãy thực hiện một số thay đổi đối với tệp mô tả cách xây dựng. Tệp này được gọi là tập lệnh xây dựng và được đặt tên là build.gradle. Nó nằm trong thư mục mà chúng ta đã thực thi gradle init. Do đó, chúng ta chỉ cần mở nó (ví dụ: trong Windows bằng lệnh start build.gradle). Chúng tôi tìm thấy khối “ phụ thuộc ” ở đó, tức là. sự phụ thuộc. Tất cả các lọ của bên thứ ba mà chúng tôi sẽ sử dụng đều được mô tả ở đây. Bây giờ chúng ta cần hiểu những gì cần mô tả ở đây. Chúng ta hãy truy cập trang web Hibernate ( http://hibernate.org/ ). Chúng tôi quan tâm đến Hibernate ORM . Chúng tôi cần bản phát hành mới nhất. Trong menu bên trái có một tiểu mục “Bản phát hành”. Chọn “ổn định mới nhất”. Cuộn xuống và tìm “Triển khai cốt lõi (bao gồm JPA)”. Trước đây cần phải kết nối hỗ trợ JPA riêng biệt nhưng giờ đây mọi thứ đã trở nên đơn giản hơn và chỉ cần một dependency là đủ. Chúng ta cũng sẽ cần làm việc với cơ sở dữ liệu bằng Hibernate. Để làm điều này, hãy sử dụng tùy chọn đơn giản nhất - Cơ sở dữ liệu H2 . Sự lựa chọn đã được thực hiện, đây là sự phụ thuộc của chúng tôi:

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'
}
Tuyệt vời, tiếp theo là gì? Chúng ta cần cấu hình Hibernate. Hibernate có một " Hướng dẫn bắt đầu ", nhưng nó ngu ngốc và gây trở ngại hơn là giúp đỡ. Vì vậy, hãy đi thẳng vào “ Hướng dẫn sử dụng ” như đúng người. Trong mục lục chúng ta thấy phần “ Bootstrap ”, tạm dịch là “Bootstrapping”. Đúng thứ bạn cần. Có rất nhiều từ thông minh được viết ở đó, nhưng vấn đề là phải có thư mục META-INF trên đường dẫn lớp và phải có tệp Persence.xml. Theo tiêu chuẩn, đường dẫn lớp chứa thư mục “tài nguyên”. Do đó, chúng tôi tạo thư mục được chỉ định: mkdir src\main\resources\META-INF Tạo tệp Persence.xml ở đó và mở nó. Trong tài liệu có một ví dụ “Ví dụ 268. Tệp cấu hình META-INF/persistence.xml”, từ đó chúng tôi sẽ lấy nội dung và chèn nó vào tệp Persistence.xml. Tiếp theo, khởi chạy IDE và nhập dự án đã tạo của chúng tôi vào đó. Bây giờ chúng ta cần lưu một cái gì đó vào cơ sở dữ liệu. Đây là một cái gì đó được gọi là một thực thể. Các thực thể đại diện cho một cái gì đó từ cái gọi là mô hình miền. Và trong mục lục, lạ thay, chúng ta thấy “ 2. Mô hình miền ”. Chúng ta xem phần văn bản và xem trong chương “2.1.Các kiểu ánh xạ” một ví dụ đơn giản về một thực thể. Chúng ta hãy tự mình rút ngắn nó một chút:
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;
    }
}
Bây giờ chúng ta có một lớp đại diện cho một thực thể. Hãy quay lại Persence.xml và sửa một chỗ ở đó: Khi được chỉ định, classchúng tôi sẽ chỉ ra lớp của mình entity.Contact. Tuyệt vời, tất cả những gì còn lại là khởi động. Hãy quay lại chương Bootstrap . Vì chúng tôi không có máy chủ ứng dụng sẽ cung cấp cho chúng tôi môi trường EE đặc biệt (tức là môi trường thực hiện hành vi hệ thống nhất định cho chúng tôi), nên chúng tôi làm việc trong môi trường SE. Đối với điều này, chỉ có ví dụ “Ví dụ 269. Ứng dụng được khởi động EntityManagerFactory” là phù hợp với chúng tôi. Ví dụ: hãy làm điều này:
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);
    }
}
Hoan hô, chủ đề của chúng ta đã sẵn sàng. Tôi không muốn bỏ qua phần này , bởi vì... Đối với các chương tiếp theo, bạn nên hiểu chủ đề của chúng ta đã diễn ra như thế nào.

Tìm kiếm hành vi có thể sửa đổi

Hãy thay thế việc khởi tạo trường đếm loại BigInteger và đặt điểm dừng ở đó ( BreakPoint ). Sau khi chèn vào dòng mong muốn, việc này có thể được thực hiện bằng cách sử dụng Ctrl+F8 hoặc thông qua menu Run -> Toggle Line Breakpoint. Sau đó, chúng tôi chạy phương thức chính của mình trong quá trình gỡ lỗi (Chạy -> Gỡ lỗi):
IntelliJ Idea: Decompilation, Compilation, Substitution (hoặc cách sửa lỗi của người khác) - 3
Một ví dụ hơi vụng về, nhưng giả sử chúng ta muốn thay đổi số lượng không gian truy vấn khi khởi động. Như chúng ta có thể thấy, sqlQuery của chúng ta là NativeQueryImpl. Bấm vào Ctrl+N, viết tên lớp và đi tới lớp đó. Để khi vào lớp nào chúng ta sẽ được chuyển tới nơi có lớp này và bật autoscroll:
IntelliJ Idea: Decompilation, Compilation, Substitution (hoặc cách sửa lỗi của người khác) - 4
Chúng tôi xin lưu ý ngay rằng Idea hiện không biết tìm mã nguồn của chương trình ở đâu (nghĩa là mã nguồn). Vì vậy, cô đã vui lòng giải mã nội dung từ tệp lớp cho chúng tôi:
IntelliJ Idea: Decompilation, Compilation, Substitution (hoặc cách sửa lỗi của người khác) - 5
Cũng lưu ý rằng trong tiêu đề của cửa sổ IntelliJ Idea có ghi nơi Gradle lưu tạo phẩm cho chúng ta. Bây giờ, hãy cùng Idea tìm đường dẫn nơi đặt hiện vật của chúng ta:
IntelliJ Idea: Decompilation, Compilation, Substitution (hoặc cách sửa lỗi của người khác) - 6
Chúng ta hãy đi đến thư mục này trên dòng lệnh bằng lệnh cd way to каталогу. Tôi sẽ lưu ý ngay: nếu có thể xây dựng dự án từ nguồn thì tốt hơn nên xây dựng từ nguồn. Ví dụ: mã nguồn Hibernate có sẵn trên trang web chính thức. Tốt hơn là bạn nên chọn phiên bản mong muốn và thực hiện tất cả các thay đổi ở đó và lắp ráp bằng cách sử dụng các tập lệnh xây dựng được chỉ định trong dự án. Tôi trình bày trong bài viết phương án khủng khiếp nhất - có jar, nhưng không có mã nguồn. Và lưu ý số 2: Gradle có thể lấy mã nguồn bằng plugin. Xem Cách tải xuống javadocs và nguồn cho jar bằng Gradle để biết chi tiết .

Thực hiện một sự thay đổi

Chúng ta cần tạo lại cấu trúc thư mục theo gói mà lớp chúng ta đang thay đổi nằm trong gói nào. Trong trường hợp này: mkdir org\hibernate\query\internal, sau đó chúng tôi tạo một tệp trong thư mục này NativeQueryImpl.java. Bây giờ chúng ta mở tệp này và sao chép tất cả nội dung của lớp từ IDE ở đó (chính nội dung mà Idea đã dịch ngược cho chúng ta). Thay đổi các dòng cần thiết. Ví dụ:
IntelliJ Idea: Decompilation, Compilation, Substitution (hoặc cách sửa lỗi của người khác) - 7
Bây giờ hãy biên dịch tập tin. Chúng tôi làm: javac org\hibernate\query\internal\NativeQueryImpl.java. Chà, bạn không thể lấy nó và biên dịch nó mà không gặp lỗi. Chúng tôi đã nhận được một loạt lỗi Không thể tìm thấy Biểu tượng, bởi vì... lớp có thể thay đổi được gắn với các lớp khác, mà IntelliJ Idea thường thêm vào đường dẫn lớp cho chúng ta. Bạn có cảm thấy tất cả sự hữu ích của IDE của chúng tôi không? =) Thôi chúng ta tự thêm vào nhé, chúng ta cũng làm được. Hãy sao chép đường dẫn cho:
  • [1] - ngủ đông-core-5.2.17.Final.jar
  • [2] - ngủ đông-jpa-2.1-api-1.0.0.Final.jar
Giống như chúng tôi đã làm: Trong chế độ xem “Dự án” trong “Thư viện bên ngoài”, chúng tôi tìm thấy tệp jar cần thiết và nhấp vào Ctrl+Shift+C. Bây giờ chúng ta hãy tạo và thực thi lệnh sau: javac -cp [1];[2] org\hibernate\query\internal\NativeQueryImpl.java Kết quả là các tệp lớp mới sẽ xuất hiện bên cạnh tệp java, tệp này cần được cập nhật trong tệp jar:
IntelliJ Idea: Decompilation, Compilation, Substitution (hoặc cách sửa lỗi của người khác) - 8
Hoan hô, bây giờ bạn có thể thực hiện cập nhật jar. Chúng tôi có thể được hướng dẫn bởi các tài liệu chính thức : jar uf hibernate-core-5.2.17.Final.jar org\hibernate\query\internal\*.class Open IntelliJ Idea rất có thể sẽ không cho phép bạn thay đổi tệp. Do đó, trước khi thực hiện cập nhật jar, rất có thể bạn sẽ phải đóng Idea và sau khi cập nhật, hãy mở nó. Sau đó, bạn có thể mở lại IDE và chạy lại dubug. Điểm ngắt không được đặt lại giữa các lần khởi động lại IDE. Do đó, việc thực thi chương trình sẽ dừng lại ở vị trí trước đó. Thì đấy, chúng ta thấy những thay đổi của mình hoạt động như thế nào:
IntelliJ Idea: Decompilation, Compilation, Substitution (hoặc cách sửa lỗi của người khác) - 9
Tuyệt vời. Nhưng ở đây câu hỏi được đặt ra - do cái gì? Đơn giản là vì khi gradle xây dựng một dự án, nó sẽ phân tích các khối phụ thuộc và kho lưu trữ. Gradle có một bộ đệm xây dựng nhất định, nằm ở một vị trí nhất định (xem “ Cách đặt vị trí bộ đệm của lớp? ” Nếu không có phần phụ thuộc nào trong bộ đệm thì Gradle sẽ tải xuống từ kho lưu trữ. Vì chúng tôi đã thay đổi jar trong cache, thì Gradle nghĩ rằng thư viện nằm trong cache và không bơm ra bất cứ thứ gì. Nhưng bất kỳ việc xóa cache nào cũng sẽ dẫn đến những thay đổi của chúng ta bị mất. Hơn nữa, không ai ngoài chúng ta có thể đi lấy chúng. Thật bất tiện biết bao , phải không? Phải làm gì. Hmm, tải xuống từ kho lưu trữ? Vì vậy, chúng tôi cần kho lưu trữ của mình, với các tùy chọn và nữ thi sĩ. Đây là bước tiếp theo.

Triển khai kho lưu trữ

Có nhiều giải pháp miễn phí khác nhau để triển khai kho lưu trữ của bạn: một trong số đó là Artifactory và giải pháp còn lại là Apache Archive . Artifactory trông thời trang, phong cách, hiện đại, nhưng tôi gặp khó khăn với nó, tôi không muốn đặt các tạo tác một cách chính xác và tạo ra siêu dữ liệu maven sai sót. Vì vậy, thật bất ngờ đối với tôi, phiên bản Apache lại có tác dụng với tôi. Hóa ra nó không đẹp lắm nhưng nó hoạt động đáng tin cậy. Trên trang tải xuống , hãy tìm phiên bản Standalone và giải nén nó. Họ có " Bắt đầu nhanh " của riêng mình. Sau khi khởi chạy, bạn cần đợi đến địa chỉ http://127.0.0.1:8080/#repositorylist. Sau đó, chọn "Tải lên hiện vật":
IntelliJ Idea: Decompilation, Compilation, Substitution (hoặc cách sửa lỗi của người khác) - 10
Nhấp vào "Bắt đầu tải lên" rồi nhấp vào "Lưu tệp". Sau đó, thông báo thành công màu xanh lục sẽ xuất hiện và hiện vật sẽ có sẵn trong phần “Duyệt qua”. Điều này nên được thực hiện đối với các tệp jar và pom:
IntelliJ Idea: Decompilation, Compilation, Substitution (hoặc cách sửa lỗi của người khác) - 11
Điều này là do các phụ thuộc ngủ đông bổ sung được chỉ định trong tệp pom. Và chúng ta chỉ còn 1 bước - chỉ định kho lưu trữ trong tập lệnh xây dựng của chúng ta:

repositories {
    jcenter()
    maven {
        url "http://127.0.0.1:8080/repository/internal/"
    }
}
Và theo đó, phiên bản ngủ đông của chúng tôi sẽ trở thành: compile 'org.hibernate:hibernate-core:5.2.17.Final-JAVARUSH'. Chỉ vậy thôi, bây giờ dự án của chúng tôi sử dụng phiên bản chúng tôi đã sửa chứ không phải phiên bản gốc.

Phần kết luận

Có vẻ như chúng ta đã quen nhau. Tôi hy vọng nó thú vị. Những “thủ thuật” như vậy hiếm khi được thực hiện, nhưng nếu đột nhiên yêu cầu nghiệp vụ của bạn đặt ra những điều kiện mà thư viện bạn sử dụng không thể đáp ứng thì bạn biết phải làm gì. Và vâng, đây là một vài ví dụ có thể được sửa theo cách này:
  • Có một máy chủ web tên là Undertow. Cho đến một thời điểm, có một lỗi xảy ra khi sử dụng proxy không cho phép chúng tôi tìm ra IP của người dùng cuối.
  • Hiện tại, WildFly JPA đã xử lý theo một cách nhất định một thời điểm không được đặc tả tính đến, vì điều này đã đưa ra các Ngoại lệ. Và nó không thể cấu hình được.
#Viacheslav
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION