Rumah  >  Artikel  >  Java  >  Contoh analisis proksi dinamik Java dan proksi statik

Contoh analisis proksi dinamik Java dan proksi statik

PHPz
PHPzke hadapan
2023-05-06 23:46:06886semak imbas

1. Proksi statik

Penggunaan proksi statik

Proksi statik, kelas proksi dan kelas proksi melaksanakan antara muka yang sama dan kelas proksi juga memegang rujukan kepada kelas proksi . Dengan cara ini, apabila kita perlu memanggil kaedah kelas proksi, kita boleh melakukannya dengan memanggil kaedah kelas proksi.

Contoh: Andaikan tugas pemimpin ialah mengadakan mesyuarat dan menilai pekerja.

Mula-mula tentukan antara muka:

package com.sharpcj;
public interface IWork {
    void meeting();
    int evaluate(String name);
}

Kemudian tentukan kelas ketua:

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;
    }
}

Kelas setiausaha:

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);
    }
}

Kelas ujian:

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");
    }
}

Hasil pelaksanaan:

Contoh analisis proksi dinamik Java dan proksi statik

Ini kod Ianya sangat mudah apabila memanggil kaedah mesyuarat kelas Secretary, kami memanggil kaedah mesyuarat kelas Leader Sebelum itu, kami juga mengembangkan kaedah ini. Pada ketika ini, sesetengah orang mungkin keliru. Ini kelihatan seperti corak penghias. Apa yang berlaku?

Perbezaan dari Corak Penghias

Sebenarnya, terdapat banyak perbezaan antara Corak Penghias dan Corak Proksi. Corak Penghias memfokuskan pada penambahan kaedah secara dinamik pada objek, manakala corak Proksi memfokus pada mengawal akses kepada objek. Dalam erti kata lain, menggunakan corak proksi, kelas proksi boleh menyembunyikan maklumat khusus objek daripada pelanggannya. Oleh itu, apabila menggunakan corak proksi, kita sering mencipta contoh objek dalam kelas proksi. Dan, apabila kita menggunakan corak penghias, pendekatan biasa kita adalah untuk menghantar objek asal sebagai parameter kepada pembina penghias.

Kita boleh meringkaskan perbezaan ini dalam ayat lain: menggunakan corak proksi, perhubungan antara proksi dan objek sebenar biasanya ditentukan pada masa penyusunan, manakala penghias boleh menjadi rekursif pada masa larian Struktur penutup tanah .

Mari kita lihat dahulu perbezaan antara rajah kelas UML kedua-duanya:

Mod ejen:

Contoh analisis proksi dinamik Java dan proksi statik

Mod penghias:

Contoh analisis proksi dinamik Java dan proksi statik

Kedua-dua pseudokod:

Mod proksi:

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();
             ….
       }
}

Corak penghias;

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();
             ….
       }
}

Malah, fokus mod proksi dan mod penghias adalah berbeza diproksikan. Seperti contoh di atas, setiausaha jelas mahu memangku ketua. Corak penghias memfokuskan pada memanjangkan kaedah kelas Objek kelas yang melaksanakan antara muka Komponen yang dipegang oleh kelas yang dihias tidak tetap, iaitu kelas yang dihias boleh menghiasi mana-mana yang melaksanakan antara muka Komponen parameter diluluskan semasa memanggil kelas.

2. Proksi dinamik

Proksi dinamik boleh dibahagikan kepada proksi dinamik JDK dan proksi dinamik CGlib berdasarkan kaedah pelaksanaan yang berbeza.

Proksi dinamik JDK: Gunakan mekanisme pantulan untuk menjana kelas yang melaksanakan antara muka proksi dan panggil InvokeHandler sebelum memanggil kaedah tertentu.
Proksi dinamik CGlib: Menggunakan pakej sumber terbuka ASM (pustaka penyuntingan bytecode Java sumber terbuka, bytecode operasi), muatkan fail kelas kelas objek proksi dan jananya dengan mengubah suai subkelas bytecodenya untuk dikendalikan .
Perbezaan: Ejen JDK hanya boleh menjana ejen untuk kelas yang melaksanakan antara muka; CGlib melaksanakan ejen untuk kelas, menjana subkelas untuk kelas yang ditentukan, dan mengatasi kaedah di dalamnya kelas Kaedah pelaksanaan tidak boleh memproksi kelas diubah suai akhir.

Proksi dinamik JDK

Mari kita ambil contoh di atas:
Mula-mula, tentukan kelas untuk melaksanakan antara muka InvocationHandler dan laksanakan kaedah invoke:

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;
    }
}

Kemudian buat objek proksi melalui kaedah Proxy.newProxyInstance():

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");
    }
}

Hasil keluaran:

Contoh analisis proksi dinamik Java dan proksi statik

Kita lihat , Melalui kelas WorkInvocationHandler, kami juga boleh membuat proksi pelaksanaan kaedah kelas Leader Sebenarnya, apa yang kami laksanakan ialah pelaksanaan sebarang kaedah, tetapi apabila kami mencipta objek proksi, kami lulus dalam antara muka Iwork dan Leader. objek kelas.

Apa yang perlu diperhatikan di sini ialah: Proksi parameter pertama dalam kaedah invoke antara muka InvocationHandler bukanlah objek kaedah panggilan kami. Dalam kod, saya menambah percetakan yang sepadan untuk mencetak nama kelas proksi Sebenarnya, proksi ialah objek proksi itu sendiri ialah kita boleh mengembalikan objek proksi dalam kaedah panggilan dan kemudian membuat panggilan berterusan.

Lihat contoh berikut:

package com.sharpcj.proxytest;
public interface IWork {
    IWork work(String subject);
}
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");
    }
}

Hasilnya adalah seperti berikut:

Contoh analisis proksi dinamik Java dan proksi statik

CGlib 动态代理实现

首先添加 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");
    }
}

结果如下:

Contoh analisis proksi dinamik Java dan proksi statik

MethodInterceptor 接口只有一个 intercept 方法,这个方法有4个参数:

  • 1)obj表示增强的对象,即实现这个接口类的一个对象;

  • 2)method表示要被拦截的方法;

  • 3)args表示要被拦截方法的参数;

  • 4)proxy表示要触发父类的方法对象;

需要注意的是:实际调用是 methodProxy.invokeSuper(), 如果使用 invoke() 方法,则需要传入被代理的类对象,否则出现死循环,造成 stackOverflow 。

Atas ialah kandungan terperinci Contoh analisis proksi dinamik Java dan proksi statik. 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