Sự khác biệt giữa proxy động JDK và CGLib là gì?


147

Trong trường hợp Mẫu thiết kế proxy , sự khác biệt giữa Proxy động của JDK và API tạo mã động của bên thứ ba như CGLib là gì?

Sự khác biệt giữa việc sử dụng cả hai phương pháp và khi nào nên thích cái này hơn cái kia?


3
Lấy mã ở đây: < gist.github.com/ksauzz/1563486 >. Trong cglib, bạn có thể tạo cả proxy lớp và proxy giao diện. Spring sử dụng CGlib theo mặc định trong khi AspectJ sử dụng proxy Java. Cũng đọc điều này: jnb.ociweb.com/jnb/jnbNov2005.html ;)
Subhadeep Ray

Câu trả lời:


185

JDK Dynamic proxy chỉ có thể proxy theo giao diện (vì vậy lớp mục tiêu của bạn cần triển khai một giao diện, sau đó cũng được lớp proxy thực hiện).

CGLIB (và javassist) có thể tạo proxy bằng cách phân lớp. Trong kịch bản này, proxy trở thành một lớp con của lớp đích. Không cần giao diện.

Vì vậy, proxy động Java có thể ủy quyền: public class Foo implements iFoonơi CGLIB có thể ủy quyền:public class Foo

BIÊN TẬP:

Tôi nên đề cập rằng vì javassist và CGLIB sử dụng proxy bằng cách phân lớp, đây là lý do bạn không thể khai báo các phương thức cuối cùng hoặc làm cho lớp cuối cùng khi sử dụng các khung dựa vào điều này. Điều đó sẽ ngăn các thư viện này cho phép phân lớp của bạn và ghi đè các phương thức của bạn.


cảm ơn..!! nhưng sẽ rất hữu ích nếu bạn có thể đưa cho tôi một mã ví dụ (hoặc Liên kết) để minh họa việc sử dụng của một người so với người khác trong một số trường hợp .. !!!
KDjava

1
Lưu ý rằng proxy của JDK thực sự đang lừa đảo proxy cho IFoo chứ không phải cho bất kỳ loại Foo nào. Đó là một sự khác biệt khá quan trọng. Ngoài ra, proxy cglib là các lớp con đầy đủ - hãy tận dụng điều đó! Sử dụng các bộ lọc để chỉ các phương thức proxy mà bạn quan tâm và sử dụng trực tiếp lớp được tạo.
lscoughlin

9
Cũng cần lưu ý rằng việc tạo lớp con CGLib đòi hỏi phải biết đủ về siêu lớp để có thể gọi đúng hàm tạo với các đối số đúng. Không giống như proxy dựa trên giao diện không quan tâm đến các nhà xây dựng. Điều này làm cho việc làm việc với các proxy CGLib ít "tự động" hơn các proxy của JDK. Một điểm khác biệt là trong chi phí "chồng". Một proxy JDK luôn phải chịu thêm các khung stack cho mỗi cuộc gọi trong khi CGLib có thể không tốn bất kỳ khung stack nào. Điều này càng trở nên phù hợp khi ứng dụng càng phức tạp (vì ngăn xếp càng lớn thì càng tiêu thụ nhiều luồng bộ nhớ).
Ray

1
cglib không thể ủy quyền các phương thức cuối cùng, nhưng sẽ không ném ngoại lệ gist.github.com/mhewedy/7345403cfa52e6f47563f8a204ec0e80
Muhammad Hewedy

Có, CGLIB đơn giản bỏ qua các phương pháp cuối cùng.
yashjain12yj

56

Sự khác biệt về chức năng

  • Các proxy JDK cho phép thực hiện bất kỳ bộ giao diện nào trong khi phân lớp Object. Bất kỳ phương thức giao diện, cộng Object::hashCode, Object::equalsObject::toStringsau đó được chuyển tiếp đến một InvocationHandler. Ngoài ra, giao diện thư viện tiêu chuẩn java.lang.reflect.Proxyđược thực hiện.

  • cglib cho phép bạn thực hiện bất kỳ bộ giao diện nào trong khi phân lớp bất kỳ lớp không phải là cuối cùng. Ngoài ra, các phương thức có thể được ghi đè tùy ý, tức là không phải tất cả các phương thức không trừu tượng cần phải bị chặn. Hơn nữa, có nhiều cách khác nhau để thực hiện một phương pháp. Nó cũng cung cấp một InvocationHandlerlớp (trong một gói khác), nhưng nó cũng cho phép gọi các phương thức siêu bằng cách sử dụng các thiết bị chặn nâng cao hơn như ví dụ a MethodInterceptor. Hơn nữa, cglib có thể cải thiện hiệu suất bằng các can thiệp chuyên dụng như FixedValue. Tôi đã từng viết một bản tóm tắt về các thiết bị đánh chặn khác nhau cho cglib .

Hiệu suất khác biệt

Các proxy của JDK được triển khai khá ngây thơ chỉ với một bộ điều phối chặn InvocationHandler. Điều này đòi hỏi một phương thức ảo gửi đến một triển khai không thể luôn luôn được nội tuyến. Cglib cho phép tạo mã byte chuyên dụng đôi khi có thể cải thiện hiệu suất. Dưới đây là một số so sánh để thực hiện một giao diện với 18 phương thức còn sơ khai:

            cglib                   JDK proxy
creation    804.000     (1.899)     973.650     (1.624)
invocation    0.002     (0.000)       0.005     (0.000)

Thời gian được ghi nhận bằng nano giây với độ lệch chuẩn trong niềng răng. Bạn có thể tìm thêm chi tiết về điểm chuẩn trong hướng dẫn của Byte Buddy , trong đó Byte Buddy là một thay thế hiện đại hơn cho cglib. Ngoài ra, lưu ý rằng cglib không còn được phát triển tích cực.


2
Tại sao tài liệu mùa xuân ủng hộ việc ủy ​​quyền JDK trên cglib với các lợi ích hiệu năng sau này? docs.spring.io/spring/docs/2.5.x/reference/ từ
P4ndaman

2
Cglib là một phụ thuộc bên ngoài và hiện không được hỗ trợ. Dựa vào phần mềm của bên thứ ba luôn là một canh bạc vì vậy tốt nhất là khi càng ít người càng tin tưởng vào nó.
Rafael Winterhalter

Trong blog của bạn, bạn nói: "Tuy nhiên, bạn nên cẩn thận khi gọi một phương thức trên đối tượng proxy đi kèm với phương thức invocationHandler # invoke. Tất cả các cuộc gọi trên phương thức này sẽ được gửi cùng một InvocationHandler và do đó có thể dẫn đến một vòng lặp vô tận . " Ý anh là gì?
Koray Tugay

Nếu bạn gọi một phương thức trên đối tượng proxy, bất kỳ cuộc gọi nào cũng được chuyển qua, trình xử lý lời gọi của chúng tôi. Nếu bất kỳ trình xử lý gọi nào gọi các đại biểu đến một cuộc gọi đến đối tượng, đệ quy được đề cập sẽ xảy ra.
Rafael Winterhalter

Xin chào Rafael, tin nhắn không liên quan đến câu trả lời của bạn, tôi đang nói với bạn về một chỉnh sửa được thực hiện 5 năm trước . Vì cglib rõ ràng vẫn còn cam kết vào năm 2019 và không cho thấy bất kỳ sự phát triển bị bắt giữ nào trong readme của nó, tôi đã xóa tuyên bố của bạn khỏi đoạn trích thẻ. Vui lòng cải thiện mô tả / trích đoạn thẻ nếu có bất cứ điều gì liên quan để đề cập.
Cœur

28

Proxy động: Triển khai động các giao diện trong thời gian chạy bằng cách sử dụng API phản chiếu JDK .

Ví dụ: Spring sử dụng proxy động cho các giao dịch như sau:

nhập mô tả hình ảnh ở đây

Các proxy được tạo ra trên đầu đậu. Nó thêm hành vi xuyên quốc gia cho đậu. Ở đây, proxy tạo động khi chạy bằng cách sử dụng API phản chiếu JDK.

Khi một ứng dụng bị dừng, proxy sẽ bị hủy và chúng ta sẽ chỉ có giao diện và bean trên hệ thống tệp.


Trong ví dụ trên chúng ta có giao diện. Nhưng trong hầu hết việc thực hiện giao diện là không tốt nhất. Vì vậy, bean không thực hiện một giao diện, trong trường hợp đó chúng ta sử dụng tính kế thừa:

nhập mô tả hình ảnh ở đây

Để tạo ra các proxy như vậy, Spring sử dụng thư viện của bên thứ ba có tên CGLib .

CGLib ( C ode G enulation Lib rary) được xây dựng dựa trên ASM , điều này chủ yếu được sử dụng để tạo proxy mở rộng proxy và thêm hành vi bean trong các phương thức proxy.

Ví dụ cho JDK Dynamic proxy và CGLib

Tham khảo mùa xuân


5

Từ tài liệu mùa xuân :

Spring AOP sử dụng proxy động JDK hoặc CGLIB để tạo proxy cho một đối tượng đích nhất định. (Proxy động JDK được ưa thích bất cứ khi nào bạn có lựa chọn).

Nếu đối tượng đích được ủy nhiệm thực hiện ít nhất một giao diện thì proxy động JDK sẽ được sử dụng. Tất cả các giao diện được thực hiện bởi loại mục tiêu sẽ được ủy quyền. Nếu đối tượng đích không thực hiện bất kỳ giao diện nào thì proxy CGLIB sẽ được tạo.

Nếu bạn muốn buộc sử dụng ủy quyền CGLIB (ví dụ: ủy quyền mọi phương thức được xác định cho đối tượng đích, không chỉ các phương thức được triển khai bởi các giao diện của nó), bạn có thể làm như vậy. Tuy nhiên, có một số vấn đề cần xem xét:

phương pháp cuối cùng không thể được khuyên, vì chúng không thể được ghi đè.

Bạn sẽ cần các nhị phân CGLIB 2 trên đường dẫn lớp của bạn, trong khi các proxy động có sẵn với JDK. Spring sẽ tự động cảnh báo bạn khi cần CGLIB và các lớp thư viện CGLIB không được tìm thấy trên đường dẫn lớp.

Hàm tạo của đối tượng proxy của bạn sẽ được gọi hai lần. Đây là kết quả tự nhiên của mô hình proxy CGLIB, theo đó một lớp con được tạo cho mỗi đối tượng được ủy quyền. Đối với mỗi phiên bản proxy, hai đối tượng được tạo: đối tượng ủy quyền thực tế và một thể hiện của lớp con thực hiện lời khuyên. Hành vi này không được thể hiện khi sử dụng proxy JDK. Thông thường, việc gọi hàm tạo của loại proxy hai lần, không phải là một vấn đề, vì thường chỉ có các bài tập diễn ra và không có logic thực sự nào được thực hiện trong hàm tạo.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.