設計模式(二)—— 代理模式

簡述

代理模式是提供對目標對象另外一種訪問方式的模式;

優點:可以在目標對象實現的基礎上,添加額外的功能,即擴展目標對象的功能;

思想:在添加對目標對象新的操作時,不用修改別人的代碼,只需要通過代碼對象,去擴展目標對象新的功能;

三種代理模式

靜態代理

定義一個接口或者父類, 目標對象和代理對象同時繼承或者實現該接口;

優點: 不用修改目標對象的情況下,對目標對象進行新的擴展

缺點:產生過多的代理對象,造成代碼冗餘;並且當共同實現或繼承同一個父類或接口,當父類或接口改變時,目標對象與代理對象都難以維護;

代碼示例:

/** 
* common interface
*/
public interface UserDao{
    void save();
}

/** 
 * target object
*/
public class UserDaoImpl implements UserDao {
    public void save() {
        System.out.println("-----save the data ------");
    }
}

/** 
 * proxy object 
 */
public class UserDaoProxy implements UserDao {
    private UserDao userDao; //use to receive target object who like UserDaoImpl

    public UserDaoProxy(UserDao userDao){
        this.userDao = userDao;
    }

    public void save(){
        System.out.println("-----add new function----");
        userDao.save();
        System.out.println("------add new function----");
    }
}

/** 
 * test method 
 */
public class test {
    public static void main(String[] args) {
        UserDao targetObject = new UserDaoImpl();  //new a target object

        //bulid relationships bethween tagert object and proxy object
        UserDaoProxy proxyObject = new UserDaoProxy(targetObject); 

        proxyObject.save();//execute proxy method
    }
}

動態代理

特點:代理對象不用實現接口;代理對象的生成,是通過JDK提供的API,動態幫我們在內存中構建代理對象;(需要我們指定目標對象的接口的類型)

JDK中動態生成代理對象的API:
包所在:java.lang.reflect.Proxy

實現方法:

/** 
* loader 指定目標對象的類加載器 
* interface 目標對象實現的接口的類型 
* h 執行目標對象的方法時,會觸發事件處理器的方法,將當前執行目標對象的發放作爲參數傳入
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h )

代碼示例:
/** 
 * create proxy object factory 
 */
public class ProxyFactory() {
    //targer object
    private Object target;

    public ProxyFactory(Obkect target) {
        this.target = target;
    }

    //return a proxy object
    public Object getProxyInstance() {
        /**  
        * InvocationHandler接口只定義了一個invoke方法,因此對於這樣的接口,我們不用單獨去定義一個類來實現該接口,
        * 而是直接使用一個匿名內部類來實現該接口,new InvocationHandler() {}就是針對InvocationHandler接口的匿名實現類 
        *
        * 在invoke方法編碼指定返回的代理對象乾的工作 
        * proxy : 把代理對象自己傳遞進來  
        * method:把代理對象當前調用的方法傳遞進來  
        * args:把方法參數傳遞進來 
        *  
        * 當調用代理對象的person.sing("冰雨");或者 person.dance("江南style");方法時, 
        * 實際上執行的都是invoke方法裏面的代碼, 
        * 因此我們可以在invoke方法中使用method.getName()就可以知道當前調用的是代理對象的哪個方法 
        */
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getClassInterface(),
            new InvocationHandler() {
                @Override
               public Object invoke(Object proxy,Method method,Object[] arge) {
                    if (method.getName().equals("save")) {
                        System.out.println("--execute your new method in here--");
                        //execute target object method in here
                        Object returnValue = method.invoke(target, args);
                        System.out.println("--execute your new method in here--");
                    }
                    return returnValue;
                }
            }
        );
    }
}

/** 
 * test 
 */
public class test {
    public static void main(String[] args) {
        UserDao targetObject = new UserDaoImpl();
        //create a proxy object
        UserDao proxyObject = (UserDao) new ProxyFactory(target).getProxyIntence();
        //use proxy object execute target method
        proxyObject.save();
    }
}

總結:動態代理對象不需要實現接口,但目標對象一定要實現接口,否則JDK無法生成目標對象的代理對象;


Cglib代理

靜態代理和動態代理都的目標對象都是需要實現一個接口的,但是有時候一些對象就是一個獨立的個體,並沒有實現接口,這時候就有了Cglib代理的出現;它是在內存中,構建一個目標對象的子類,從而實現對目標對象的擴展;

代碼示例:

/** 
 * target object 
 */
public class UserDao {
    public void save() {
        System.out.println("----save one data ---");
    }
}

/** 
 * Cglib procy factory 
 */
public class ProxyFactory implements MethodInterceptor{
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    //create proxy object
    public Object getProxyInstance() {
        //tool object
        Enhancer en = new Enhancer();
        //set target object's class
        en.setSuperClass(target.getClass());
        //set callback
        en.setCallback(this);
        //create child class(proxy object)
        return en.cteate();
    }

    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) {
        System.out.println("-- your function --");
        Object returnValue = method.invoke(target, args);
        System.out.println(" -- your function --");
        return returnValue;
    }
}

/** 
 * test method 
 */
public class test {
    public static void main(String[] args) {
        UserDao userDao = new UserDao();
        UserDao proxyDao = (UserDao)new ProxyFactory(userDao).getProxyInstance();
        proxyDao.save();
    }
}

在Spring的AOP中

如果加入容器的目標對象有實現接口,則用動態代理,
如果目標對象沒有實現接口,則用Cglic代理;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章