模式介紹:能夠將系統中需要的一些橫向的通用邏輯和核心業務邏輯分割開,即改變通用邏輯的時候不影響核心業務邏輯的代碼。
首先介紹靜態代理設計的思路,要實現對某個類進行代理,比如說汽車(Car),有run()和stop()的方法,要對該類中所有方法進行代理(添加同一種事務),那麼該對象產生的代理就必須具有Car中所有的方法。一旦調用Car的方法,如:run(),就先調用代理的run()方法,在方法中再調用Car的run()方法,可想而知,要在前後加上邏輯就很方便了,更重要的是不會影響核心業務邏輯(這裏是指Car的run方法)。
由於要在代理中調用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);
}
}