Statischer Proxy, die Proxy-Klasse und die Proxy-Klasse implementieren dieselbe Schnittstelle, und die Proxy-Klasse enthält auch die Proxy-Klasse. Ein Verweis auf die Proxy-Klasse. Wenn wir die Methode der Proxy-Klasse aufrufen müssen, können wir dies durch Aufrufen der Methode der Proxy-Klasse tun.
Beispiel: Gehen Sie davon aus, dass die Aufgabe der Führungskraft darin besteht, Besprechungen abzuhalten und Mitarbeiter zu bewerten.
Definieren Sie zuerst eine Schnittstelle:
package com.sharpcj; public interface IWork { void meeting(); int evaluate(String name); }
Dann definieren Sie die Leader-Klasse:
package com.sharpcj; import java.util.Random; public class Leader implements IWork { @Override public void meeting() { System.out.println("领导早上要组织会议"); } @Override public int evaluate(String name) { int score = new Random(System.currentTimeMillis()).nextInt(20) + 80; System.out.println(String.format("领导给%s的考评为%s分", name, score)); return score; } }# ?? Ergebnis: # 🎜🎜#
Dieser Code ist sehr einfach, wenn wir die Besprechungsmethode der Klasse Sekretär
aufrufen Rufen Sie die Meeting-Methode der Leader
-Klasse auf. Zuvor haben wir auch diese Methode erweitert. An diesem Punkt könnten einige Leute verwirrt sein. Dies scheint ein bisschen wie ein Dekorationsmuster zu sein. Was ist los? Der Unterschied zwischen dem Dekoratormuster und dem Dekoratormuster
Tatsächlich gibt es immer noch viele Unterschiede zwischen dem Dekoratormuster und dem Proxy-Muster. Das Decorator-Muster konzentriert sich auf das dynamische Hinzufügen von Methoden zu einem Objekt, während sich das Proxy-Muster auf die Steuerung des Zugriffs auf das Objekt konzentriert. Mit anderen Worten: Mithilfe des Proxy-Musters kann eine Proxy-Klasse die spezifischen Informationen eines Objekts vor ihren Clients verbergen. Daher erstellen wir bei Verwendung des Proxy-Musters häufig eine Instanz eines Objekts in einer Proxy-Klasse. Und wenn wir das Dekoratormuster verwenden, besteht unser üblicher Ansatz darin, das Originalobjekt als Parameter an den Konstruktor des Dekorators zu übergeben.
Wir können diese Unterschiede in einem anderen Satz zusammenfassen: Mithilfe des Proxy-Musters wird die Beziehung zwischen dem Proxy und dem realen Objekt normalerweise zur Kompilierungszeit bestimmt, während der Dekorateur dies rekursiv konstruieren kann Laufzeit. Schauen wir uns zunächst den Unterschied zwischen den beiden UML-Klassendiagrammen an: Secretary
类的 meeting 方法时,我们调用了Leader
类的 meeting 的方法,在此之前,我们还扩充了该方法。这时有的人可能有疑惑了,这看起来有点是装饰者模式了。这到底怎么回事?
实际上,在装饰器模式和代理模式之间还是有很多差别的。装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。
我们可以用另外一句话来总结这些差别:使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。
先看看两者的 UML 类图区别:
代理模式:
装饰者模式:
两者伪代码:
代理模式:
package com.sharpcj; public class Secretary implements IWork { private Leader mLeader; public Secretary(Leader mLeader) { this.mLeader = mLeader; } @Override public void meeting() { System.out.println("秘书先给老板准备材料"); mLeader.metting(); } @Override public int evaluate(String name) { return mLeader.evaluate(name); } }
装饰者模式;
package com.sharpcj; public class TestApp { public static void main(String[] args) { Leader leader = new Leader(); Secretary secretary = new Secretary(leader); secretary.meeting(); secretary.evaluate("Joy"); } }
其实代理模式和装饰者模式侧重点不一样,代理模式重点在于明确了被代理的类。如上例中,秘书很明确要代理的是的领导。而装饰者模式侧重于拓展类的方法,装饰类持有的实现Component接口的类的对象不是固定的,也就是说,装饰类可以根据在调用时传入的参数,装饰任意一个实现了 Component 接口的类。
动态代理的根据实现方式的不同可以分为 JDK 动态代理和 CGlib 动态代理。
JDK 动态代理:利用反射机制生成一个实现代理接口的类,在调用具体方法前调用InvokeHandler来处理。
CGlib 动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。
还是以上面的例子为例:
首先,定一个类实现 InvocationHandler
接口,并实现 invoke 方法:
Interface Subject { void doAction() } public class RealSubject implements Subject{ @Override public void doAction() {}; } public class Proxy implements Subject{ private RealSubject realSubject; public Proxy(RealSubject realSubject) { //关系在编译时确定 this.realSubject = realSubject; } @Override public void doAction() { …. realSubject.doAction(); …. } }
然后通过 Proxy.newProxyInstance()
Agent-Modus:
Decorator-Modus:
Pseudocode für beide:
Agentenmodus:
Interface Component { void doAction() } public class ConcreteComponent implement Component { @Override public void doAction() {}; } public class Decorator implements Component { private Component component; public Decorator(Component component) { //关系在编译时确定 this.component = new component; } public void doAction() { …. component.doAction(); …. } }Decorator-Modus;
package com.sharpcj; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class WorkInvocationHandler implements InvocationHandler { private Object object; public WorkInvocationHandler(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("object: " + object.getClass().getSimpleName()); System.out.println("proxy: " + proxy.getClass().getSimpleName()); if ("meeting".equals(method.getName())) { System.out.println("代理先准备会议材料..."); return method.invoke(object, args); } else if ("evaluate".equals(method.getName())) { if(args[0] instanceof String) { if ("James".equals(args[0])) { System.out.println("James 犯过错误,所以考评分数较低..."); return 70; } } return method.invoke(object, args); } return null; } }Tatsächlich liegen der Proxy-Modus und der Decorator-Modus im Mittelpunkt Anders Der Fokus des Proxy-Modus liegt auf der Angabe der Klasse, die vertreten wird. Wie im obigen Beispiel möchte die Sekretärin eindeutig im Namen des Leiters handeln. Das Dekorationsmuster konzentriert sich auf die Erweiterung der Klassenmethode, die die von der dekorierten Klasse gehaltene Komponentenschnittstelle implementiert. Das heißt, die dekorierte Klasse kann jedes Objekt dekorieren, das die Komponentenschnittstelle implementiert die beim Aufruf der Klasse übergebenen Parameter. #🎜🎜# 2. Dynamischer Proxy #🎜🎜##🎜🎜#Dynamischer Proxy kann basierend auf unterschiedlichen Implementierungsmethoden in dynamischen JDK-Proxy und dynamischen CGlib-Proxy unterteilt werden. #🎜🎜##🎜🎜##🎜🎜#Dynamischer JDK-Proxy: #🎜🎜#Verwenden Sie den Reflexionsmechanismus, um eine Klasse zu generieren, die die Proxy-Schnittstelle implementiert, und rufen Sie InvokeHandler auf, bevor Sie die spezifische Methode aufrufen.
zu implementieren InvocationHandler-Schnittstelle und implementieren Sie die Aufrufmethode: #🎜🎜#<pre class="brush:java;">package com.sharpcj;
import java.lang.reflect.Proxy;
public class TestApp {
public static void main(String[] args) {
/*Leader leader = new Leader();
Secretary secretary = new Secretary(leader);
secretary.meeting();
secretary.evaluate("Joy");*/
Leader leader = new Leader();
IWork proxy = (IWork) Proxy.newProxyInstance(Leader.class.getClassLoader(),
new Class[]{IWork.class}, new WorkInvocationHandler(leader));
proxy.meeting();
proxy.evaluate("Joy");
proxy.evaluate("James");
}
}</pre>#🎜🎜# Erstellen Sie dann ein Proxy-Objekt über die Methode <code>Proxy.newProxyInstance()
: #🎜🎜#package com.sharpcj.proxytest; public interface IWork { IWork work(String subject); }#🎜🎜 ##🎜🎜 #Ausgabeergebnis: #🎜🎜##🎜🎜##🎜🎜##🎜🎜##🎜🎜##🎜🎜#Wir sehen, dass wir durch die WorkInvocationHandler-Klasse auch die Implementierung der Methoden von vertreten können Tatsächlich implementieren wir eine beliebige Methode, aber wenn wir das Proxy-Objekt erstellen, übergeben wir die Iwork-Schnittstelle und das Leader-Klassenobjekt. #🎜🎜##🎜🎜##🎜🎜##🎜🎜#Hier muss Folgendes beachtet werden: #🎜🎜#Der erste Parameter-Proxy in der Aufrufmethode der InvocationHandler-Schnittstelle ist nicht das Objekt unserer aufrufenden Methode dieser Parameter Was ist das? Im Code habe ich speziell einen entsprechenden Ausdruck hinzugefügt, um den Klassennamen des Proxys auszudrucken. Tatsächlich ist Proxy das Proxy-Objekt selbst. Seine Bedeutung besteht darin, dass wir das Proxy-Objekt in der Aufrufmethode zurückgeben und dann kontinuierlich aufrufen können. #🎜🎜##🎜🎜##🎜🎜#Sehen Sie sich das folgende Beispiel an: #🎜🎜#
package com.sharpcj.proxytest; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class WorkInvocationHandler implements InvocationHandler { private Object object; public WorkInvocationHandler(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("work".equals(method.getName())){ System.out.println("--- work: " + args[0]); return proxy; } return null; } }
package com.sharpcj.proxytest; import java.lang.reflect.Proxy; public class TestApp { public static void main(String[] args) { IWork worker = (IWork) Proxy.newProxyInstance(IWork.class.getClassLoader(), new Class[]{IWork.class}, new WorkInvocationHandler(new IWork() { @Override public IWork work(String subject) { return null; } })); worker.work("AAA").work("BBB").work("CCC"); } }
... dependencies { // 引入 cglib 库 compile 'cglib:cglib:3.1' testCompile group: 'junit', name: 'junit', version: '4.12' } ...#🎜🎜##🎜🎜#Das Ergebnis ist wie folgt: #🎜🎜##🎜🎜##🎜🎜## 🎜🎜##🎜 🎜#
首先添加 cglib 依赖
build.gradle 文件:
... dependencies { // 引入 cglib 库 compile 'cglib:cglib:3.1' testCompile group: 'junit', name: 'junit', version: '4.12' } ...
前面说了,cglib 针对类进行代理,我们以上面的 Leader 类为例,先创建一个类实现 MethodInterceptor
接口:
package com.sharpcj; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class LeaderMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { if ("meeting".equals(method.getName())) { System.out.println("代理先准备会议材料..."); return methodProxy.invokeSuper(o, objects); } else if ("evaluate".equals(method.getName())) { if(objects[0] instanceof String) { if ("James".equals(objects[0])) { System.out.println("James 犯过错误,所以考评分数较低..."); return 70; } } return methodProxy.invokeSuper(o, objects); } return null; } }
测试代码:
package com.sharpcj; import net.sf.cglib.core.DebuggingClassWriter; import net.sf.cglib.proxy.Enhancer; import java.lang.reflect.Proxy; public class TestApp { public static void main(String[] args) { // System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\temp\code"); //保存生成的 class 文件 Enhancer enhancer = new Enhancer(); // 通过CGLIB动态代理获取代理对象的过程 enhancer.setSuperclass(Leader.class); // 设置enhancer对象的父类 enhancer.setCallback(new LeaderMethodInterceptor()); // 设置enhancer的回调对象 Leader proxy= (Leader)enhancer.create(); // 创建代理对象 // 通过代理对象调用目标方法 proxy.meeting(); proxy.evaluate("Joy"); proxy.evaluate("James"); } }
结果如下:
MethodInterceptor
接口只有一个 intercept
方法,这个方法有4个参数:
1)obj表示增强的对象,即实现这个接口类的一个对象;
2)method表示要被拦截的方法;
3)args表示要被拦截方法的参数;
4)proxy表示要触发父类的方法对象;
需要注意的是:实际调用是 methodProxy.invokeSuper()
, 如果使用 invoke()
方法,则需要传入被代理的类对象,否则出现死循环,造成 stackOverflow 。
Das obige ist der detaillierte Inhalt vonBeispielanalyse eines dynamischen Java-Proxys und eines statischen Proxys. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!