JDK動態(tài)代理

JDK動態(tài)代理是java.lang.reflect.*包提供的方式,它必須借助一個借口才能產(chǎn)生一個對象,也就是說JDK動態(tài)代理是對接口的代理。一般要使用JDK動態(tài)代理,首先得定義接口,然后再對這個接口的實現(xiàn)類對象進(jìn)行代理,產(chǎn)生代理對象。SpringAOP就是JDK動態(tài)代理的應(yīng)用之一。

第一步:定義接口

JDK動態(tài)代理必須有接口,這里僅僅定義一個及其簡單的接口用于示例。

package com.lemon.designmode.interfaces;

/**
 * @author lemon
 * @date 2018/2/11 上午10:25
 */
public interface HelloWorld {

    void sayHello();
}

第二步:實現(xiàn)接口

這里是接口的實現(xiàn)類,具體的業(yè)務(wù)邏輯寫在這里。

package com.lemon.designmode.bean;

import com.lemon.designmode.interfaces.HelloWorld;

/**
 * @author lemon
 * @date 2018/2/11 上午10:23
 */
public class HelloWorldImpl implements HelloWorld {

    @Override
    public void sayHello() {
        System.out.println("向Java世界問好...");
    }
}

這里是最簡單的Java接口和實現(xiàn)類的關(guān)系,此時可以開始動態(tài)代理了,一般會分為兩個步驟:第一是建立代理對象和真是服務(wù)對象的代理和被代理關(guān)系,第二步是時間代理對象具體方法的邏輯。

第三步:動態(tài)代理綁定和代理邏輯實現(xiàn)

JDK動態(tài)代理中,要實現(xiàn)代理邏輯類必須娶實現(xiàn)java.lang.reflect.InvocationHandler接口,它內(nèi)部定義了一個invoke方法,并提供接口數(shù)組用于將代理對象下掛到被代理對象所屬的接口下,也就是說代理對象和被代理對象都是實現(xiàn)了同一個接口,只不過被代理對象是顯示實現(xiàn)(使用了關(guān)鍵字implements),代理對象是運(yùn)行時實現(xiàn)。

package com.lemon.designmode.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author lemon
 * @date 2018/2/11 上午10:27
 */
public class JdkProxyExample implements InvocationHandler {

    private Object target;

    /**
     * 該方法用于將真實對象綁定到代理類,并返回代理對象
     *
     * @param target 真實服務(wù)對象
     * @return 代理對象
     */
    public Object bind(Object target) {
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    /**
     * 代理方法邏輯
     *
     * @param proxy  代理對象
     * @param method 當(dāng)前調(diào)度方法
     * @param args   被代理對象的方法需要的參數(shù)
     * @return 代理結(jié)果
     * @throws Throwable 異常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("進(jìn)入代理方法");
        System.out.println("代理方法調(diào)用真實對象之前的邏輯處理");
        Object obj = method.invoke(target, args);
        System.out.println("代理方法調(diào)用真實對象之后的邏輯處理");
        return obj;
    }
}

上面的代碼實現(xiàn)了兩步:第一是將真實服務(wù)對象和代理對象進(jìn)行了綁定;第二步是實現(xiàn)了代理邏輯。
這里通過bind方法將真實服務(wù)對象保存到了代理類的成員變量中,并返回了代理對象。

newProxyInstance方法分析:
1)第一個參數(shù)是類加載器,這里使用的是target的類加載器
2)第二個參數(shù)是傳入真實服務(wù)對象所在的接口,用于將代理對象下掛到和真實服務(wù)對象所在的接口
3)第三個參數(shù)是定義實現(xiàn)方法邏輯的代理類,this表示當(dāng)前類對象,也就是代理類,那么這個代理類就必須實現(xiàn)InvocationHandler接口的invoke方法,它就是代理邏輯的實現(xiàn)方法。

invoke方法分析:
1)第一個參數(shù)是代理對象,就是bind方法生成的對象
2)第二個參數(shù)是當(dāng)前調(diào)度的方法
3)第三個參數(shù)是調(diào)度方法的參數(shù)。

第四步:測試JDK動態(tài)代理

package com.lemon.designmode.test;

import com.lemon.designmode.bean.HelloWorldImpl;
import com.lemon.designmode.interfaces.HelloWorld;
import com.lemon.designmode.proxy.JdkProxyExample;

/**
 * @author lemon
 * @date 2018/2/11 上午10:44
 */
public class JdkProxyTest {

    public static void main(String[] args) {
        JdkProxyExample jdkProxyExample = new JdkProxyExample();
        HelloWorld proxy = (HelloWorld) jdkProxyExample.bind(new HelloWorldImpl());
        proxy.sayHello();
    }
}

這里使用代理對象直接調(diào)用真實服務(wù)對象同名方法,實現(xiàn)了零入侵式代碼增強(qiáng)。Spring AOP就是這個原理,在真實服務(wù)對象方法加入前置通知、后置通知、環(huán)繞通知等。
測試方法運(yùn)行結(jié)果如下:

進(jìn)入代理方法
代理方法調(diào)用真實對象之前的邏輯處理
向Java世界問好...
代理方法調(diào)用真實對象之后的邏輯處理

如果想了解CGLIB動態(tài)代理,請移步另一篇博客《CGLIB動態(tài)代理》。

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

相關(guān)閱讀更多精彩內(nèi)容

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