Java 動態代理使用總結


我想在網上查了一些有關 Java 動態代理相關的技術資料,發現講的都是一些理論或者源碼,沒有太多實際的應用舉例,讓人看的雲里霧里、似懂非懂。索性我就自己總結一下,方便后續在使用時進行快速查閱。

Java 動態代理技術其實是 AOP 編程思想的實現。AOP 編程思想可以簡單的理解為:在不改變原有業務代碼情況下,實現對原有業務功能的修改或擴展。

AOP 編程思想在我們的實際開發中經常使用,比如你開發了一個網站系統,某些功能需要有權限才能使用,你肯定不想在每個具體的方法上進行硬編碼實現權限驗證,這樣很不好統一維護,你會編寫一個權限過濾器,對用戶的請求進行攔截,驗證用戶權限。這里的權限過濾器其實就是 AOP 編程思想的一個體現,在不改變原有業務功能代碼的情況下,實現對原有業務功能的權限驗證擴展。

Java 動態代理技術 AOP 編程思想的實現方式,本質上就是利用反射技術,為實現了【具體接口】的實現類對象,創建實現了【具體接口】的代理對象,通過對代理對象的 invoke 方法進行代碼修改,從而在不改變原有實現類代碼的前提下,對原有實現類實例化后的對象的相關方法進行功能修改或者擴展。

從上面的描述可以發現:要實現 Java 動態代理技術,首先必須要有一個接口,以及該接口的實現類。下面我們還是舉一個簡單的例子來詳細介紹 Java 動態代理技術吧。

// 定義一個接口
public interface PersonInterface {

    //上午
    void Morning();

    //下午
    void Afternoon();

    //傍晚
    void Evening();

    //夜里
    void Night();
}

// 定義 Person 類,實現了 PersonInterface 接口中的方法
public class Person implements PersonInterface {

    @Override
    public void Morning() {
        System.out.println("上午:讀書學習...");
    }

    @Override
    public void Afternoon() {
        System.out.println("下午:鍛煉身體...");
    }

    @Override
    public void Evening() {
        System.out.println("傍晚:聽歌娛樂...");
    }

    @Override
    public void Night() {
        System.out.println("夜里:早點睡覺...");
    }
}

現在我們要實現目標是:在不改變 Person 類中任何源代碼的情況下,對 Person 類實例化后的對象中 Morning、Afternoon、Evening 這 3 個方法,進行功能增強或者修改,調用這 3 個方法打印的內容是修改后的內容,不是原有內容。

注意:要求必須使用 Person 類進行實例化對象,然后調用 Morning、Afternoon、Evening 這 3 個方法。

這就導致我們無論是采用一個新的類,去實現 PersonInterface 接口,重新實現 Morning、Afternoon、Evening 這 3 個方法,還是采用一個新的類,繼承 Person 去重寫 Morning、Afternoon、Evening 這 3 個方法,都是不可行的。因為最終我們實例化 Person 對象后,打印的還是 Person 中這 3 個方法的原有內容。

這里只能采用 Java 動態代理技術來實現,具體代碼如下:

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

