Java設計模式——代理模式

代理模式

學習目標

  • 掌握代理模式的應用場景和實現原理。
    • 應用場景
      • 保護目標對象:客戶並不知道對象是如何實現具體業務功能的,只要調用代理對象的接口就好了。
      • 加強目標對象:在實現目標對象的業務功能以前或者以後作一些業務實現。Spring-AOP
    • 實現原理
      • 靜態代理:代理對象持有被代理對象的引用。由被代理對象實現目標業務。
      • 動態代理:代理對象持有被代理對象的引用,代碼運行產生一個新的java類,並被編譯從新加載,而後在新的類中執行目標業務實現動態代理。
  • 瞭解靜態代理和動態代理的區別。
    • 靜態代理:被代理對象在代碼中被寫死了,不易擴展,適用於簡單的業務,代碼複用性不高,違背開閉原則。
    • 動態代理:只要是實現了同一個接口的被代理類,均可以代理實現,實現了業務擴展。代碼複用,遵循開閉原則。
  • 瞭解CGLib和JDK-proxy的區別。
    • JDK-proxy動態代理:實現了被代理對象的接口,生成新的class字節碼,利用反射調用實現代理。沒法代理不實現代理業務接口的方法。
    • CGLib動態代理:繼承被代理對象,生成新的class字節碼,經過fastclass機制調用實現代理。沒法代理被final關鍵字修飾的方法

代理模式的定義

爲其餘對象提供一種代理,以控制對這個對象的訪問。代理對象在客戶端和服務端起到中介的做用。是一種結構型設計模式。java

靜態代理

/**
 * @description: 被代理對象須要實現的目標接口
 * @author: lmc
 * @create: 2019-06-12 15:38
 **/
public interface ITarget {
    /**
     * @description: 被代理對象的行爲
     * @return void
     * @date 2019/6/12 15:39
     * @author lmc
     */
    void behavior();
}
/**
 * @description: 真實的被代理的目標對象
 * @author: lmc
 * @create: 2019-06-12 15:41
 **/
public class TargetImpl implements ITarget {

    public void behavior() {
        System.out.println("執行真實的被代理對象的行爲。");
    }
}

下面的類也是一個被代理的目標對象,可是沒有實現ITarget接口設計模式

/**
 * @description: 真實的被代理的目標對象
 * @author: lmc
 * @create: 2019-06-12 15:41
 **/
public class Target{

    public void behavior() {
        System.out.println("執行被代理對象target的行爲。");
    }
}
/**
 * @description: 靜態代理類
 * @author: lmc
 * @create: 2019-06-12 15:45
 **/
public class StaticProxy {

    /**
     *  持有被代理對象的引用
     */
    private ITarget targetImpl;

    /**
     *  持有被代理對象的引用
     */
    private Target target;//一個沒有實現接口的類



    /**
     *  構造方法初始化值
     * @param targetImpl
     */
    public StaticProxy(ITarget targetImpl,Target target){
        this.targetImpl=targetImpl;
        this.target=target;
    }

    /**
     * @description: 被代理以前的加強行爲
     * @date 2019/6/12 15:56
     * @author lmc
     */
    private void beforeBehavior(){
        System.out.println("執行代理以前須要作的一些事情。");
    }

    /**
     * @description: 被代理以後的加強行爲
     * @date 2019/6/12 15:57
     * @author lmc
     */
    private void afterBehavior(){
        System.out.println("執行代理以後須要作的一些事情。");
    }

    /**
     * @description: 開始執行代理
     * @date 2019/6/12 15:59
     * @author lmc
     */

    public void startProxy(){
        beforeBehavior();
        targetImpl.behavior();
        target.behavior();
        afterBehavior();
    }
}
/**
 * @description: 靜態代理客戶端 懶漢式單例
 * @author: lmc
 * @create: 2019-06-12 16:01
 **/
public class StaticProxyClient implements Serializable {

    private final static StaticProxyClient staticProxyClient=new StaticProxyClient();

    private StaticProxyClient(){
        if(null != staticProxyClient){
            throw new RuntimeException("單例類,不容許被反射實例化");
        }
    };

    public static StaticProxyClient getInstance(){
        return staticProxyClient;
    }

    /**
     * @description: 開始靜態代理
     * @date 2019/6/12 16:20
     * @author lmc
     */
    public void startStaticProxy(){
        ITarget targetImpl=new TargetImpl();
        Target target=new Target();
        StaticProxy staticProxy=new StaticProxy(targetImpl,target);
        staticProxy.startProxy();
    }

