代理模式
代理模式:借用百度百科的定义{
}
简单来说就是,有个人要做某件事,让另一个人代替他去做(比如一个人要去追求一个女生,要送情书,但是害羞,就叫好哥们去送。。。-_-||当然是选择原谅她)
我们用代码实现:
有个美丽的姑娘:
package com.lin.proxy;
public class Girl {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
有个送情书的汉子:(将送情书,礼物的行为定义为接口,追求者实现接口)
package com.lin.proxy;
public interface GiveGift {
public void sendFlower();
public void sendLoveLetter();
}
package com.lin.proxy;
public class Boy implements GiveGift {
private Girl mm;
public Boy(Girl mm){
this.mm = mm;
}
@Override
public void sendFlower() {
// TODO 送花
System.out.println(mm.getName()+"送你一朵花");
}
@Override
public void sendLoveLetter() {
// TODO 送情书
System.out.println(mm.getName()+"送你一封信");
}
}
这个时候,由于种种原因,boy没有自己出面,让别人代替(这时候是代替者与girl见面,girl看不到boy),如下所示:
代替者也实现GiveGift接口,但是礼物是boy提供的,所以,代替者只能调用boy的方法:
package com.lin.proxy;
public class ProxyBoy implements GiveGift {
private Boy boy;
public ProxyBoy(Boy boy){
this.boy = boy;
}
@Override
public void sendFlower() {
// TODO 代替送花
boy.sendFlower();
}
@Override
public void sendLoveLetter() {
// TODO 代替送情书
boy.sendLoveLetter();
}
public Boy getBoy() {
return boy;
}
public void setBoy(Boy boy) {
this.boy = boy;
}
}
那么,以下情况就会发生:
package com.lin.proxy;
public class Test {
public static void main(String[] args) {
// TODO 原谅的颜色在飘
Girl mm = new Girl();
mm.setName("小红");
Boy boy = new Boy(mm);
ProxyBoy proxyBoy = new ProxyBoy(boy);
proxyBoy.sendFlower();
proxyBoy.sendLoveLetter();
}
}
这便是最简单形式的代理模式,我们可以发现如果代替者想记录boy送了哪些东西,要记日志,那么他就可以再自己的方法内做操作,不会影响boy。(扩充:如果没有inteface,可以使用cglib实现代理)
复杂版(动态代理):
现在假设这个代理者开展了一个代替送东西的业务(快递),他要为任何人送任何东西,那么因为每个人要送的东西不一样,送的对象也不一样,所以代理者必须实现,根据目标的不同进行动态调整:
参考上面,代替者是实现被代理对象的接口来进行代理,那么,如果这个步骤能够变成根据接口,生成代码并编译使用,那么就能动态了。(java 的api已经为我们提供了动态代理的类 Proxy)。
JDK的API提供的使用例子
InvocationHandler handler = new MyInvocationHandler(...);
Class proxyClass = Proxy.getProxyClass(
Foo.class.getClassLoader(), new Class[] { Foo.class });
Foo f = (Foo) proxyClass.
getConstructor(new Class[] { InvocationHandler.class }).
newInstance(new Object[] { handler });
我们模仿一个试试(反射是个好东西):
我们需要一个动态代理类MyProxy,需要一个拥有接口的被代理的对象(没有接口的对象想实现代理使用CGLib,继承实现),,需要一个InvocationHandler,用于生成实现对应功能的代理类
先实现InvocationHandler:
package com.lin.proxy;
import java.lang.reflect.Method;
public interface InvocationHandler {
void invoke(Object obj,Method method,Object[] args);
}
假设代理者需要在送物品前后记录数据:
package com.lin.proxy;
import java.lang.reflect.Method;
public class RecodeHandler implements InvocationHandler {
private Object target;
public RecodeHandler(Object target) {
this.target = target;
}
@Override
public void invoke(Object obj,Method method,Object[] args) {
long start = System.currentTimeMillis();
System.out.println("Start Time"+start);
System.out.println(obj.getClass().getName());
//记录数据·········
try {
//送出物品
method.invoke(target, args);
} catch (Exception e) {
e.printStackTrace();
}
//送出后进行记录········
long end = System.currentTimeMillis();
System.out.println("Moving time:"+(end-start));
}
}
Myproxy:
package com.lin.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 MyProxy { public static Object newProxyInstance(Class interfaces,Object[] args,InvocationHandler handler) throws Exception { //拼凑出代理类的源代码文件 String methodStr = ""; String rt = "\r\n"; Method[] methods = interfaces.getMethods(); //根据interface获取所有方法(模仿jdk 使用handler调用) for(Method m : methods) { methodStr +=" @Override"+ rt + " public void "+m.getName()+"() {"+ rt + " try{"+ rt + " java.lang.reflect.Method md = "+interfaces.getName()+".class.getMethod(\""+m.getName()+"\");"+ rt + " handler.invoke(this,md,args);"+ rt + " }catch(Exception e){e.printStackTrace();}"+ " }"; } //源代码文件字符串拼接 String src = "package com.lin.proxy;"+ rt + "public class MySendProxy implements "+interfaces.getName()+"{" + rt + " com.lin.proxy.InvocationHandler handler;" + rt + " Object[] args;" + rt + " public MySendProxy(com.lin.proxy.InvocationHandler handler,Object[] args) {" + rt + " this.handler = handler;" + rt + " this.args = args;" + rt + " }" + rt + methodStr + rt + "}"; //源代码保存路径(看需求而定) String fileName = "d:/src/com/lin/proxy/MySendProxy.java"; //将数据写入文件 File f = new File(fileName); if(!f.getParentFile().exists()) { f.getParentFile().mkdirs(); } FileWriter fw = new FileWriter(f); fw.write(src); fw.flush(); fw.close(); //编译刚才生成的代理类的文件 //获取系统的java编译器(JDK提供) JavaCompiler complier = ToolProvider.getSystemJavaCompiler(); //编译 StandardJavaFileManager javaFileManager = complier.getStandardFileManager(null, null, null); //fileName = "d:/src/com/lin/proxy/MySendProxy.java"; Iterable units = javaFileManager.getJavaFileObjects(fileName); CompilationTask t = complier.getTask(null, javaFileManager, null, null, null, units); t.call(); javaFileManager.close(); //将class加载入内存并且创建一个实例 URL[] urls = new URL[]{new URL("file:/"+"d:/src/")}; URLClassLoader ul = new URLClassLoader(urls); Class c = ul.loadClass("com.lin.proxy.MySendProxy"); Constructor constructor = c.getConstructor(InvocationHandler.class,Object[].class); Object m = constructor.newInstance(handler,args); //将代理类实例返回 //此处由于java文件和class文件已经不再使用,因此我们可以将其删除 //实现没有任何文件残留 /* *············ *······· * */ return m; } }
现在我们的代理功能已经实现:
那么之前的送礼物代码可以改成这样:
package com.lin.proxy;
public class Test {
public static void main(String[] args) throws Exception {
// TODO 原谅的颜色在飘
Girl mm = new Girl();
mm.setName("小红");
Boy boy = new Boy(mm);
// ProxyBoy proxyBoy = new ProxyBoy(boy);
// proxyBoy.sendFlower();
// proxyBoy.sendLoveLetter();
GiveGift proxy = (GiveGift)MyProxy.newProxyInstance(GiveGift.class,new Object[]{}, new RecodeHandler(boy));
proxy.sendFlower();
proxy.sendLoveLetter();
}
}
执行结果:
Start Time1498981076580
com.lin.proxy.MySendProxy
小红送你一朵花
Moving time:0
Start Time1498981076580
com.lin.proxy.MySendProxy
小红送你一封信
Moving time:0
这就是动态代理的简单模仿实现。
浙公网安备 33010602011771号