Witaj Amigo.

- Cześć Risza.

- Dziś opowiem o nowym i bardzo ciekawym temacie -  dynamiczne proxy .

W Javie istnieje kilka sposobów na zmianę funkcjonalności żądanej klasy...

Metoda pierwsza. Dziedzictwo

Najłatwiejszym sposobem zmiany zachowania jakiejś klasy jest utworzenie nowej klasy, odziedziczenie jej z oryginalnej (klasy bazowej) i nadpisanie jej metod. Następnie zamiast obiektów klasy oryginalnej użyj obiektów klasy pochodnej. Przykład:

Reader reader = new UserCustomReader();

Metoda druga. Korzystanie z klasy opakowania (Wrapper).

Przykładem takiej klasy jest BufferedReader . Po pierwsze, jest dziedziczony z Reader , więc można go używać zamiast tego. Po drugie, przekierowuje wszystkie wywołania do oryginalnego obiektu Reader , który musi zostać przekazany w konstruktorze do obiektu BufferedReader. Przykład:

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

Metoda trzecia. Tworzenie dynamicznego serwera proxy (Proxy).

Dynamiczny serwer proxy — 1

Java ma specjalną klasę (java.lang.reflect.Proxy), za pomocą której można faktycznie skonstruować obiekt w czasie wykonywania (dynamicznie) bez tworzenia dla niego osobnej klasy.

Odbywa się to bardzo prosto:

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

- Teraz to coś nowego!

— Ale przecież nie potrzebujemy tylko obiektu bez metod. Konieczne jest, aby ten obiekt miał metody i robiły to, czego potrzebujemy. W tym celu Java wykorzystuje specjalny interfejs InvocationHandler , za pomocą którego można przechwycić wszystkie wywołania metod adresowane do obiektu proxy. obiekt proxy można utworzyć tylko za pomocą interfejsów.

Invoke to standardowa nazwa metody/klasy, której głównym zadaniem jest po prostu wywołanie jakiejś metody.

Handler to standardowa nazwa klasy obsługującej jakieś zdarzenie. Na przykład procedura obsługi kliknięcia myszą nosiłaby nazwę MouseClickHandler i tak dalej.

Interfejs InvocationHandler ma pojedynczą metodę invoke, do której kierowane są wszystkie wywołania obiektu proxy . Przykład:

Kod
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;
 }
}

Podczas wywoływania metody czytelnika . close () , wywołana zostanie metoda invoke , a na ekranie wyświetlony zostanie komunikat „yes!”.

- Tj. zadeklarowaliśmy klasę CustomInvocationHandler, zaimplementowaliśmy w niej interfejs InvocationHandler i jego metodę invoke . Metoda invoke po wywołaniu wyświetla napis „yes!” Następnie utworzyliśmy obiekt typu CustomInvocationHandler i przekazaliśmy go do metody newProxyInstance podczas tworzenia obiektu proxy.

- Tak to prawda.

Jest to bardzo potężne narzędzie, zwykle tworzenie takich proxy służy do imitowania obiektów z programów, które fizycznie działają na innym komputerze. Lub do kontroli dostępu

- w tej metodzie możesz sprawdzić uprawnienia bieżącego użytkownika, obsłużyć błędy, logować błędy i wiele więcej.

Oto przykład, w którym metoda invoke wywołuje również metody na oryginalnym obiekcie:

Kod
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!");
  }

  // это вызов метода close у obiektа readerOriginal
  // Nazwa метода и описание его параметров хранится в переменной method
  return method.invoke(readerOriginal, args);
 }
}

Ten przykład ma dwie cechy.

Najpierw „oryginalny” obiekt Reader jest przekazywany do konstruktora , do którego odwołanie jest przechowywane w obiekcie CustomInvocationHandler .

Po drugie, w metodzie invoke ponownie wywołujemy tę samą metodę, ale na „oryginalnym” obiekcie.

- Tak. Te. ta ostatnia linia jest wywołaniem tej samej metody, ale już na oryginalnym obiekcie:

return method.invoke(readerOriginal, args);

- Tak.

Nie powiedziałbym, że jest to zbyt oczywiste, ale nadal zrozumiałe. Wygląda na to.

- Świetnie. A oto coś innego. Musisz przekazać trochę więcej informacji o usłudze do metody newProxyInstance, aby utworzyć obiekt proxy. Ale ponieważ Ponieważ nie tworzymy potwornych obiektów zastępczych, informacje te można łatwo uzyskać z samej oryginalnej klasy.

Oto przykład dla Ciebie:

Kod
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;
 }
}

- Tak. ClassLoader i lista interfejsów. To coś z Reflection, prawda?

- Tak.

- Jasne. Cóż, myślę, że mogę stworzyć prymitywny prosty obiekt proxy, jeśli kiedykolwiek będę go potrzebować.

- Zapytaj Diego