    /**
     * @description: 重寫readResolve,防止序列化破壞單例
     * @return java.lang.Object
     * @date 2019/6/12 16:18
     * @author lmc
     */
    private Object readResolve(){
        return staticProxyClient;
    }
}
/**
 * @description: 靜態代理測試
 * @author: lmc
 * @create: 2019-06-12 16:10
 **/
public class StaticProxyTest {

    public static void main(String[] args) {

        StaticProxyClient.getInstance().startStaticProxy();
    }
}
靜態代理測試結果

只要代理對象持有被代理對象的引用就能夠實現靜態代理了。ide

JDK-proxy動態代理

/**
 * @description: 真實的被代理的目標對象
 * @author: lmc
 * @create: 2019-06-12 15:41
 **/
public class TargetImpl1 implements ITarget {

    public void behavior() {
        System.out.println("執行被代理對象1的行爲。");
    }
}
/**
 * @description: 真實的被代理的目標對象
 * @author: lmc
 * @create: 2019-06-12 15:41
 **/
public class TargetImpl2 implements ITarget {

    public void behavior() {
        System.out.println("執行被代理對象2的行爲。");
    }
}
/**
 * @description: 真實的被代理的目標對象
 * @author: lmc
 * @create: 2019-06-12 15:41
 **/
public class TargetImpl3 implements ITarget {

    public void behavior() {
        System.out.println("執行被代理對象3的行爲。");
    }
}
/**
 * @description: JDK動態代理對象
 * @author: lmc
 * @create: 2019-06-12 17:00
 **/

public class JdkProxy implements InvocationHandler {

    /**
     * 被代理對象的引用
     */
    private ITarget target;

    /**
     * @description: 獲取代理以後的實例對象
     * @param target 被代理對象
     * @return com.lmc.gp12380.pattern.proxy.ITarget
     * @date 2019/6/12 19:55
     * @author lmc
     */
    public ITarget getProxyInstance(ITarget target){
        this.target = target;
        Class<?> clazz = target.getClass();
        return (ITarget) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        beforeBehavior();
        //經過反編譯工具能夠查看源代碼
        byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{ITarget.class});
        FileOutputStream os = new FileOutputStream("E://$Proxy0.class"); os.write(bytes); os.close();
        Object obj = method.invoke(this.target,args);
        afterBehavior();
        return obj;
    }

    /**
     * @description: 被代理以前的加強行爲
     * @date 2019/6/12 15:56
     * @author lmc
     */
    private void beforeBehavior(){
        System.out.println("執行代理以前須要作的一些事情。");
    }

    /**
     * @description: 被代理以後的加強行爲
     * @date 2019/6/12 15:57
     * @author lmc
     */
    private void afterBehavior(){
        System.out.println("執行代理以後須要作的一些事情。");
    }
}

下面這個類是測試沒有實現ITarget接口的代理工具

/**
 * @description: JDK動態代理對象
 * @author: lmc
 * @create: 2019-06-12 17:00
 **/
public class JdkProxy1 implements InvocationHandler {

    /**
     * 被代理對象的引用
     */
    private Target target;

    /**
     * @description: 獲取代理以後的實例對象
     * @param target 被代理對象
     * @return com.lmc.gp12380.pattern.proxy.ITarget
     * @date 2019/6/12 19:55
     * @author lmc
     */
    public Target getProxyInstance(Target target){
        this.target = target;
        Class<?> clazz = target.getClass();
        return (Target) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        beforeBehavior();
        //經過反編譯工具能夠查看源代碼
        byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{ITarget.class});
        FileOutputStream os = new FileOutputStream("E://$Proxy0.class"); os.write(bytes); os.close();
        Object obj = method.invoke(this.target,args);
        afterBehavior();
        return obj;
    }

    /**
     * @description: 被代理以前的加強行爲
     * @date 2019/6/12 15:56
     * @author lmc
     */
    private void beforeBehavior(){
        System.out.println("執行代理以前須要作的一些事情。");
    }

    /**
     * @description: 被代理以後的加強行爲
     * @date 2019/6/12 15:57
     * @author lmc
     */
    private void afterBehavior(){
        System.out.println("執行代理以後須要作的一些事情。");
    }
}

下面這個類是Jdk-proxy代理生成的class字節碼反編譯以後的java學習

