1. 程式人生 > 實用技巧 >java設計模式9-代理模式

java設計模式9-代理模式

基本介紹

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

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

3、代理模式有不同的形式,主要有三種靜態代理動態代理(JDK代理/介面代理)Cglib(在記憶體動態的建立物件,而不需要實現介面,也是動態代理)

靜態代理

基本介紹

靜態代理在使用時,需要定義介面或者父類,被代理物件(即目標物件)與代理物件一起實現相同的介面或者繼承相同父類.

應用例項

1、定義一個介面:ITeacherDao

2、目標物件

TeacherDAO實現介面ITeacherDAO

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

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

5、注意:代理物件與目標物件要實現相同的介面,然後通過呼叫相同的方法來呼叫目標物件的方法

思路

程式碼

//介面
public interface ITeacherDao {

    void teach(); // 授課的方法
}
public class TeacherDao implements ITeacherDao {

    @Override
    public void teach() {
        
// TODO Auto-generated method stub System.out.println(" 老師授課中 。。。。。"); } }
/**
 * 代理物件,靜態代理
 */
public class TeacherDaoProxy implements ITeacherDao {
    /**
     * 目標物件,通過介面來聚合
     */
    private ITeacherDao target;


    //構造器
    public TeacherDaoProxy(ITeacherDao target) {
        this.target = target;
    }


    @Override
    
public void teach() { // TODO Auto-generated method stub //方法 System.out.println("開始代理 完成某些操作。。。。。 "); target.teach(); //方法 System.out.println("提交。。。。。"); } }
public class Client {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //建立目標物件(被代理物件)
        TeacherDao teacherDao = new TeacherDao();

        //建立代理物件, 同時將被代理物件傳遞給代理物件
        TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);

        //通過代理物件,呼叫到被代理物件的方法
        //即:執行的是代理物件的方法,代理物件再去呼叫目標物件的方法
        teacherDaoProxy.teach();
    }
}
靜態代理有缺點
1、優點:在不修改目標物件功能的前提下,能通過代理物件對目標功能擴充套件
2、缺點:因為代理物件需要與目標物件實現一樣的介面,所以會有很多代理類
3、一旦介面增加方法,目標物件與代理物件都要維護

動態代理

基本介紹

1、代理物件,不需要實現介面,但是目標物件要實現介面,否則不能用動態代理

2、代理物件的生成,是利用JDKAPI,動態的在記憶體中構建代理物件

3、動態代理:JDK代理/介面代理,cglib代理

JDk中生成代理物件的API

1、代理類所在包:java.lang.reflect.Proxy

2、JDK實現代理只需要使用newProxyInstance方法,但是該方法需要接收三個引數

  a)ClassLoader:代理類的類載入器

  b)Interfaces:目標代理介面,是一個數組

  c)invocationHandler:通過一個呼叫處理器,完成對目標代理物件方法的呼叫

應用例項

思路

程式碼

//介面
public interface ITeacherDao {

    void teach(); // 授課方法
    void sayHello(String name);
}
public class TeacherDao implements ITeacherDao {

    @Override
    public void teach() {
        // TODO Auto-generated method stub
        System.out.println(" 老師授課中.... ");
    }

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

}
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;
                    }
                });
    }
}
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代理

基本介紹

1、靜態代理和JDK代理模式都要求目標物件要實現一個藉口,但是有時候只是一個單獨物件,並沒有實現任何的介面,這個時候可使用目標子類來實現代理,這是cglib代理.

2、cglib代理也叫子類代理,它是在記憶體中構建一個子類物件從而實現對目標物件功能擴充套件

3、cglib是一個強大的高效能的程式碼生成包,它可以在執行期間擴充套件java類與實現java介面.

4、AOP程式設計中如何選擇代理模式

  a)目標物件需要實現介面,JDK代理

  b)目標物件不需要實現介面,cglib代理

5、cglib包的底層是通過使用位元組碼處理框架ASM來轉換位元組碼並生成新的類

實現步驟

1、引入cglib相關依賴

2、在記憶體中動態構建子類,注意代理的類不能為final,否則報錯

a)IllegalArgumentException

3、目標物件的方法如果為final/static,那麼就不會被攔截,即不會執行目標物件額外的業務方法

應用例項

思路

程式碼

public class TeacherDao {

    public String teach() {
        System.out.println(" 老師授課中  , 我是cglib代理,不需要實現介面 ");
        return "hello";
    }
}
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;
    }

}
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);
    }

}

幾種常見的代理模式

1、防火牆代理

  a)內網通過代理穿透防火牆,實現對公網的使用

2、快取代理

  a)請求圖片檔案等資源時,先到快取代理中取,如果取到則ok,取不到,到儲存庫中取,然後快取

3、遠端代理

  a)遠端物件的本地代理,通過它可以把遠端物件當本地物件來呼叫.遠端代理通過網路和真正的遠端物件溝通訊息