代理模式、動態代理及其應用

代理模式是二十多種設計模式中的一個,屬於比較經常使用的設計模式。本質上就是用來委託咱們生成的代理類去完成一些額外的功能,這樣可以達到解耦、封裝的目的。
一般能夠用在RPC、AOP中。好比在RPC中,當咱們調用遠程方法時,須要委託代理類幫助咱們去經過網絡鏈接遠程的服務提供者,幫助咱們將消息編碼發送給服務端,幫咱們接受服務端發來的結果。java

靜態代理

在代理模式中,一般指的時靜態代理。
靜態代理設計模式

這是靜態代理的UML圖。代理的思想是:代理類ProxySubject擁有實際類RealSubject的相同方法doSomething(實現相同的一個接口),同時代理類內聚了實際類(即傳入實際類的一個引用),在代理類的doSomething方法中,經過實際類的引用調用實際類的doSomething方法,並在調用先後加入須要代理實現的額外功能。這樣咱們就能夠經過調用代理類的相同方法來達到咱們的目的。
舉個例子:網絡

interface Subject{
        void doSomething();
    }
class RealSubject implements Subject{
    void doSomething(){
        System.out.println("doSomething");
    }
}
class ProxySubject implements Subject{
    private Subject target;
    public ProxySubject(Subject target){
        this.target = target;
    }
    void doSomething(){
        //do before
        System.out.println("do before");
        
        target.doSomething();//調用realSubject的doSomething方法
        
        //do after
        System.out.println("do after");
    }
}
class ProxyTest{
    public static void main(String args[]){
        //生成代理類,並將實際類傳入
        ProxySubject proxy = new ProxySubject(new RealSubject());
        proxy.doSomething();
    }
}

以上,經過代理類執行實際類相同的方法,咱們能夠運行額外的功能。這裏不只會運行實際類doSomething方法裏面的打印「doSomething」,還會在以前打印「do before",以後打印」do after",這兩個即爲額外的功能。框架

動態代理

上面的靜態代理其實存在一個問題,一個實際類對應一個代理類,而不少時候須要代理實現的額外功能是相同的,好比咱們要在A類的某個方法調用前打印「before」,B類的某個方法調用前打印「before」,C類也有這個需求,若是用靜態代理,咱們須要寫A類的代理,B類的代理,C類的代理。。。這樣豈不是要累死。
java當中的動態代理就是爲了這個解決這個問題。其實思路也很簡單,不用去管A類、B類、C類仍是什麼其餘類,統一用反射調用實際類的方法。
java提供了動態代理類Proxy,使用其中的靜態方法Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)來動態生成代理類。interfaces爲實際類的接口,對應上面圖中的Subject,loader爲接口的Classloader,InvocationHandler接口有一個invoke方法須要本身實現,咱們委託代理類實現的額外功能便放在該方法裏。須要注意的是由此看出java的動態代理須要有接口才能使用。若是不用接口實現動態代理只能求助於cglib這類字節碼加強框架。
下面是動態代理的通常實現方法:ide

public class InvokeProxy implements InvocationHandler{
    private Object target;
    
    public Object bind(Object target){
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        
        System.out.println("do something before");
        method.invoke(target, args);
        System.out.println("do something after");
        
        return null;

        /*
        若是反射調用的方法有返回值:
        Object result;
        result = method.invoke(target, args);
        return result;
        */
    }

}

一樣有接口Subject:this

interface Subject{
        void doSomething();
    }

假設類A實現了Subject接口,能夠根據以下代碼生成A的代理編碼

public class ProxyTest {

    public static void main(String[] args) {
        Subject proxyA = (Subject)new InvokeProxy().bind(new A());
        proxyA.doSomething();
    }

}

若是還有類B實現了Subject接口,咱們只需更改上面的代碼便可,而不用像靜態代理那樣重複的編寫代碼。這就是動態代理相對於靜態代理的意義。spa

相關文章
相關標籤/搜索