Agen: Untuk mengawal objek A, objek B baharu dicipta dan objek B melakukan semua operasi objek A sebaliknya, yang dipanggil ejen. Penubuhan sistem agensi melibatkan tiga peranan yang mengambil bahagian: objek sebenar (A), objek proksi (B), dan pelanggan.
Objek proksi (B) memainkan peranan perantara, menghubungkan objek sebenar (A) dan klien Jika dikembangkan lagi, objek proksi boleh melaksanakan logik yang lebih kompleks, seperti kawalan akses untuk objek sebenar.
Keperluan: Antara muka lapisan perniagaan pekerja memerlukan kebenaran pentadbir untuk memanggil simpan dan senarai panggilan tidak memerlukan kebenaran Pengecualian akan dilemparkan apabila membuat panggilan tanpa kebenaran.
/** * 代理接口 */ 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(); } }e
----------------真实对象-------------------- 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)
Apabila dipanggil terus menggunakan objek sebenar EmployeeServiceImpl, kedua-dua senarai dan simpan boleh diakses terus, tetapi ia tidak memenuhi sekatan kebenaran pentadbir pada keperluan. Jika anda menggunakan objek proksi EmployeeProxy, anda boleh melengkapkan keperluan.
Lengkapkan logik proksi dengan terus mencipta kelas objek proksi baharu Kaedah ini dipanggil mod proksi statik.
Mod proksi dinamik yang biasa digunakan dalam Java termasuk proksi dinamik JDK dan proksi dinamik cglib Di sini kita memfokuskan pada proksi dinamik JDK
Ia masih lagi keperluan asal , IEmployeeService EmployeeServiceImpl SessionHolder tidak berubah Tambah pengawal proksi JDK baharu-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); } }
Kelas Apl ujian diubah sedikit:
rreee. kod boleh sama Perbezaan antara keperluan pelaksanaan dan proksi statik ialah kurang objek proksi dicipta. Terdapat soalan pada ketika ini. Tiada objek proksi dibuat. Mengapakah panggilan kelas proksi boleh dilaksanakan? ? Analisis PrinsipPertama, mari kita buat kesimpulan Prinsip pelaksanaan asas proksi dinamik JDK: menggunakan kaedah pelaksanaan antara muka, pada masa jalan, kelas dibina secara dinamik dalam memori, dan kemudian. disusun dan dilaksanakan. Kelas ini digunakan sekali sahaja Apabila JVM dihentikan, kelas proksi akan hilang.Peranan yang mengambil bahagian Untuk memahami prinsip proksi dinamik JDK, anda mesti terlebih dahulu memahami kelas yang terlibat dalam proksi dinamik JDK
InvocationHandler : pengendali invocation kaedah objek sebenar, kaedah invoke terbina dalam, fungsinya: sesuaikan logik proksi untuk objek sebenar
EmployeeInvocationHandler: pengendali invocation kaedah objek sebenar perkhidmatan pekerja , kelas ini mempunyai 3 kegunaan: 1>Tetapkan objek sebenar
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(); } }2> Sesuaikan kaedah proksi untuk melaksanakan logikTambah logik pengesahan kebenaran untuk kaedah simpan objek sebenar
//真实对象-EmployeeServiceImpl private Object target; public EmployeeInvocationHandler(Object target){ this.target = target; }3> Kembalikan Selepas kaedah proksi dilaksanakan, kelas proksi bernama: $ProxyX (dengan X ialah nombor siri, secara lalai kepada 0) dikembalikan secara dinamik oleh JDK itu.
//代理对象控制执行方法 //参数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); }
Proksi: Kelas kawalan proksi dinamik, iaitu kelas induk bagi kelas $ProxyX yang dijana secara dinamik oleh JDK Fungsinya adalah seperti berikut: 1> dengan memanggil Kelas kaedah pembina kelas ProxyBuilder
//获取jvm在内存中生成代理对象 public Object getProxy(){ return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); }2> mengembalikan tika kelas $ProxyX melalui kaedah ProxyInstance baharu
private static Constructor<?> getProxyConstructor(Class<?> caller, ClassLoader loader, Class<?>... interfaces){ return proxyCache.sub(intf).computeIfAbsent( loader, (ld, clv) -> new ProxyBuilder(ld, clv.key()).build() ); }
$Proxy0: Apabila kelas App ialah berjalan, kelas proksi yang dibina secara dinamik oleh JDK diwarisi kepada kelas Proksi
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) { //... }Secara lalai, JVM tidak menyimpan objek kod bait kelas proksi yang dibuat secara dinamik Anda boleh mengkonfigurasi parameter proksi dalam kaedah utama untuk mengekalkan bytecode.
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(); } }Selepas pelaksanaan, Objek bytecode kelas proksi akan dijana dalam direktori akar projek. Untuk memudahkan tafsiran, selepas mengalih keluar beberapa kaedah yang tidak perlu
kelas $Proxy0
//JDK8之前 System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); //JDK8之后 System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
Daripada kod sumber, ciri-ciri $Proxy0:
Rajah berikut menunjukkan semua kelas yang mengambil bahagian dalam proksi dinamik:
Rajah berikut ialah rajah jujukan operasi bagi rajah di atas, ikut sahajaAtas ialah kandungan terperinci Bagaimana untuk melaksanakan proksi dinamik JDK di Java. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!