public class ProxyTest {
    public static void main(String[] args) {

        //實例化一個 Person 對象 wolfer
        Person wolfer = new Person();

        /*
        基於 wolfer 對象和 PersonInterface 接口,創建動態代理對象
        */

        //第一種方式:采用匿名內部類實現
        PersonInterface wolferProxy = (PersonInterface) Proxy.newProxyInstance(
            //wolfer 對象的類加載器,其實就是 Person 類的加載器
            wolfer.getClass().getClassLoader(),
            //wolfer 對象所實現的接口的字節碼,其實就是 Person 類所實現的接口的字節碼
            wolfer.getClass().getInterfaces(),
            //動態代理的具體實現(匿名內部類實現方式)
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) {
                    try {
                        if ("Morning".equals(method.getName())) {

                            //如果是 wolfer 對象的 Morning 方法
                            //給 wolfer 對象的原有 Morning 方法代碼前,增加一行代碼
                            System.out.println("上午:睡到自然醒,然后起床...");

                            //調用 Morning 的原有方法
                            return method.invoke(wolfer, args);

                        } else if ("Afternoon".equals(method.getName())) {

                            //調用 Afternoon 的原有方法,獲取返回值
                            Object obj = method.invoke(wolfer, args);

                            //如果是 wolfer 對象的 Afternoon 方法
                            //給 wolfer 對象的原有 Afternoon 方法代碼后,增加一行代碼
                            System.out.println("下午:去吃個大餐,補充能量...");

                            return obj;

                        } else if ("Evening".equals(method.getName())) {

                            //如果是 wolfer 對象的 Evening 方法
                            //直接改變了 Evening 原有方法的實現代碼
                            System.out.println("傍晚:燈紅酒綠,紙醉金迷...");

                            //由於 Evening 方法不需要返回值,因此這里返回 null 即可
                            return null;

                        } else {

                            //其它方法不進行改變和擴展,直接調用原有方法
                            return method.invoke(wolfer, args);
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        return null;
                    }
                }
        });

        //第二種實現方式:
        //由於動態代理的第 3 個參數 InvocationHandler 是僅有一個方法的接口,
        //因此可以簡化為 Lambda 表達式的編寫方式
        PersonInterface wolferProxy = (PersonInterface) Proxy.newProxyInstance(
            	wolfer.getClass().getClassLoader(),
            	wolfer.getClass().getInterfaces(),
                (proxy, method, proxyArgs) -> {
                    try {
                        if ("Morning".equals(method.getName())) {
                            System.out.println("上午:睡到自然醒,然后起床...");
                            return method.invoke(wolfer, proxyArgs);
                        } else if ("Afternoon".equals(method.getName())) {
                            Object obj = method.invoke(wolfer, proxyArgs);
                            System.out.println("下午:去吃個大餐,補充能量...");
                            return obj;
                        } else if ("Evening".equals(method.getName())) {
                            System.out.println("傍晚:燈紅酒綠,紙醉金迷...");
                            return null;
                        } else {
                            return method.invoke(wolfer, proxyArgs);
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        return null;
                    }
                }
        );

        //上面兩種實現方式,只保留一種即可

        //調用 wolfer 的動態代理對象的 Morning 方法,發現:
        //相比 Person 類的原有 Morning 方法,前面增加了一句打印
        wolferProxy.Morning();

        //調用 wolfer 的動態代理對象的 Afternoon 方法,發現:
        //相比 Person 類的原有 Afternoon 方法,發現后面增加了一句打印
        wolferProxy.Afternoon();

        //調用 wolfer 的動態代理對象的 Evening 方法,發現:
        //相比 Person 類的原有 Evening 方法,打印的內容被徹底改變了
        wolferProxy.Evening();

        //調用 wolfer 的動態代理對象的 Night 方法
        //由於動態代理沒有對其修改和擴展,因此打印內容跟 Person 類原有的 Night 方法相同
        wolferProxy.Night();
    }
}

/*
打印內容如下所示:
上午:睡到自然醒,然后起床...
上午:讀書學習...
下午:鍛煉身體...
下午:去吃個大餐,補充能量...
傍晚:燈紅酒綠,紙醉金迷...
夜里:早點睡覺...
*/

Ok,通過以上例子,應該能夠快速理解並掌握 Java 動態代理技術了。

實際應用場景是:如果你引用了第三方的 jar 包,如果你感覺它的某些類的方法不太滿足自身的需求,想要改變或增強的話,而這些類恰好也實現了某些接口,此時就可以使用 Java 動態代理技術就行修改或增強,來滿足自身業務的需要。另外在 Java 的各種框架中,比如 Spring 相關框架也廣泛使用 Java 動態代理技術。

希望以上內容對大家有用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM