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?
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?
Câu trả lời:
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 iFoo
nơ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.
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::equals
và Object::toString
sau đó đượ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 InvocationHandler
lớ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.
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:
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:
Để 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.
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.