設計模式 -- 代理模式(Proxy)

模式介紹:能夠將系統中需要的一些橫向的通用邏輯和核心業務邏輯分割開,即改變通用邏輯的時候不影響核心業務邏輯的代碼。

首先介紹靜態代理設計的思路,要實現對某個類進行代理,比如說汽車(Car),有run()stop()的方法,要對該類中所有方法進行代理(添加同一種事務),那麼該對象產生的代理就必須具有Car中所有的方法。一旦調用Car的方法,如:run(),就先調用代理的run()方法,在方法中再調用Carrun()方法,可想而知,要在前後加上邏輯就很方便了,更重要的是不會影響核心業務邏輯(這裏是指Carrun方法)

    由於要在代理中調用Car的具體的方法,所以可以使用聚合的方式,將Car當成成員變量加到代理類中,而這樣還不夠靈活,因爲這樣就只能代理Car類對象了,所以更好的做法是定義一個接口,將接口聚合到代理類中,從而從只能代理某個類擴展成代理實現了某個接口的所有類。下面以產生坦克類的代理,計算模擬坦克的運行時間爲例:

Ø  可以運動的” -- Moveable接口 – Moveable.java

package com.yilong.designpattern.proxy;

public interface Moveable {

    void move();

    void stop();

}

Ø  坦克 – Tank.java,實現了Moveable接口

package com.yilong.designpattern.proxy;

import java.util.Random;

 

public class Tank implements Moveable {

    public void move() {

       System.out.println("Tank Moving...");

       try {

           Thread.sleep(new Random().nextInt(10000));

       } catch (InterruptedException e) {

           e.printStackTrace();

       }

    }

    public void stop() {

       System.out.println("Tank Stopping...");

       try {

           Thread.sleep(new Random().nextInt(10000));

       } catch (InterruptedException e) {

           e.printStackTrace();

       }

    }

}

Ø  坦克代理 – TimeProxy.java,綜上所述,需要實現要代理的對象實現的接口,

Tank對應的Moveable接口,這樣TimeProxy就必須實現Moveable的方法。同時,需要將Moveable接口聚合到TimeProxy中,使得在代理過程中可以調用代理對象的方法。

package com.yilong.designpattern.proxy;

public class TimeProxy implements Moveable {

    Moveable moveable;

    public TimeProxy (Moveable moveable) {

       this.moveable = moveable;

    }

    public void move() {

       long start = System.currentTimeMillis();

       moveable.move();

       long end = System.currentTimeMillis();

       System.out.println("Moved Time:" + (end - start));

    }

    public void stop() {

       long start = System.currentTimeMillis();

       moveable.stop();

       long end = System.currentTimeMillis();

       System.out.println("Stopped Time:" + (end - start));

   

    }

}

Ø  專門創建代理的類Proxy – Proxy.java

package com.yilong.designpattern.proxy;

 

public class Proxy {

    public static Object newProxyInstance(Moveable m) {

       return new TimeProxy(m);

    }

}

Ø  測試類 – Client.java

package com.yilong.designpattern.proxy;

public class Client {

    public static void main(String[] args) {

       Tank t = new Tank();

       Car c = new Car();

       //Moveable m = new TankTimeProxy(t);

       Moveable m = (Moveable)Proxy.newProxyInstance(c);

       m.move();

       m.stop();

    }

}

    說明:此處的TimeProxy能代理實現了Moveable接口的任何類,而不僅僅是Tank,也可是Car:

Ø  汽車 – Car.java

package com.yilong.designpattern.proxy;

import java.util.Random;

public class Car implements Moveable {

    public void move() {

       System.out.println("Car Moving...");

       try {

           Thread.sleep(new Random().nextInt(5000));

       } catch (InterruptedException e) {

           e.printStackTrace();

       }

    }

    public void stop() {

       System.out.println("Car Stopping...");

       try {

           Thread.sleep(new Random().nextInt(5000));

       } catch (InterruptedException e) {

           e.printStackTrace();

       }

    }

}

    上述的代理只能代理實現了某個接口的類,而且代理只是關於時間日誌記錄的代理,而理想的情況是:

²  能夠動態代理所有實現了接口的類

²  能夠動態指定要實現怎樣的代理(日誌記錄、性能統計、安全控制、事務處理等)

因此考慮使用動態代理模式,根據上面所說,關鍵需要實現以下幾點:

²  使用JAVA提供的Compiler在構造代理類時根據傳遞進來的接口,使得總代理類

實現該接口,並實現裏面的所有方法;

²  創建代理類的共同接口,即所有代理類都必須實現該接口,並實現裏面的invoke

方法,invoke方法其中一個參數是Method,而代理類已經把代理對象聚合進來了,故只需要對代理對象調用Method方法即可。具體實現如下:

Ø  接口Moveable – Moveable.java

package com.yilong.designpattern.proxy;

public interface Moveable {

    void move();

    void stop(int i, int j);

}

Ø  代理類接口 – InvocationHandler.java。該接口有個抽象的方法invoke

參數包括生成的代理的對象Object o,要調用的方法Method m,方法的參數Object[] parameters

