Was ist ein dynamischer Proxy?
Das Proxy-Objekt ( RealSubject ) kann zur Laufzeit dynamisch geändert werden, die zu steuernde Schnittstelle ( Abstract Subject (Subject) ) kann zur Laufzeit geändert werden und die Steuerungsmethode kann ebenfalls dynamisch geändert werden, wodurch eine sehr flexible dynamische Agentur erreicht wird Beziehung.
Java bietet eine Implementierungsmethode für dynamische Proxys.
In der java.lang.reflect-Bibliothek von Java werden die folgenden drei Klassen zur Implementierung dynamischer Proxys bereitgestellt.
Proxy (Klasse): Diese Klasse repräsentiert Proxy-Einstellungen, normalerweise Typ (http, Socken) und Socket-Adresse. Proxy
ist ein unveränderliches Objekt.
InvocationHadler (Schnittstelle): Kann als Aufruf-Handler bezeichnet werden und ist die Schnittstelle, die vom Aufruf- Handler der Proxy-Instanz implementiert wird. Jeder Agenteninstanz ist ein Anrufhandler zugeordnet. Wenn eine Methode auf einer Proxy-Instanz aufgerufen wird, wird der Methodenaufruf codiert und an die Methode ihres Aufruf-Handlers weitergeleitet invoke
.
Methode (Klasse): Stellt Informationen zu einer einzelnen Methode in einer Klasse oder Schnittstelle bereit (und wie auf die Methode zugegriffen wird). Die reflektierten Methoden können Klassenmethoden oder Instanzmethoden (einschließlich abstrakter Methoden) sein.
Wie implementiert man also einen dynamischen Proxy? Hauptsächlich durch die folgenden vier Schritte:
1) Definieren Sie ein abstraktes Thema
2) Definieren Sie ein echtes Thema
Die ersten beiden sind die gleichen wie statische Proxys. Der Schlüssel liegt darin, wie diese dynamische Agentenrolle implementiert wird .
3) InvocationHadler (Schnittstelle) muss implementiert werden . Die Implementierungsklasse dieser Schnittstelle vervollständigt die eigentliche Arbeit der dynamischen Proxy-Klasse.
In der Aufrufmethode dieser Schnittstelle wird der Reflexionsmechanismus von Java verwendet, um das eigentliche Thema dynamisch über die Methode (Klasse) aufzurufen .
4) Auf dem Client (oder der Implementierungsklasse von InvocationHadler) wird beim Aufruf der statischen Methode newProxyInstance von Proxy (Klasse) ein Proxy-Objekt zurückgegeben (dies ist die dynamische Proxy-Klasse, die eigentlich keine eigentliche Arbeit leistet) .
Lassen Sie uns den Implementierungsmechanismus des dynamischen Proxys durch Code verstehen.
Nehmen Sie auch das Beispiel des Autokaufs im statischen Proxy-Modus (Proxy) – Hinweise zu Java-Designmustern für Anfänger .
Wenn Sie im wirklichen Leben ein Auto von einem Verkäufer kaufen ( abstraktes Subjekt) , kaufen Sie normalerweise kein Auto direkt vom Hersteller ( reales Subjekt) . Sie können ein Auto in einem 4S-Geschäft kaufen ( Proxy-Rolle ) , wo Sie können Weitere Informationen erhalten. Viele Dienstleistungen.
Der Java-Code lautet wie folgt:
Abstraktes Thema:
/**
* 抽象主题(Subject)
*
* 汽车的销售商
*
*/
public interface CarSeller {
/*
* 销售汽车
*/
public Object sellCars(int type);
}
Echtes Thema:
/**
* 真实主题(Real Subject)角色
* 奥迪厂家
*
*/
public class AudiCarFactory implements CarSeller {
/*
* 实现了抽象主题(Subject)角色的方法
*/
public Object sellCars(int type) {
System.out.println("奥迪工厂出售汽车。");
if(type == 1){
return "AudiA6";
}else{
return "AudiA8";
}
}
}
Aufrufprozessor (Implementierungsklasse von InvocationHadler):
/*
* 实际提供代理商服务的一个类
* (很多地方把这个类称为代理类,个人觉得不准确。
* 它是一个会完成动态代理类的实际工作的类。)
*/
public class CarProxyInvocationHandler implements InvocationHandler {
Object carSeller = null;
public CarProxyInvocationHandler(Object carSeller){
this.carSeller = carSeller;
}
/*
* 代理角色提供服务的真正方法。
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
public Object invoke(Object proxy, Method method, Object[] args){
Object result = null;
try {
serveBeforeSell();
result = method.invoke(carSeller,args);
serveAfterSell();
} catch (Exception e) {
System.exit(1);
}
// System.out.println("result:" + result +";" + args[0]);
return result;
}
protected void serveBeforeSell(){
System.out.println("汽车代理商为客户提供了一些售前服务");
}
protected void serveAfterSell(){
System.out.println("汽车代理商为客户提供了一些售后服务");
}
}
Klient:
/**
* 客户端调用
*/
public class Customer {
public static void main(String[] args) {
// 创建真实主题对象
CarSeller carSeller = new AudiCarFactory();
// 通过动态的方式,得到了真正的代理角色对象。
// 在运行时,真实主题对象,代理角色以及实际完成代理操作的类被关联起来了。
CarSeller carProxy = (CarSeller) Proxy.newProxyInstance(carSeller.getClass()
.getClassLoader(), carSeller.getClass().getInterfaces(),
new CarProxyInvocationHandler(carSeller));
// 由代理商销售汽车
Object car = carProxy.sellCars(2);
System.out.println("顾客从代理商那里买了一辆" + car);
}
}
Operationsergebnis:
汽车代理商为客户提供了一些售前服务
奥迪工厂出售汽车。
汽车代理商为客户提供了一些售后服务
顾客从代理商那里买了一辆AudiA8
Wenn ich zum ersten Mal die Implementierung des dynamischen Java-Proxys sehe, gibt es mehrere Stellen, die immer schwer zu verstehen sind:
1) Wofür werden die drei Parameter in der newProxyInstance-Methode der Proxy-Klasse verwendet?
Der erste Parameter ist der Klassenlader , der vom echten Themenobjekt verwendet wird, um die erstellte dynamische Proxy-Klasse zu laden.
Der zweite Parameter, alle Schnittstellen, die vom realen Themenobjekt implementiert werden ----- Für die dynamische Proxy-Klasse werden alle Schnittstellen des realen Themas implementiert.
Der dritte Parameter ist eine Instanz des Aufrufhandlers (die Implementierungsklasse von InvocationHadler) . Da die dynamische Proxy-Klasse die Proxy-Klasse erbt, verwenden Sie ihre InvocationHadler-Implementierungsklasse , um aus der Unterklasse (normalerweise der dynamischen Proxy-Klasse) eine Funktion zu erstellen, die eine neue Proxy
Instanz erstellt . (Siehe die JDK-Hilfedokumentation)
Auf diese Weise wird die dynamische Proxy-Klasse $Proxy0, die wir erhalten, als Proxy eines echten Themas bezeichnet, und der Proxy erbt Proxy (Klasse) , implementiert auch die abstrakte Themenschnittstelle und ist an die InvocationHadler- Schnittstelle gebunden.
Da die dynamische Proxy-Klasse $Proxy0 zur Laufzeit erstellt wird, können wir den Code dieser Klasse nicht direkt sehen. Sie können auf den Link unten verweisen, um mehr zu erfahren.
2) Wofür werden die drei Parameter in der Aufrufmethode der InvocationHadler-Schnittstelle verwendet?
Der erste Parameter ist das dynamische Proxy-Objekt $Proxy0, das normalerweise nicht verwendet wird. Aufgrund dieses Parameters können wir jedoch auf die Methoden dieses Objekts zugreifen.
Der zweite Parameter ist die vom Proxy-Objekt ( reales Subjekt ) definierte Methode.
Der dritte Parameter ist der Parameter der Methode, die durch das Proxy-Objekt ( reales Subjekt ) definiert wird.
Der zweite und dritte Parameter können den Reflexionsmechanismus der Methodenklasse verwenden, um das Themenobjekt dynamisch aufzurufen.
3) Wie rufen Sie beim Aufrufen der Methode der dynamischen Proxy-Klasse die Aufrufmethode der InvocationHadler-Schnittstelle auf?
Anhand des Codes der dynamischen Proxy-Klasse $Proxy0 können wir sehen, dass die Aufrufmethode von InvocationHadler in der Methode der abstrakten Themenschnittstelle aufgerufen wird, die sie implementiert. Und verwenden Sie den Zeiger this der dynamischen Proxy-Klasse $Proxy0 selbst als ersten Parameter der Aufrufmethode.
Nachdem Sie diese drei Punkte verstanden haben, können Sie in Kombination mit dem obigen Code den Funktionsmechanismus des dynamischen Proxys verstehen:
1) Erstellen Sie ein echtes Themenobjekt (AudiCarFactory) , das die abstrakte Themenklasse (CarSeller) erbt.
2) Verwenden Sie die newProxyInstance-Methode der Proxy-Klasse, um eine dynamische Proxy-Klasse $Proxy0 abzurufen und die CarProxyInvocationHandler-Instanz zu binden.
3) Die dynamische Proxy-Klasse $Proxy0 erbt die Proxy-Klasse und implementiert alle Schnittstellen des realen Subjektobjekts (in diesem Fall nur CarSeller);
4) In der Methode (sellCars) der abstrakten Themenklasse (CarSeller), die von der dynamischen Proxy-Klasse $Proxy0 implementiert wird , wird die Aufrufmethode von CarProxyInvocationHandler aufgerufen;
5) In der Aufrufmethode von CarProxyInvocationHandler wird die Methode „sellCars“ des eigentlichen Themas (AudiCarFactory) aufgerufen und vor oder nach „sellCars“ werden zusätzliche Vorgänge hinzugefügt.
Nachfolgend finden Sie das Zeitdiagramm, das klarer sein sollte:
Eine dynamische Proxy-Klasse ist eine Klasse, die die Schnittstelle angeben kann, die sie zur Laufzeit und beim Erstellen der Klasse implementiert. Jede Instanz der Proxy-Klasse verfügt über ein entsprechendes InvocationHandler- Objekt.
Um die Zusammenfassung eines Internetnutzers zu zitieren:
Der sogenannte dynamische Proxy (Dynamic Proxy) ist eine Klasse: Es handelt sich um eine zur Laufzeit generierte Klasse. Beim Generieren müssen Sie ihm eine Reihe von Schnittstellen bereitstellen, und dann gibt die Klasse bekannt, dass sie diese Schnittstelle implementiert. Sie können natürlich eine Instanz dieser Klasse als jede dieser Schnittstellen verwenden. Natürlich ist dieser dynamische Proxy (Dynamic Proxy) tatsächlich ein Proxy (Proxy) und wird keine wesentliche Arbeit für Sie erledigen. Bei der Generierung seiner Instanz müssen Sie einen Handler bereitstellen, der die eigentliche Arbeit übernimmt.