1. 程式人生 > 實用技巧 >設計模式之十一代理模式

設計模式之十一代理模式

基本介紹

代理模式:

1)為一個物件提供一個替身,以控制對這個物件的訪問。即通過代理物件訪問目標物件,這樣做的好處是:可以在目標物件實現的基礎上,增強額外的功能操作,即擴充套件目標物件的功能。

2) 被代理的物件可以是遠端物件、建立開銷大的物件或需要安全控制的物件

3) 代理模式有不同的形式, 主要有三種 靜態代理、動態代理 (JDK代理、介面代 理)和 Cglib代理 (可以在記憶體動態的建立物件,而不需要實現介面, 他是屬於 動態代理的範疇) 。

靜態代理:

實列

 具體要求

1) 定義一個介面:ITeacherDao

2) 目標物件TeacherDAO實現介面ITeacherDAO

3) 使用靜態代理方式,就需要在代理物件TeacherDAOProxy中也實現ITeacherDAO

4) 呼叫的時候通過呼叫代理物件的方法來呼叫目標物件.

5) 特別提醒:代理物件與目標物件要實現相同的介面,然後通過呼叫相同的方法來 呼叫目標物件的方法。

uml類圖

程式碼演示:

package com.hy.proxy.staticproxy;

/**
 * @author hanyong
 * @date 2020/11/26 22:30
 */
public interface ITeacherDao {

    void teach();
}

package com.hy.proxy.staticproxy;

/** * @author hanyong * @date 2020/11/26 22:30 */ public class TeacherDao implements ITeacherDao { @Override public void teach() { System.out.println("初始教學方式"); } } package com.hy.proxy.staticproxy; /** * @author hanyong * @date 2020/11/26 22:34 */ public class TeacherDAOProxy implements
ITeacherDao { private ITeacherDao iTeacherDao; public TeacherDAOProxy(ITeacherDao iTeacherDao) { this.iTeacherDao = iTeacherDao; } @Override public void teach() { System.out.println("代理前處理"); iTeacherDao.teach(); System.out.println("代理後處理"); } } package com.hy.proxy.staticproxy; /** * @author hanyong * @date 2020/11/26 22:35 */ public class Client { public static void main(String[] args) { ITeacherDao teacherDao = new TeacherDao(); ITeacherDao teacherDaoProxy = new TeacherDAOProxy(teacherDao); teacherDaoProxy.teach(); } }

執行結果:

優缺點:

1) 優點:在不修改目標物件的功能前提下, 能通過代理物件對目標功能擴充套件

2) 缺點:因為代理物件需要與目標物件實現一樣的介面,所以會有很多代理類

3) 一旦介面增加方法,目標物件與代理物件都要維護

動態代理:

程式碼實現:

package com.hy.proxy.dynamicproxy;

/**
 * @author hanyong
 * @date 2020/11/26 22:30
 */
public interface ITeacherDao {

    void teach();
    void sayHello(String name);
}

package com.hy.proxy.dynamicproxy;

/**
 * @author hanyong
 * @date 2020/11/26 22:30
 */
public class TeacherDao implements ITeacherDao {
    @Override
    public void teach() {
        System.out.println("初始教學方式");
    }

    @Override
    public void sayHello(String name) {
        // TODO Auto-generated method stub
        System.out.println("hello " + name);
    }
}

package com.hy.proxy.dynamicproxy;

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

/**
 * @author hanyong
 * @date 2020/11/27 21:31
 */
public class ProxyFactory {
    //維護一個目標物件 , Object
    private Object target;

    //構造器 , 對target 進行初始化
    public ProxyFactory(Object target) {

        this.target = target;
    }

    //給目標物件 生成一個代理物件
    public Object getProxyInstance() {

        //說明
        /*
         *  public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

            //1. ClassLoader loader : 指定當前目標物件使用的類載入器, 獲取載入器的方法固定
            //2. Class<?>[] interfaces: 目標物件實現的介面型別,使用泛型方法確認型別
            //3. InvocationHandler h : 事情處理,執行目標物件的方法時,會觸發事情處理器方法, 會把當前執行的目標物件方法作為引數傳入
         */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // TODO Auto-generated method stub
                        System.out.println("JDK代理開始~~");
                        //反射機制呼叫目標物件的方法
                        Object returnVal = method.invoke(target, args);
                        System.out.println("JDK代理提交");
                        return returnVal;
                    }
                });
    }
}

package com.hy.proxy.dynamicproxy;

/**
 * @author hanyong
 * @date 2020/11/27 21:32
 */
public class Client {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //建立目標物件
        ITeacherDao target = new TeacherDao();

        //給目標物件,建立代理物件, 可以轉成 ITeacherDao
        ITeacherDao proxyInstance = (ITeacherDao) new ProxyFactory(target).getProxyInstance();

        // proxyInstance=class com.sun.proxy.$Proxy0 記憶體中動態生成了代理物件
        System.out.println("proxyInstance=" + proxyInstance.getClass());

        //通過代理物件,呼叫目標物件的方法
        proxyInstance.teach();

        proxyInstance.sayHello(" tom ");
    }
}

輸出結果

cglib代理

uml類圖

程式碼實現:

package com.hy.proxy.cglib;

/**
 * @author hanyong
 * @date 2020/11/27 21:41
 */
public class TeacherDao {
    public String teach() {
        System.out.println(" 老師授課中  , 我是cglib代理,不需要實現介面 ");
        return "hello";
    }
}

package com.hy.proxy.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author hanyong
 * @date 2020/11/27 21:41
 */
public class ProxyFactory implements MethodInterceptor {

    //維護一個目標物件
    private Object target;

    //構造器,傳入一個被代理的物件
    public ProxyFactory(Object target) {
        this.target = target;
    }

    //返回一個代理物件:  是 target 物件的代理物件
    public Object getProxyInstance() {
        //1. 建立一個工具類
        Enhancer enhancer = new Enhancer();
        //2. 設定父類
        enhancer.setSuperclass(target.getClass());
        //3. 設定回撥函式
        enhancer.setCallback(this);
        //4. 建立子類物件,即代理物件
        return enhancer.create();

    }

    //重寫  intercept 方法,會呼叫目標物件的方法
    @Override
    public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("Cglib代理模式 ~~ 開始");
        Object returnVal = method.invoke(target, args);
        System.out.println("Cglib代理模式 ~~ 提交");
        return returnVal;
    }
}

package com.hy.proxy.cglib;

/**
 * @author hanyong
 * @date 2020/11/27 21:42
 */
public class Client {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //建立目標物件
        TeacherDao target = new TeacherDao();
        //獲取到代理物件,並且將目標物件傳遞給代理物件
        TeacherDao proxyInstance = (TeacherDao) new ProxyFactory(target).getProxyInstance();

        //執行代理物件的方法,觸發intecept 方法,從而實現 對目標物件的呼叫
        String res = proxyInstance.teach();
        System.out.println("res=" + res);
    }
}

執行結果: