"Chào, Amigo."

"Xin chào, Rishi."

"Hôm nay tôi sẽ giải thích cho các bạn một chủ đề mới và rất thú vị: proxy động" .

"Java có một số cách để thay đổi chức năng của một lớp cụ thể..."

"Phương pháp đầu tiên là kế thừa."

"Cách dễ nhất để thay đổi hành vi của một lớp là tạo một lớp mới kế thừa lớp gốc (cơ sở) và ghi đè các phương thức của nó. Sau đó, thay vì sử dụng lớp gốc, bạn sử dụng lớp dẫn xuất. Ví dụ:"

Reader reader = new UserCustomReader();

"Phương pháp thứ hai là sử dụng một lớp bao bọc."

" BufferedReader là một ví dụ về loại lớp này. Đầu tiên, nó kế thừa Reader . Nói cách khác, nó có thể được sử dụng thay cho Reader. Thứ hai, nó chuyển hướng tất cả các cuộc gọi đến đối tượng Reader ban đầu , đối tượng này phải được chuyển đến hàm tạo của đối tượng BufferedReader . Ví dụ:"

Reader readerOriginal = new UserCustomReader();
Reader reader = new BufferedReader(readerOriginal);

"Phương pháp thứ ba là tạo một proxy động (Proxy)."

"Có một lớp đặc biệt trong Java (java.lang.reflect.Proxy) thực sự cho phép bạn xây dựng một đối tượng trong quá trình thực thi chương trình (một cách linh hoạt) mà không cần tạo một lớp riêng cho nó."

"Điều này rất dễ thực hiện:"

Reader reader = (Reader)Proxy.newProxyInstance();

"Đó đã là một cái gì đó mới đối với tôi!"

"Nhưng tất nhiên, chúng ta không cần một đối tượng không có phương thức. Chúng ta cần đối tượng có các phương thức và chúng ta cần chúng để thực hiện những gì chúng ta muốn. Java sử dụng một giao diện đặc biệt cho giao diện này được gọi là InvocationHandler, giao diện này có thể chặn tất cả các lệnh gọi phương thức được liên kết với đối tượng proxy. Chỉ có thể tạo đối tượng proxy bằng giao diện."

" Gọi – là tên tiêu chuẩn cho một phương thức hoặc lớp có nhiệm vụ chính là gọi một số phương thức."

" Handler – là tên tiêu chuẩn cho một lớp xử lý một số sự kiện. Ví dụ: một lớp xử lý nhấp chuột sẽ được gọi là MouseClickHandler, v.v."

"Giao diện InvocationHandler có một phương thức gọi duy nhất mà tất cả các lệnh gọi đến đối tượng proxy đều được hướng tới . Ví dụ:"

Mã số
Reader reader = (Reader)Proxy.newProxyInstance(new CustomInvocationHandler());
reader.close();
class CustomInvocationHandler implements InvocationHandler
{
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 {
  System.out.println("yes!");
  return null;
 }
}

"Khi gọi phương thức . close () của trình đọc , phương thức gọi sẽ được gọi và màn hình sẽ hiển thị 'có!'"

"Vì vậy, chúng tôi đã khai báo một CustomInvocationHandler, lớp và triển khai giao diện InvocationHandler cũng như phương thức gọi của nó . Khi phương thức gọi được gọi, nó sẽ hiển thị 'có!'. Sau đó, chúng tôi đã tạo một đối tượng CustomInvocationHandler và chuyển nó sang phương thức newProxyInstance khi tạo một đối tượng ủy quyền."

"Vâng, tất cả đều đúng."

"Đây là một công cụ rất mạnh. Thông thường, những proxy này được tạo ra để mô phỏng các đối tượng trong các chương trình đang chạy trên máy tính khác.  Hoặc để kiểm soát quyền truy cập."

"Bạn có thể kiểm tra quyền của người dùng hiện tại, xử lý lỗi, ghi nhật ký lỗi và hơn thế nữa trong phương pháp này."

"Đây là một ví dụ trong đó phương thức gọi cũng gọi các phương thức của đối tượng ban đầu:"

Mã số
Reader original = new UserCustomReader();

Reader reader = (Reader)Proxy.newProxyInstance(new CustomInvocationHandler(original));
reader.close();
class CustomInvocationHandler implements InvocationHandler
{
 private Reader readerOriginal;

 CustomInvocationHandler(Reader readerOriginal)
 {
  this.readerOriginal = readerOriginal;
 }

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 {
  if (method.getName().equals("close"))
  {
   System.out.println("Reader closed!");
  }

  // This calls the readerOriginal object's close method.
  // The method's name and a description of its parameters are stored in the method variable.
  return method.invoke(readerOriginal, args);
 }
}

"Ví dụ này có hai tính năng đặc biệt."

"Đầu tiên, đối tượng Reader «bản gốc» được chuyển đến hàm tạo và một tham chiếu đến nó được lưu bên trong CustomInvocationHandler . "

"Thứ hai, chúng tôi gọi lại cùng một phương thức này trong phương thức gọi, nhưng lần này là trên đối tượng «gốc»."

"À. Nói cách khác, dòng cuối cùng này gọi cùng một phương thức, nhưng trên đối tượng ban đầu:"

return method.invoke(readerOriginal, args);

"Chuẩn rồi."

"Tôi sẽ không nói rằng nó quá rõ ràng, nhưng nó vẫn có thể hiểu được. Hoặc có vẻ như vậy."

"Tuyệt. Còn một điều nữa. Trong phương thức newProxyInstance, bạn cần truyền thêm một ít thông tin quản lý để tạo một đối tượng proxy. Nhưng vì chúng ta không tạo các đối tượng proxy khổng lồ, thông tin này có thể dễ dàng lấy được từ chính lớp ban đầu. "

"Đây là một ví dụ khác:"

Mã số
Reader original = new UserCustomReader();

ClassLoader classLoader = original.getClass().getClassLoader();
Class<?>[] interfaces = original.getClass().getInterfaces();
CustomInvocationHandler invocationHandler = new CustomInvocationHandler(original);

Reader reader = (Reader)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
class CustomInvocationHandler implements InvocationHandler
{
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 {
  return null;
 }
}

"À. ClassLoader và danh sách các giao diện. Đây là thứ từ Reflection phải không?"

"Chuẩn rồi."

"Tôi hiểu rồi. Chà, tôi nghĩ tôi có thể tạo một đối tượng proxy nguyên thủy, siêu đơn giản nếu tôi cần."

"Vậy thì đi kiểm tra với Diego."