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).
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:
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:
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:
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
GO TO FULL VERSION