Java設計模式學習記錄-代理模式

代理模式

代理模式是常見設計模式的一種,代理模式的定義是:爲其餘對象提供一種代理以控制對這個對象的訪問html

在某些狀況下,一個對象不適合或者不能直接引用另外一個對象,而代理對象能夠在客戶端和目標對象之間起到中介的做用。java

靜態代理

理解設計模式是比較枯燥的,因此仍是以舉例子的方式來進行理解,spring

例如:公司開年會想找個明星來表演,那麼並不會直接聯繫明星(主要仍是聯繫不上),而是會聯繫明星的經紀人,明星就是被代理的對象,而經紀人就是代理對象。明星只須要準備來參加年會時應該表演什麼節目就能夠,其餘的出場費之類的事情就交給經紀人來處理就行了。代理對象能夠理解爲被代理對象的擴展,能作被代理對象不能作的事情,也能夠調用代理對象作事情。設計模式

那麼用代碼實現這個場景是什麼樣子的呢?框架

執行合做方法的接口ide

/**
 * @Description: 經紀公司接口,代理對象和被代理對象都須要實現的接口
 */
public interface Company {
    /** 合做 */
    void cooperation();
}

被代理對象函數

/**
 * @Description: 目標對象-明星(被代理對象)
 */
public class Start implements Company {

    @Override
    public void cooperation() {
        System.out.println("is show time");
    }
}

代理對象工具

/**
 * @Description: 經紀人(代理對象)
 */
public class Agent implements Company {

    private Company company;

    public Agent(Company company)
    {
        this.company = company;
    }

    @Override
    public void cooperation()
    {
        System.out.println("收出場費,化妝等等");
        company.cooperation();
        System.out.println("收拾行李,打道回府");
    }
}

測試類oop

import org.junit.Test;

/**
 * @Description: 測試類
 */
public class ProxyTest {

    @Test
    public void AnnualMeeting()
    {
        //目標對象
        Start start = new Start();
        //構建代理對象,生成代理關係
        Agent agent = new Agent(start);
        //用代理對象執行被代理對象的動做
        agent.cooperation();
        
    }

}

輸出結果:性能

收出場費,化妝等等
is show time
收拾行李,打道回府

靜態代理的特色是:能夠在目標對象實現的基礎上,加強額外的功能操做,即擴展目標對象的功能。有時候不方便修改別人的代碼或者是引入的一個功能,須要進行功能擴展一下才能適用於本身的業務實現,可使用代理模式來進行設計。

可是靜態代理的實現基礎是一個目標對象對應一個代理對象,而且在編譯時就已經維護好了代理關係,若是目標對象是多個那麼就會須要多個代理對象,這樣在更新目標的對象的時候還須要更新代理對象,當代理對象持續增長時維護成本就變得很是困難。

針對於這種狀況,動態代理應運而生。

動態代理

JDK代理

動態代理的代理對象不須要和目標對象共同實現接口,而是利用JDK的API,動態的在內存中構建代理對象。

動態生成代理對象須要調用JDK中的java.lang.reflect.Proxy類的newProxyInstance方法,這個方法須要三個參數:

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,]Class<?>[] interfaces,InvocationHandler h)
ClassLoader loader:類加載器,用來加載目標對象類,由於是在運行時得到目標對象,因此確定須要用到反射。
Class<?>[] interfaces:目標對象類實現的接口集合,這些接口中定義目標對象能夠執行的方法。
InvocationHandler h:這個參數表明的是動態代理對象在調用方法的時候,會將方法轉發到哪個invocationHandler對象身上,InvocationHandler是個接口,
須要本身實現它,而後定義本身的動態代理執行方法。
建立包含動態代理對象具體執行方法的實現類。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @Description: 包含動態代理對象具體執行方法的實現類
 */
public class MyInvocationHandler implements InvocationHandler {

    private Company company;

    public MyInvocationHandler(Company company)
    {
        this.company = company;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {

        System.out.println("收出廠費,化妝等");
        //具體執行方法
        Object result = method.invoke(company,args);

        System.out.println("收拾現場,卸妝,打道回府");

        return result;
    }
}

測試類

import org.junit.Test;
import java.lang.reflect.Proxy;

/**
 * @Description: 測試類
 */
public class DynamicProxyTest {

    @Test
    public void AnnualMeeting()
    {
        //建立目標對象
        Company start = new Start();
        //建立代理對象須要執行的方法處理對象
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler(start);
        //得到目標對象的類加載器
        ClassLoader classLoader = start.getClass().getClassLoader();
        //建立動態代理對象
        Company proxy = (Company) Proxy.newProxyInstance(classLoader,start.getClass().getInterfaces(),myInvocationHandler);
        //用動態代理對象執行目標對象的方法
        proxy.cooperation();
    }
}

輸出結果:

收出廠費,化妝等
is show time
收拾現場,卸妝,打道回府

JDK動態代理的特色:代理對象不須要實現接口,可是目標對象必須實現接口。

那麼若是在實際的業務中目標對象確實沒有實現接口,怎麼辦呢?

遇到這種狀況的時候就須要時cglib動態代理了。

Cglib代理

Cglib代理,也叫做子類代理,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展.

  • JDK的動態代理有一個限制,就是使用動態代理的對象必須實現一個或多個接口,若是想代理沒有實現接口的類,就可使用Cglib實現。
  • Cglib是一個強大的高性能的代碼生成包,它能夠在運行期擴展java類與實現java接口.它普遍的被許多AOP的框架使用,例如Spring AOP和synaop,爲他們提供方法的interception(攔截)。
  • Cglib包的底層是經過使用一個小塊的字節碼處理框架ASM來轉換字節碼並生成新的類.不鼓勵直接使用ASM,由於它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉。

在實現cglib代理時須要引入cglib的jar包,可是spring核心功能已經包含了cglib的功能,因此引入spring-core的jar包就能夠了。

須要注意的是:代理的類不能爲final,不然報錯,目標對象的方法若是爲final/static,那麼就不會被攔截,即不會執行目標對象額外的業務方法。

沒有實現接口的目標對象類

/**
 * @Description: 沒有經紀公司的明星,就行像最近以我的練習生出道的蔡徐坤
 */
public class AloneStart {
    /** 合做 */
    public void cooperation() {
        System.out.println("is show time");
    }

}

生成Cglib代理對象的類

import org.mockito.cglib.proxy.Enhancer;
import org.mockito.cglib.proxy.MethodInterceptor;
import org.mockito.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

/**
 * @Description: 生成代理對象的類
 */
public class CglibProxy implements MethodInterceptor {


    private AloneStart aloneStart;

    public CglibProxy(AloneStart aloneStart)
    {
        this.aloneStart = aloneStart;
    }

    /**
     * 建立代理對象
     * @return
     */
    public Object getProxyInstance()
    {
        //動態代理工具類
        Enhancer enhancer = new Enhancer();
        //設置父類
        enhancer.setSuperclass(aloneStart.getClass());
        //設置回調函數調用對象
        enhancer.setCallback(this);
        //返回代理對象
        return enhancer.create();

    }

    @Override
    public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable
    {
        System.out.println("收出廠費,化妝等");
        //執行代理方法
        methodProxy.invokeSuper(obj,objects);
        System.out.println("卸妝,回家");
        return null;
    }
}

測試類

import org.junit.Test;

/**
 * @Description: 測試類
 */
public class CglibProxyTest {

    @Test
    public void cglibTest()
    {
        //建立目標對象
        AloneStart aloneStart = new AloneStart();
        //建立代理對象
        AloneStart startProxy = (AloneStart) new CglibProxy(aloneStart).getProxyInstance();
        //用代理對象執行目標對象的方法
        startProxy.cooperation();
    }
}

輸出結果:

收出廠費,化妝等
is show time
卸妝,回家

jdk採用反射機制調用委託類的方法,而cglib採用相似索引的方式直接調用委託類方法;

還有須要注意的是:

在Spring的AOP中

若是加入容器的目標對象有實現接口,用JDK代理
若是目標對象沒有實現接口,用Cglib代理

 參考:

Java的三種代理模式: https://www.cnblogs.com/cenyu/p/6289209.html

說說代理模式:http://www.importnew.com/26116.html

相關文章
相關標籤/搜索