Rumah  >  Artikel  >  Java  >  Bagaimana untuk melaksanakan proksi dinamik JDK di Java

Bagaimana untuk melaksanakan proksi dinamik JDK di Java

WBOY
WBOYke hadapan
2023-04-30 08:49:06948semak imbas

Konsep

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.

Kes

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.

Proksi statik

/**
 * 代理接口
 */
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 JDK

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 Prinsip

Pertama, 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

Bagaimana untuk melaksanakan proksi dinamik JDK di Java

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 logik

Tambah 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.

Bagaimana untuk melaksanakan proksi dinamik JDK di Java

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:

  • 1> Mewarisi kelas Proksi dan melaksanakan antara muka IEmployeeService

  • 2> Cerminkan kaedah simpan dan senaraikan antara muka IEmployeeService melalui blok statik untuk mendapatkan objek kaedahnya Kaedah

  • 3>

  • 4> Menulis semula kaedah senarai simpan antara muka IEmployeeService bergantung pada atribut h bagi kaedah Proxy.invoke kelas induk

Kebenaran didedahkan

Rajah berikut menunjukkan semua kelas yang mengambil bahagian dalam proksi dinamik:

Bagaimana untuk melaksanakan proksi dinamik JDK di Java

Rajah berikut ialah rajah jujukan operasi bagi rajah di atas, ikut sahaja

Bagaimana untuk melaksanakan proksi dinamik JDK di Java

Atas ialah kandungan terperinci Bagaimana untuk melaksanakan proksi dinamik JDK di Java. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam