1. 程式人生 > >Java基礎-靜態代理以及動態代理

Java基礎-靜態代理以及動態代理

object 越來越大 耦合 返回 業務層 load 類加載 HA 目的

動態代理:

在了解動態代理之前,先對代理有一個認識.

代理模式是Java常見的設計模式之一。所謂代理模式是指客戶端並不直接調用實際的對象,而是通過調用代理,來間接的調用實際的對象。

打個比方:你買火車票的時候,並不直接花錢購買, 而是將錢預先墊付到搶票軟件上, 使搶票軟件為你購買, 你要做的行為就是買票,搶票軟件就是你的代理

代理對象控制對被代理對象的訪問:

技術分享圖片

這是代理的通用模型圖

  • Subject:定義了被代理角色和代理角色的共同接口或者抽象類,也就是subject中定義了共同接口opration();

  • Realsubject:實現或者繼承抽象主題角色,定義實現具體業務邏輯的實現。

  • Proxy:也就是代理人,porxy持有Realsubject的引用控制和實現. 並且有自己的處理邏輯.

代理分為靜態和動態兩種,先了解靜態代理,知道其缺點後,再了解動態代理,會豁然開朗.

靜態代理的作用:

靜態代理通常用於對原有業務邏輯的擴充。比如持有二方包的某個類,並調用了其中的某些方法。然後出於某種原因,比如記錄日誌、打印方法執行時間,但是又不好將這些邏輯寫入二方包的方法裏。所以可以創建一個代理類實現和二方方法相同的方法,通過讓代理類持有真實對象,然後在原代碼中調用代理類方法,來達到添加我們需要業務邏輯的目的。

靜態代理的實現:

這裏方便理解 引入的Proxy是自己定義的,在下面動態代理的時候會使用java.reflect中提供的Proxy

package proxy;  
//將業務層抽象出來的接口.
public interface Subject {  
    void doSomething();
}


package proxy;

//真實對象,也就是被代理者,理解為你自己就可以了,要進行買票行為.
public class RealObject implements Subject{
    
    @Override
    public void doSomething() {

        System.out.println("這裏是真實對象");

    }
}


package proxy;

//代理者,進行代理事務的人,理解成搶票軟件,要代替你進行買票行為.
public class Proxy implements  Subject{

    private Subject realObject;

     Proxy(Subject realObject){
        this.realObject=realObject;
        //從這裏知道需要代理的人是誰
    }
    
    @Override
    public void doSomething() {
        System.out.println("這裏是代理對象");
        //可以在調用真實對象前增加操作
        //例如在開搶前告訴你,搶票要開始了.
        realObject.doSomething();//在這裏調用真實的代理對象
        //可以在調用真實對象後增加操作.
        //在搶票結束後,告訴你成功或者失敗.
    }
    
}

執行:

package proxy;

public class ProxyTest {


    public static void main(String[] args){

        RealObject realObject =new RealObject();
        realObject.doSomething();//被代理對象做的事,或者說委托人想要做的事

        System.out.println("-----------------");
        Proxy proxy =new Proxy(new RealObject());//告知代理對象被代理對象是誰,或者說告訴執行者委托人要做什麽.
        proxy.doSomething();//通過代理對象調用被代理對象,實現代理.

        /*
        代理和被代理是不是有點暈,換一種說法來說.
        以DOTA為例:
        大哥本來要自己拉野,忽然發現一大波線襲來,於是就和醬油說,幫我拉兩波野,一會帶你喝湯! 大哥就是被代理者
        醬油說: 是大哥! 醬油在去拉野的路上,吃了賞金符,拉完野之後配合隊友殺了一波人. 醬油就是代理者
        吃賞金符就是預處理.
        殺人就是結果處理.
         */
    }

}

大家有沒有發現,這樣雖然形成了代理行為,但是寫法恨死,耦合度很高,現在是一個火車票,如果有飛機票,演唱會票等等一系列的需求和一系列的雇主,會導致代理類的代碼量越來越大,並且會有重名的危險,這也就是靜態代理的缺點.

為了解決這個問題,大佬們發明出了動態代理的方法.
JAVA為我們提供了動態代理的接口,使用起來非常方便,下邊來了解一下.

需要將要擴展的功能寫在一個InvocationHandler 實現類裏:

這個Handler中的invoke方法中實現了代理類要擴展的公共功能。

靜態代理的缺點:
有十個不同的RealObject,同時我們要去代理的方法是不同的,比要代理方法:doSomething、doAnotherThing、doTwoAnotherThing.

方法1:為每個代理創建代理類,這樣會造成數個類
方法2: 在一個代理類中創建數個代理方法,當代理方法越來越多的時候容易造成代理類中的代碼量越來越大 並且有重名的風險.

為此動態代理誕生了:

動態代理的使用:

先看一下動態代理的接口:

  1. 實現InvocationHandler接口

我們來看一下接口的結構:

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable; 
//這個接口只有一個方法 就是invoke方法,也就是說我們只要實現invoke方法就可以了
}

雖然這個參數不用我們傳入,但是還是需要了解一下,第一個就是代理類的實例,第二個是方法, 第三個是對象的參數數組.

2.通過newProxyInstance方法創建代理實例:

newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 

通過這個方法返回我們代理類的實例, 也就是RealObject的Proxy

返回指定接口的代理類的實例,該接口將方法調用分派給指定的調用處理程序

從參數上看 第一個參數是類加載器, 第二個方法是個Class對象數組接口,第三個參數是我們上邊實現的InvocationHandler

在這裏不貼源碼了,有興趣的可以自己追蹤或者查看下方我的參考資料.

動態代理實例:

沿用上邊靜態代理的類,下面只寫測試類和處理類.

處理類:

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

public class DynamicProxyHandler implements InvocationHandler {

    private Object realObject;

    Object bind(Object realObject){

        this.realObject=realObject;//給真實對象賦值


        return    Proxy.newProxyInstance(realObject.getClass().getClassLoader(),
                                       realObject.getClass().getInterfaces(),
                                        this);
        //通過Class對象得到該對象的構造器,接口方法數組,傳入自己構造後的實例.
        //java.reflect.Proxy方法最後返回的是一個代理實例.
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在這裏拓展代理邏輯

        System.out.println("rua!");

        Object result= method.invoke(realObject,args);//方法的調用.
        //這裏的invoke調用是java進行調用,proxy對象,方法,和參數數組已經是我們之前傳入的被代理類的了.

        return result;
    }
}

測試類:

public class DynamicProxy {

    public static void main(String[] args){


        DynamicProxyHandler pHandler=new DynamicProxyHandler();//創建我們動態代理處理類的對象
        Subject proxy=(Subject)pHandler.bind(new RealObject());//對其進行動態代理的參數綁定然後返回一個代理實例
        proxy.doSomething();//調用    
    }
}

我第一次看的時候也是雲裏霧裏,不明白為什麽這樣就動態了, 這裏的方法調用,都與方法對象無關,不直接進行調用.
不管你在Subject中定義了什麽接口,都與調用無關,依靠反射,實時獲取你的方法,參數,類的數據,再通過反射進行調用.

有十個不同的RealObject,同時我們要去代理的方法是不同的,比要代理方法:doSomething、doAnotherThing、doTwoAnotherThing.
以這個例子來說, 我傳入10個不同的對象,對象中自然有自己的實現,我通過反射拿到實現後構建新的對象,不會對原有對象造成影響,再對新的對象進行方法調用操作,來100個 1000個對象也同樣可以.

上面一切的條件都是建立在反射的基礎上,如果反射了解的不太清楚,請返回去看反射,我的博客也有寫,也可以通過網絡自行了解.

參考資料:

1.https://blog.csdn.net/wangqyoho/article/details/77584832
2.https://www.zhihu.com/question/20794107/answer/23330381
3.Thinking in java page 592-598

Java基礎-靜態代理以及動態代理