package com.yilong.designpattern.proxy;

import java.lang.reflect.Method;

public interface InvocationHandler {

    public void invoke(Object o, Method m, Object[] parameters);

}

Ø  時間記錄的代理 – TimeHandler.java。實現InvocationHandler接口,將

代理的對象聚合到類中:private Object targer;

package com.yilong.designpattern.proxy;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

public class TimeHandler implements InvocationHandler {

    private Object target;

   

    public TimeHandler(Object target) {

       super();

       this.target = target;

    }

    public void invoke(Object o, Method m, Object[] parameters) {

       long startTime = System.currentTimeMillis();

       System.out.println("Start Time:" + startTime);

       try {

           m.invoke(target, parameters);

       } catch (Exception e) {

           e.printStackTrace();

       }

       long endTime = System.currentTimeMillis();

       System.out.println("End Time:" + endTime);

       System.out.println("Cost Time:" + (endTime - startTime));

    }

    public Object getO() {

       return target;

    }

    public void setO(Object o) {

       this.target = o;

    }

}

Ø  坦克 – Tank.java。實現了Moveable接口

package com.yilong.designpattern.proxy;

import java.util.Random;

public class Tank implements Moveable {

    public void move() {

       System.out.println("Tank Moving...");

       try {

           Thread.sleep(new Random().nextInt(10000));

       } catch (InterruptedException e) {

           e.printStackTrace();

       }

    }

    public void stop(int i, int j) {

       System.out.println("Tank Stopping...");

       try {

           Thread.sleep(new Random().nextInt(10000));

       } catch (InterruptedException e) {

           e.printStackTrace();

       }

    }

}

Ø  實現動態代理的核心類Proxy – Proxy.java。根據傳遞進來的接口和代理類產

生新的代理實例。

package com.yilong.designpattern.proxy;

import java.io.File;

import java.io.FileWriter;

import java.lang.reflect.Constructor;

import java.lang.reflect.Method;

import java.net.URL;

import java.net.URLClassLoader;

import javax.tools.JavaCompiler;

import javax.tools.StandardJavaFileManager;

import javax.tools.ToolProvider;

import javax.tools.JavaCompiler.CompilationTask;

public class Proxy {

    public static Object newProxyInstance(Class infc,

InvocationHandler h) throws Exception {

       String methodStr = "";//接收代理實例要實現的全部方法

       String parameterStr = "";//代理對象的方法中的參數字符串

       String parameterValue = "";//構造Method需要的參數類型字符串

       String objStr = "";//調用代理對象的方法時需要的參數列表

       String rt = "/r/n";//換行符

       //存放傳遞進來的接口的全部方法;

       Method[] methods = infc.getMethods();

       for(Method m : methods) {

           parameterStr = "";

           parameterValue = ", ";

           objStr = "";

           char identify = 'a';

           int index = 0;

           for(Class c : m.getParameterTypes()) {

              parameterValue += c.getCanonicalName() + ".class, ";

              parameterStr += c.getCanonicalName() + " " + identify

+ ", ";

              objStr += "        objs[" + index + "] = " + identify

+ ";" + rt;

              identify ++;

              index ++;

           }

           parameterValue = parameterValue.substring(0,

parameterValue.length() - 2);

           if(parameterStr.length() >= 2) {

              parameterStr = parameterStr.substring(0,

parameterStr.length() - 2);

           }

           methodStr += "    public void " + m.getName() + "(" + parameterStr + ") {" + rt +

                      "        Object[] objs = new Object[" + index-- + "];" + rt + objStr + rt +

                      "        try {" + rt +

                      "            Method md = " + infc.getName() + ".class.getMethod(/"" + m.getName() + "/"" + parameterValue + ");" + rt +

                      "            h.invoke(this, md, objs);" + rt +

                      "        } catch(Exception e) {" + rt +

                      "            e.printStackTrace();" + rt +

                      "        }" + rt +

                      "    }" + rt;

       }

      

       String src =

           "package com.yilong.designpattern.proxy;" + rt +

           "import java.lang.reflect.Method;" + rt +

           "public class NewProxy implements " + infc.getName() + "{" + rt +

          

           "    com.yilong.designpattern.proxy.InvocationHandler h;" + rt +

           "    public NewProxy(InvocationHandler h) {" + rt +

           "        this.h = h;" + rt +

           "    }" + rt + methodStr +

           "}";

       //System.out.println(System.getProperty("user.dir"));//D:/MyEclipse/Proxy1

       String fileName = "D:/MyEclipse/src/com/yilong/designpattern/proxy/NewProxy.java";

       File f = new File(fileName);

        FileWriter fw = new FileWriter(f);

       fw.write(src);

       fw.close();

      

       //compile

       JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

       StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);

       Iterable units = fileMgr.getJavaFileObjects(fileName);

       CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);

       t.call();

       fileMgr.close();

      

       //load into memery and create an instance

       URL[] urls = new URL[] {new URL("file:/D:/MyEclipse/src/")};

        URLClassLoader ul = new URLClassLoader(urls);

       Class c = ul.loadClass("com.yilong.designpattern.proxy.NewProxy");

       //System.out.println(c);

       Constructor ctr = c.getConstructor(InvocationHandler.class);

       Object moveObject = ctr.newInstance(h);

       return moveObject;

    }

}

