設計模式之代理模式

代理模式:一個委托類,一個代理類,實現相同的接口,通常是在代理類中有一個委托類的對象,代理類并不會真正的執(zhí)行方法,只是在委托類執(zhí)行方法之前或之后提供一些服務。比如賬戶驗證,權限查看啥的。



代理模式主要分為靜態(tài)代理和動態(tài)代理。
靜態(tài)代理:在它在運行的時候,代理類的.class文件就存在了。
動態(tài)代理:利用反射,在運行時動態(tài)的加載類。


靜態(tài)代理

// Car.java
package com.codeliu.dao;

public interface Car {
    // 預訂車
    public void revCar();
    // vip客戶可以直接買車
    public void buyCar();
} 
// CarImpl.java
package com.codeliu.dao.impl;

import com.codeliu.dao.Car;
/**
 * 委托類
 */
public class CarImpl implements Car {
    public void revCar() {
        System.out.println("我要去預訂一輛車咯");
    }

    public void buyCar() {
        System.out.println("我要買車咯");
    }
}
/ CarProxy.java
package com.codeliu.dao.impl;

import com.codeliu.dao.Car;
/**
 * 代理類
 */
public class CarProxy implement Car {
    private CarImpl ci;
    public CarProxy(CarImpl ci) {
        this.ci = ci;
    }

    public void revCar() {
        System.out.println("該客戶是否有足夠的錢預訂車");
        ci.revCar();
        System.out.println("該客戶預訂完成");
    }

    public void buyCar() {
        System.out.println("該客戶是否是vip客戶");
        ci.buyCar();
        System.out.println("該客戶買車完成");
    }
}
package com.codeliu.test;

import com.codeliu.dao.impl.CarImpl;
import com.codeliu.dao.impl.CarProxy;
public class TestProxy {
    public static void main(String[] args) {
        CarImpl ci = new CarImpl();
        CarProxy cp = new CarProxy(ci);
        cp.revCar();
        cp.buyCar();
    }
}

靜態(tài)代理是一個代理類只能為一個委托類服務,如果有多個委托類實現了同一個接口,則就要有多個代理類,除了委托類調用的方法不一樣,
其他操作都一樣,這樣勢必就會產生很多重復代碼。這時就是動態(tài)代理類上場的時候了。



動態(tài)代理又分為JDK動態(tài)代理和Cglib動態(tài)代理。



JDK動態(tài)代理要使用Java提供的InvocationHandler接口和Proxy類的newProxyInstance方法。

// InvocationHandler接口
public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

nvocationHandler接口接口中只有一個invoke方法,所有動態(tài)代理類都要實現這個接口,并實現invoke方法。
關于該方法的三個參數
proxy表示被代理的對象
method表示被代理對象的方法
args表示方法的參數

// Proxy類的newProxyInstance方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

loader表示委托類的類加載器
interfaces表示委托類實現的接口(這就說明委托類一定要實現了接口才能使用)
h表示InvocationHandler接口的子實例,也就是動態(tài)代理類的實例



為了說明動態(tài)代理類和靜態(tài)代理類的不同,我定義了兩個委托類

// Car.java
package com.codeliu.dao;

public interface Car {
    // vip客戶可以直接買車
    public void buyCar();
} 
// CarImp1.java
package com.codeliu.dao.impl;
import com.codeliu.dao.Car;
/**
 * 委托類1
 */
public class CarImp1 implements Car {
    public void buyCar() {
        System.out.println("我是vip1");
    }
}
// CarImp2.java
package com.codeliu.dao.impl;
import com.codeliu.dao.Car;
/**
 * 委托類2
 */
public class CarImp2 implements Car {
    public void buyCar() {
        System.out.println("我是vip2");
    }
}
// CarProxy.java
package com.codeliu.dao.impl;

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

/**
 * 動態(tài)代理類
 */
public class CarProxy implements InvocationHandler {
    private Object target;
    /**
     * 綁定一個委托對象并獲得一個代理類對象
     * @param  target [description]
     * @return        [description]
     */
    public Object bind(Object target) {
        this.target = target;
        // 取得代理對象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), 
                                       this);
    }

    @Override
    //這個方法并不是我們自己去調用
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("該客戶是否是vip客戶");
        // 執(zhí)行委托類的方法
        Object result = method.invoke(target,args);
        System.out.println("該客戶買車完成");
        return result;
    }
}
package com.codeliu.test;

import com.codeliu.dao.impl.CarImp1;
import com.codeliu.dao.impl.CarImp2;
import com.codeliu.dao.impl.CarProxy;
import com.codeliu.dao.Car;
public class TestProxy {
    public static void main(String[] args) {
        CarProxy cp = new CarProxy();
        // 傳入一個實現了該接口的實例就行
        Car car = (Car)cp.bind(new CarImp1());
        // Car car = (Car)cp.bind(new CarImp2());
        car.buyCar();
    }
} 

輸出

該客戶是否是vip客戶
我是vip1
該客戶買車完成

以上就是一個JDK動態(tài)代理的例子,但我們可以看到,如果委托類沒有實現接口的話,就不能使用newProxyInstance方法,進而不能使用JDK動態(tài)代理。這時候,Cglib動態(tài)代理上場了。



Cglib是針對類來實現代理的,對指定的目標類生成一個子類,通過方法攔截技術攔截所有父類方法的調用。因為是生成子類,所以就不能用在final修飾的類上。

要使用Cglib,首先的下載jar包。

package com.codeliu.cglibproxy;
public class Train {
    public void move() {
        System.out.println("火車行駛中。。。。");
    }
}
package com.codeliu.cglibproxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor {
    
    private Enhancer enhancer = new Enhancer();
    
    /**
     * 獲得代理類
     * @return
     */
    public Object getProxy(Class<?> c) {
        // 設置創(chuàng)建子類的類
        enhancer.setSuperclass(c);
        // 回調方法
        enhancer.setCallback(this);
        // 返回代理對象
        return enhancer.create();
    }

    @Override
    /**
     * 攔截所有目標方法的調用
     * obj 目標類的實例
     * m 目標方法的反射對象
     * args 方法的參數
     * proxy 代理類的實例
     */
    public Object intercept(Object obj, Method m, Object[] args,
            MethodProxy proxy) throws Throwable {
        System.out.println("火車啟動啦??!");
        // 代理類調用父類的方法
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("火車停止啦??!");
        return result;
    }

}
package com.codeliu.cglibproxy;
public class Test {
    public static void main(String[] args) {
        CglibProxy proxy = new CglibProxy();
        Train t = (Train)proxy.getProxy(Train.class);
        t.move();
    }
}

輸出

火車啟動啦??!
火車行駛中。。。。
火車停止啦?。?



上面的程序我都是用sublime手敲的,不知道有沒錯誤。



吃完飯我又在Eclipse運行了一遍,確保沒有錯誤,要對自己和大家負責,不要傳播錯誤的知識,嘿嘿。

?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容