public final class $Proxy0 extends Proxy
        implements ITarget {

    public $Proxy0(InvocationHandler invocationhandler) {
        super(invocationhandler);
    }

    public final boolean equals(Object obj) {
        try {
            return ((Boolean) super.h.invoke(this, m1, new Object[]{
                    obj
            })).booleanValue();
        } catch (Error _ex) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
        return false;
    }

    public final String toString() {
        try {
            return (String) super.h.invoke(this, m2, null);
        } catch (Error _ex) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
        return null;
    }

    public final void behavior() {
        try {
            super.h.invoke(this, m3, null);
            return;
        } catch (Error _ex) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode() {
        try {
            return ((Integer) super.h.invoke(this, m0, null)).intValue();
        } catch (Error _ex) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
        return 0;
    }

    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{
                    Class.forName("java.lang.Object")
            });
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("com.lmc.gp12380.pattern.proxy.ITarget").getMethod("behavior", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (NoSuchMethodException nosuchmethodexception) {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        } catch (ClassNotFoundException classnotfoundexception) {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}

這裏咱們知道了這個反編譯生成的java類實現了ITarget接口測試

/**
 * @description: JDK代理實現測試
 * @author: lmc
 * @create: 2019-06-12 19:49
 **/
public class JdkProxyTest {

    public static void main(String[] args) {

        ITarget target1 = (ITarget) new JdkProxy().getProxyInstance(new TargetImpl1());
        target1.behavior();

        ITarget target2 = (ITarget) new JdkProxy().getProxyInstance(new TargetImpl2());
        target2.behavior();

        ITarget target3 = (ITarget) new JdkProxy().getProxyInstance(new TargetImpl3());
        target3.behavior();

        /**
         *  Target 類沒有實現接口 沒法被jdkproxy代理
         */
        ITarget target4 = (ITarget) new JdkProxy1().getProxyInstance(new Target());
        target4.behavior();
    }
}
JDK-proxy動態代理測試結果

嘿嘿,能夠看到target4代理出錯啦。緣由就是Target類沒有實現ITarget接口。this

CGLib動態代理

/**
 * @description: CGLib動態代理對象
 * @author: lmc
 * @create: 2019-06-12 17:00
 **/
public class CGLibProxy implements MethodInterceptor {

    /**
     * @description: 被代理以前的加強行爲
     * @date 2019/6/12 15:56
     * @author lmc
     */
    private void beforeBehavior(){
        System.out.println("執行代理以前須要作的一些事情。");
    }

    /**
     * @description: 被代理以後的加強行爲
     * @date 2019/6/12 15:57
     * @author lmc
     */
    private void afterBehavior(){
        System.out.println("執行代理以後須要作的一些事情。");
    }


    /**
     * @description: 獲取代理以後的實例對象
     * @param target 被代理對象
     * @return com.lmc.gp12380.pattern.proxy.ITarget
     * @date 2019/6/12 19:55
     * @author lmc
     */
    public Object getProxyInstance(Object target){

        Class<?> clazz = target.getClass();
        //至關於Proxy,代理的工具類
        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 {
        beforeBehavior();
        /**
         *  利用 cglib 的代理類能夠將內存中的 class 文件寫入本地磁盤
         */
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E://cglib_proxy_class/");

        Object obj = methodProxy.invokeSuper(o,objects);
        afterBehavior();
        return obj;
    }
}
/**
 * @description: CGLib代理實現測試
 * @author: lmc
 * @create: 2019-06-12 20:26
 **/
public class CGLibProxyTest {

    public static void main(String[] args) {

        ITarget target1= (ITarget) new CGLibProxy().getProxyInstance(new TargetImpl1());
        target1.behavior();

        ITarget target2= (ITarget) new CGLibProxy().getProxyInstance(new TargetImpl2());
        target2.behavior();

        ITarget target3= (ITarget) new CGLibProxy().getProxyInstance(new TargetImpl3());
        target3.behavior();

        Target target4= (Target) new CGLibProxy().getProxyInstance(new Target());
        target4.behavior();
    }
}
CGLib動態代理測試結果

嘿嘿,能夠看到Target沒有實現ITarget接口,也能實現動態代理。設計

代碼裏面是能夠生成CGLib動態代理的字節碼文件的,也能夠反編譯過來看看,由於我是看不懂,我就不展現了。3d

代理模式的優缺點

優勢
  1. 代理模式能將代理對象與真實被調用的目標對象分離。
  2. 必定程度上下降了系統的耦合度,擴展性好。
  3. 能夠起到保護目標對象的做用。
  4. 能夠對目標對象的功能加強。
缺點
  1. 代理模式會形成系統設計中類的數量增長。
  2. 在客戶端和目標對象增長一個代理對象,會形成請求處理速度變慢。
  3. 增長了系統的複雜度。
相關文章
相關標籤/搜索