Ø  測試類 – Client.java

package com.yilong.designpattern.proxy;

public class Client {

    public static void main(String[] args) throws Exception {

       Tank t = new Tank();

       InvocationHandler timeHandler = new TimeHandler(t);

       Moveable m = (Moveable)Proxy.newProxyInstance

(Moveable.class, timeHandler);

       m.move();

       m.stop(1, 2);

    }

}

Ø  動態編譯生成的文件 – NewProxy.java

package com.yilong.designpattern.proxy;

import java.lang.reflect.Method;

public class NewProxy implements com.yilong.designpattern.proxy.Moveable{

    com.yilong.designpattern.proxy.InvocationHandler h;

    public NewProxy(InvocationHandler h) {

        this.h = h;

    }

    public void stop(int a, int b) {

        Object[] objs = new Object[2];

        objs[0] = a;

        objs[1] = b;

        try {

            Method md = com.yilong.designpattern.proxy.Moveable.class.getMethod("stop", int.class, int.class);

            h.invoke(this, md, objs);

        } catch(Exception e) {

            e.printStackTrace();

        }

    }

    public void move() {

        Object[] objs = new Object[0];

        try {

            Method md = com.yilong.designpattern.proxy.Moveable.class.getMethod("move");

            h.invoke(this, md, objs);

        } catch(Exception e) {

            e.printStackTrace();

        }

    }

}

    下面是JDK的代理,主要涉及JDK中的兩個對象:

(1)     Proxy : 跟上面模擬的相似,是用來產生總代理的一個實例,需要傳遞代理類的類

型、代理的接口對象和代理對象(已經接受了要代理的那個類的一個實例)

(2)     InvocationHandler接口 : 所有要實現代理的類(事務處理、異常處理等)都需要實

現該接口,並實現接口的invoke方法;

Ø  文件User.java

package com.yilong.jdk.proxy;

public class User {

    private int id;

    private String name;

   

    public User(int id, String name) {

       this.id = id;

       this.name = name;

    }

   

    public int getId() {

       return id;

    }

    public void setId(int id) {

       this.id = id;

    }

    public String getName() {

       return name;

    }

    public void setName(String name) {

       this.name = name;

    }

}

Ø  文件UserService.java

package com.yilong.jdk.proxy;

public interface UserService {

    public void addUser(User u);

}

Ø  文件UserServiceImpl.java

package com.yilong.jdk.proxy;

import java.util.Random;

public class UserServiceImpl implements UserService {

    public void addUser(User u) {

       System.out.println(u.getId());

       System.out.println("1. Add User To Database!");

       System.out.println("2. Add Logger To LogFile!");

       try {

           Thread.sleep(new Random().nextInt(5000));

       } catch (InterruptedException e) {

           e.printStackTrace();

       }

    }

}

Ø  文件TimeHandler.java – 日誌記錄的代理

package com.yilong.jdk.proxy;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

public class TimeHandler implements InvocationHandler {

    private Object target;

    public TimeHandler(Object target) {

       super();

       this.target = target;

    }

    public Object invoke(Object proxy, Method method, Object[] args)

           throws Throwable {

       long startTime = System.currentTimeMillis();

       System.out.println("Start Time:" + startTime);

       Object obj = method.invoke(target, args);

       long endTime = System.currentTimeMillis();

       System.out.println("End Time:" + endTime);

       return obj;//將調用method後的返回值作爲參數傳遞回去

    }

}

Ø  文件TransactionHandler.java – 事務處理的代理

package com.yilong.jdk.proxy;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

public class TransactionHandler implements InvocationHandler {

    private Object target;

    public TransactionHandler(Object target) {

       this.target = target;

    }

    public Object invoke(Object proxy, Method method, Object[] args)

           throws Throwable {

       System.out.println("Transaction Strat!");

       Object obj = method.invoke(target, args);

       System.out.println("Transaction Commit!");

       return obj;

    }

}

Ø  測試類Client.java

package com.yilong.jdk.proxy;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Proxy;

public class Client {

    public static void main(String[] args) {

       User u = new User(1, "yilong");

       UserService userServiceImpl = new UserServiceImpl();

       //產生事務代理對象,傳遞需要被代理的對象實例

       InvocationHandler transactionHandler = new

TransactionHandler(userServiceImpl);

       //產生時間日誌的代理對象,傳遞需要被代理的對象實例

       InvocationHandler timeHandler = new

TimeHandler(transactionHandler);

       InvocationHandler TimeTransaction =

(InvocationHandler)Proxy.newProxyInstance(

              transactionHandler.getClass().getClassLoader(),

              transactionHandler.getClass().getInterfaces(),

              timeHandler);

      

       UserService userServiceProxy =

(UserService)Proxy.newProxyInstance(

userServiceImpl.getClass().getClassLoader(),

userServiceImpl.getClass().getInterfaces(), TimeTransaction);

              userServiceProxy.addUser(u);

    }

}

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