Java動態(tài)代理

軟件開發(fā)的最終目的是以不變應(yīng)萬變,為此我們的前輩們總結(jié)了很多模式來應(yīng)對各種需求,代理模式就是其中的一種。假如我們已經(jīng)理解了靜態(tài)代理,那動態(tài)代理就相對容易理解了,被代理的類是動態(tài)變化的,是一個泛型的 target; 那到底怎么實現(xiàn)的呢。
Java的動態(tài)代理,會用到InvocationHandler、Proxy 。代碼設(shè)計的時候考慮到方便擴展和維護,都需要抽象出公共特性,例如共有的方法。 動態(tài)代理的出現(xiàn)是為了解決:
1.控制外部調(diào)用
2.增強某些方法的處理
3.需要在N多類的同一個方法執(zhí)行前或者執(zhí)行后做一些事情(AOP)

代碼例子:

/**
 * 公共接口,抽象出公共方法
 */
public interface Person {
    public void giveTask();
}

/**
 * 具體的人,例如學(xué)生,實現(xiàn)交作業(yè)的方法
 */
public class Student implements  Person {
    private String name;
    public  Student(String name) {
        this.name = name;
    }

    public void task() {
        System.out.println(name+"交作業(yè)");
    }
}

/**
 * 具體的人,老師改作業(yè)
 */
public class Tescher implements Person {

    @Override
    public void task() {
        System.out.println("老師改作業(yè)");
    }
}

如果不用代理,那直接就調(diào)用Student和Teacher類了,那我們現(xiàn)在想在所有的task方法執(zhí)行前都干點事情,也許你說可以用抽象類,然后抽象類中實現(xiàn)方法進行處理,也不是不行,但是這樣帶來問題,我將來想擴展更多的類是不是需要改動父類或者我super調(diào)用不及時,等等這樣的問題。 所以動態(tài)代理的出現(xiàn)就是為了不侵入的方式去增強原有的方法。

/**
 * 我想在原有的老師或者學(xué)生做事情前,干點啥又不用去改動原來的方法
 * 這就是一個Person代理方法處理,并不是代理類哦,代理是通過他動態(tài)創(chuàng)建的
 * 在代理類里調(diào)用本類的invoke方法,從而調(diào)用目標類的方法
 * @param <T> 泛型
 */
public class PersonInvocationHandler<T> implements InvocationHandler {
    // 被代理的對象
    T target;

    public PersonInvocationHandler(T target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理執(zhí)行" + method.getName() +"方法");
        // 用反射執(zhí)行被代理對象的方法
        Object ret = method.invoke(target,args);
        return ret;
    }
}

具體調(diào)用:

    public static void main(String[] args) {
        // 學(xué)生
        Person xiaoming = new Student("小明");
        // 老師
        Person laoshi = new Tescher();

        // 構(gòu)建代理方法處理者,就是通過他要在別人方法前后干點事情
        PersonInvocationHandler invocationHandler1 = new PersonInvocationHandler(laoshi);

        // 通過Proxy.newProxyInstance 生成一個新的代理類對象,這個方法硬生生的創(chuàng)造出了一個新的類出來$Proxy0
        // 并且按照定義的接口,實現(xiàn)了接口中的方法,方法內(nèi)部是調(diào)用 invocationHandler的invoke()
        Person person1 = (Person)Proxy.newProxyInstance(Tescher.class.getClassLoader(),new Class<?>[]{Person.class},invocationHandler1);

        // 表面上看是調(diào)用Person.task(), 實際上是調(diào)用新創(chuàng)建的代理類的task()方法,神不知鬼不覺。
        person1.task();

    }

運行結(jié)果如你猜想的一樣:

代理執(zhí)行task方法
老師改作業(yè)

動態(tài)代理之所以動態(tài),就在于用泛型接收被代理對象和完全動態(tài)的創(chuàng)建了一個新的代理類出來。 怎么知道創(chuàng)建了新的類呢,在Proxy.newProxyInstance前設(shè)置一下參數(shù),就會輸出動態(tài)創(chuàng)建的類到項目目錄下,$Proxy0.class這個就是剛剛創(chuàng)建被編譯成字節(jié)碼的文件。

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

利用IDEA反編譯查看一下

/*
繼承Proxy,方便調(diào)用invocationHandler,還記得Proxy.newProxyInstance傳進去的,實現(xiàn)Person接口
*/
public final class $Proxy0 extends Proxy implements Person {
//以下是提取的 方法
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
     // 其他部分省略了,這里是獲取方法,利用反射找到被代理的方法
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.alienjun.dynamictest.Person").getMethod("task");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }

    // 根據(jù)接口實現(xiàn)task()方法,這里只是調(diào)用父類的invocationHandler.invoke(),具體執(zhí)行task是在invocationHandler.invoke中執(zhí)行的
    public final void task() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
}

到這里就已經(jīng)明白整個動態(tài)代理過程了,
動態(tài)代理

其中怎么創(chuàng)建的類,又是怎么被編譯和加載調(diào)用的更多細節(jié)可以細看源碼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容