エージェント: A オブジェクトを制御するために、新しい B オブジェクトが作成され、代わりに B オブジェクトが A オブジェクトのすべての操作を実行します。これは、オブジェクトと呼ばれます。エージェント。代理店システムの確立には、実オブジェクト (A)、プロキシ オブジェクト (B)、およびクライアントの 3 つの役割が関与します。
プロキシ オブジェクト (B) は、実オブジェクト (A) とクライアントを接続する仲介的な役割を果たし、さらに拡張すると、実オブジェクトへのアクセス制御など、より複雑なロジックを実装できます。
要件: 従業員ビジネス レイヤー インターフェイスでは、save を呼び出すには管理者権限が必要ですが、リストの呼び出しには権限は必要ありません。権限なしで呼び出すと例外がスローされます。
/** * 代理接口 */ public interface IEmployeeService { void save(); void list(); }
/** * 真实对象 */ public class EmployeeServiceImpl implements IEmployeeService { @Override public void save() { System.out.println("EmployeeServiceImpl-正常的save...."); } @Override public void list() { System.out.println("EmployeeServiceImpl-正常的list...."); } }
/** * 模拟当前登录用户对象 */ public class SessionHolder { private static String currentUser; public static String getCurrentUser(){ return currentUser; } public static void setCurrentUser(String currentUser){ SessionHolder.currentUser = currentUser; } }
/** * 代理对象 */ public class EmployeeProxy implements IEmployeeService { //真实对象 private EmployeeServiceImpl employeeService; public EmployeeProxy(EmployeeServiceImpl employeeService){ this.employeeService = employeeService; } @Override public void save() { //权限判断 if("admin".equals(SessionHolder.getCurrentUser())){ employeeService.save(); }else{ throw new RuntimeException("当前非admin用户,不能执行save操作"); } } @Override public void list() { employeeService.list(); } }
public class App { public static void main(String[] args) { System.out.println("----------------真实对象--------------------"); EmployeeServiceImpl employeeService = new EmployeeServiceImpl(); employeeService.list(); employeeService.save(); System.out.println("----------------代理对象--------------------"); SessionHolder.setCurrentUser("dafei"); //设置权限(当前登录用户) EmployeeProxy employeeProxy = new EmployeeProxy(employeeService); employeeProxy.list(); employeeProxy.save(); } }
----------------真实对象-------------------- EmployeeServiceImpl-正常的list.... EmployeeServiceImpl-正常的save.... ----------------代理对象-------------------- EmployeeServiceImpl-正常的list.... Exception in thread "main" java.lang.RuntimeException: 当前非admin用户,不能执行save操作 at com.langfeiyes.pattern.proxy.demo.EmployeeProxy.save(EmployeeProxy.java:20) at com.langfeiyes.pattern.proxy.demo.App.main(App.java:16)
実際のオブジェクト EmployeeServiceImpl を使用して直接呼び出す場合、リストと保存の両方に直接アクセスできますが、要件に関する管理者権限の制限を満たしていません。プロキシ オブジェクト EmployeeProxy を使用すると、要件を満たすことができます。
プロキシ オブジェクトの新しいクラスを直接作成して、プロキシ ロジックを完成します。この方法は、静的プロキシ モードと呼ばれます。
Java で一般的に使用される動的プロキシ モードには、JDK 動的プロキシと cglib 動的プロキシが含まれます。ここでは、JDK 動的プロキシに焦点を当てます
依然として元の需要、以前の IEmployeeService EmployeeServiceImpl SessionHolder は変更されていません。新しい JDK プロキシ コントローラー - EmployeeInvocationHandler
/** * jdk动态代理控制类,由它牵头代理类获取,代理方法的执行 */ public class EmployeeInvocationHandler implements InvocationHandler { //真实对象-EmployeeServiceImpl private Object target; public EmployeeInvocationHandler(Object target){ this.target = target; } //获取jvm在内存中生成代理对象 public Object getProxy(){ return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } //代理对象控制执行方法 //参数1:代理对象 //参数2:真实对象的方法(使用方式得到方法对象) //参数3:真实对象方法参数列表 //此处是代理对象对外暴露的可编辑的方法处理场所,代理对象每调用一个次方法,就会执行一次invoke @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String name = method.getName(); if("save".equals(name) && !"admin".equals(SessionHolder.getCurrentUser())){ throw new RuntimeException("当前非admin用户,不能执行save操作"); } return method.invoke(target, args); } }
テスト アプリ クラスはわずかに変更されています:
public class App { public static void main(String[] args) { System.out.println("----------------真实对象--------------------"); EmployeeServiceImpl employeeService = new EmployeeServiceImpl(); employeeService.list(); employeeService.save(); System.out.println("----------------代理对象--------------------"); SessionHolder.setCurrentUser("dafei"); EmployeeInvocationHandler handler = new EmployeeInvocationHandler(employeeService); IEmployeeService proxy = (IEmployeeService) handler.getProxy(); proxy.list(); proxy.save(); } }
上記のコードは次のようになります。同じです。要件の実装と静的プロキシの違いは、作成されるプロキシ オブジェクトの量が少ないことです。ここで質問がありますが、プロキシ オブジェクトは作成されていませんが、なぜプロキシ クラス呼び出しが実装できるのでしょうか? ?
まず、JDK 動的プロキシの基本的な実装原理で結論を述べましょう。実行時にインターフェイス実装を使用して、クラスがメモリ内に動的に構築され、コンパイルされて実行されます。このクラスは 1 回限りの使用であり、JVM を停止するとプロキシ クラスは消えます。
参加するロール JDK 動的プロキシの原理を理解するには、まず JDK 動的プロキシに関連するクラスを理解する必要があります。
##InvocationHandler : 実オブジェクト メソッド呼び出しプロセッサ、組み込み呼び出しメソッド、その機能: 実オブジェクトのプロキシ ロジックをカスタマイズ
EmployeeInvocationHandler: 従業員サービスの実オブジェクト メソッド呼び出しプロセッサ、このタイプには 3 つの用途があります: 1>実際のオブジェクトを設定します
//真实对象-EmployeeServiceImpl private Object target; public EmployeeInvocationHandler(Object target){ this.target = target; }
//代理对象控制执行方法 //参数1:代理对象 //参数2:真实对象的方法(使用方式得到方法对象) //参数3:真实对象方法参数列表 //此处是代理对象对外暴露的可编辑的方法处理场所,代理对象每调用一个次方法,就会执行一次invoke @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String name = method.getName(); if("save".equals(name) && !"admin".equals(SessionHolder.getCurrentUser())){ throw new RuntimeException("当前非admin用户,不能执行save操作"); } return method.invoke(target, args); }
//获取jvm在内存中生成代理对象 public Object getProxy(){ return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); }
Proxy: 動的プロキシ コントロール クラスは、JDK によって動的に生成される $ProxyX クラスの親クラスであり、その機能は次のとおりです: 1> 呼び出してプロキシ オブジェクトを構築しますProxyBuilder クラス ビルダー メソッド Class
private static Constructor<?> getProxyConstructor(Class<?> caller, ClassLoader loader, Class<?>... interfaces){ return proxyCache.sub(intf).computeIfAbsent( loader, (ld, clv) -> new ProxyBuilder(ld, clv.key()).build() ); }
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) { //... }
$Proxy0 を通じて $ProxyX クラスのインスタンスを返します: App クラスが実行されているとき、 JDK によって動的に構築されたプロキシ クラスは、プロキシ クラスに継承されます
public class App { public static void main(String[] args) { //System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"); System.out.println("----------------真实对象--------------------"); EmployeeServiceImpl employeeService = new EmployeeServiceImpl(); employeeService.list(); employeeService.save(); System.out.println("----------------代理对象--------------------"); SessionHolder.setCurrentUser("dafei"); EmployeeInvocationHandler handler = new EmployeeInvocationHandler(employeeService); IEmployeeService proxy = (IEmployeeService) handler.getProxy(); proxy.list(); proxy.save(); } }
//JDK8之前 System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); //JDK8之后 System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
#解釈を容易にするために、いくつかの不要なメソッドを削除した後、
$Proxy0 class
次の図は、操作シーケンスです。上の図の図に従ってください 以上がJava で JDK 動的プロキシを実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。public class $Proxy0 extends Proxy implements IEmployeeService {
private static Method m4;
private static Method m3;
static {
try {
m4 = Class.forName("com.langfeiyes.proxy.demo.IEmployeeService")
.getMethod("save");
m3 = Class.forName("com.langfeiyes.proxy.demo.IEmployeeService")
.getMethod("list");
} catch (Exception e) {
e.printStackTrace();
}
}
public $Proxy0(InvocationHandler var1) throws Throwable {
super(var1);
}
public final void save() throws Throwable {
super.h.invoke(this, m4, (Object[])null);
}
public final void list() throws Throwable{
super.h.invoke(this, m3, (Object[])null);
}
}
#1>Proxy クラスを継承し、IEmployeeService インターフェイスを実装します
次の図は、動的プロキシに参加しているすべてのクラスを示しています。