模式介绍:能够将系统中需要的一些横向的通用逻辑和核心业务逻辑分割开,即改变通用逻辑的时候不影响核心业务逻辑的代码。
首先介绍静态代理设计的思路,要实现对某个类进行代理,比如说汽车(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);
}
}