設計模式-代理模式

WellBay 1月前


標籤:love   系統   代理類   oca   解決   效率   問題   instance   byte   

7、代理模式
代理模式是指為其他物件提供一種代理,以控制對這個物件的訪問,屬於結構型模式。在某些情況下,一個物件不適合或者不能直接引用另一個物件,而代理物件可以在客戶端和目標物件之間起到中介的作用。
代理模式一般包含三種角色:
抽象主題角色(Subject):抽象主題類的主要職責是宣告真實主題與代理的共同介面方法,該類可以是介面也可以是抽象方法。
真實主題角色(RealSubject):該類也被稱為被代理類,該類定義了代理所表示的真實物件,是負責執行系統真正的業務邏輯物件。
代理主題角色Proxy:也被稱為代理類,其內部有RealSubjuct的引用,因此具備完全的對RealSubject的代理權。客戶端呼叫代理物件的方法,但是會在代理物件前後增加一些處理程式碼。

代理模式的通用寫法:

package com.jdwa.nomalproxy;

public interface ISubject {
    void request();
}
package com.jdwa.nomalproxy;

public class RealSubject implements ISubject{
    @Override
    public void request() {
        System.out.println("real service is called ");
    }
}

package com.jdwa.nomalproxy;

public class Proxy implements ISubject {

    private ISubject subject;

    public Proxy(ISubject subject){
        this.subject = subject;
    }


    @Override
    public void request() {
        before();
        subject.request();
        after();
    }

    private void before(){
        System.out.println("called before real service ...");
    }

    private void after(){
        System.out.println("called after real service ...");
    }

}
package com.jdwa.nomalproxy;

public class Client {
    public static void main(String[] args) {
        ISubject subject = new RealSubject();
        Proxy proxy = new Proxy(subject);
        proxy.request();
    }

}

從靜態代理帶動態代理:

package com.jdwa.staticproxy;

public interface IPerson {
    void findLove();
}
package com.jdwa.staticproxy;

public class Tom implements IPerson {
    @Override
    public void findLove() {
        System.out.println("must be a beautiful girl");
    }
}
package com.jdwa.staticproxy;

public class TomFather implements IPerson {

    private IPerson tom;

    public TomFather(IPerson tom){
        this.tom = tom;
    }
    @Override
    public void findLove() {
        before();
        tom.findLove();
        after();
    }

    private void before(){
        System.out.println("find a suitable girl ...");
    }

    private void after(){
        System.out.println("Get ready to be together ...");
    }
}
package com.jdwa.staticproxy;

public class Client {
    public static void main(String[] args) {
        IPerson tom = new Tom();
        IPerson tomFather = new TomFather(tom);
        tomFather.findLove();
    }
}

動態代理

package com.jdwa.staticproxy;

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

public class MatchMarker implements InvocationHandler {

    private IPerson target;

    public IPerson getInstance(IPerson target){
        this.target = target;
        Class<?> clazz = target.getClass();
        return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(this.target,args);
        after();
        return result;
    }

    private void before(){
        System.out.println("I‘m matchmarker,I hava already collect your infomation ,start to look for");
    }

    private void after(){
        System.out.println("They are very satisfied with each other");
    }
}

測試

package com.jdwa.staticproxy;

public class Client {
    public static void main(String[] args) {
//        IPerson tom = new Tom();
//        IPerson tomFather = new TomFather(tom);
//        tomFather.findLove();

        MatchMarker matchMarker = new MatchMarker();
        IPerson tom = matchMarker.getInstance(new Tom());
        tom.findLove();
    }
}

這就是JDK自帶的動態代理實現。
我們都知道jdk動態代理採用位元組重組,重新生成物件來代替原始物件,以達到動態代理的目的。jdk動態代理生成物件的步驟如下:
a、獲取被代理物件的引用,並獲取他的所有介面,反射獲取。
b、jdk動態代理類重新生成一個新的類,同時新的類要實現被代理類實現的所有介面
c、動態生成Java程式碼新加的業務邏輯方法由一定的邏輯程式碼呼叫
d、編譯新生成的Java程式碼,得到class
e、重新載入到JVM中執行

我們通過將記憶體中的物件位元組碼輸出,然後反編譯,可以檢視代理物件的原始碼。

package com.jdwa.staticproxy;

import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;

public class Client {
    public static void main(String[] args) throws Exception{
//        IPerson tom = new Tom();
//        IPerson tomFather = new TomFather(tom);
//        tomFather.findLove();

//        MatchMarker matchMarker = new MatchMarker();
//        IPerson tom = matchMarker.getInstance(new Tom());
//        tom.findLove();
        
        IPerson person = new MatchMarker().getInstance(new Tom());
        person.findLove();
        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{IPerson.class});
        FileOutputStream fos = new FileOutputStream("E://$Proxy0.class");
        fos.write(bytes);
        fos.close();
    }
}

通過原始碼我們可以發現$Proxy0類繼承了Proxy類,同時還實現了IPerson介面,重寫了findLove方法。在靜態塊中用反射查詢了目標 物件的所有方法,而且儲存了所有方法的引用,重寫的方法用反射呼叫目標物件的方法。這些程式碼,都是jdk幫我們自動生成的。

CGLIB實現

package com.jdwa.staticproxy;


import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CGLibMatchMarker implements MethodInterceptor {

    public Object getInstance(Class<?> clazz) throws Exception{
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);

        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object obj = methodProxy.invokeSuper(o,objects);
        after();
        return obj;
    }

    private void before(){
        System.out.println("cglib ----I‘m matchmarker,I hava already collect your infomation ,start to look for");
    }

    private void after(){
        System.out.println("cglib ----They are very satisfied with each other");
    }
}

package com.jdwa.staticproxy;

import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;

public class Client {
    public static void main(String[] args) throws Exception{
//        IPerson tom = new Tom();
//        IPerson tomFather = new TomFather(tom);
//        tomFather.findLove();

//        MatchMarker matchMarker = new MatchMarker();
//        IPerson tom = matchMarker.getInstance(new Tom());
//        tom.findLove();

//        IPerson person = new MatchMarker().getInstance(new Tom());
//        person.findLove();
//        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{IPerson.class});
//        FileOutputStream fos = new FileOutputStream("E://$Proxy0.class");
//        fos.write(bytes);
//        fos.close();

        Tom tom = (Tom) new CGLibMatchMarker().getInstance(Tom.class);
        tom.findLove();

    }
}



cglib的執行效率要高於jdk的,就是因為CGLib採用了FastClass機制,他的原理簡單來說就是:為代理類和被代理類各生成一個類,這個類會為代理類和被代理類的方法分配一個INDEX(int型別);這個INDEX當作一個入參,FastClass就可以直接定位要呼叫的方法並直接進行呼叫,省去了反射呼叫,所以呼叫效率比JDK代理通過反射呼叫高。

歡迎大家留言,以便於後面的人更快解決問題!另外亦歡迎大家可以關注我的微信公眾號,方便利用零碎時間互相交流。共勉!

設計模式-代理模式

標籤:love   系統   代理類   oca   解決   效率   問題   instance   byte   

原文地址:https://www.cnblogs.com/caozz/p/proxy.html


上一篇:用餘弦定理證明海倫公式
下一篇:pdf轉圖片加水印壓縮