代理模式和动态代理模式

1.什么是代理模式:

        代理模式:为其他对象提供一种代理以便控制对这个对象的访问。


2.抛砖

之前做过一个坦克大战的例子(好像地球人都做过哈),假定有一个接口叫Moveable,坦克类Tank实现Moveable接口,必须实现move方法,如何计算这个move方法的执行时间呢?当然坦克类的代码是不能修改的。有两种办法:1.使用继承,让子类Tank2继承Tank,然后重写move方法,在重写的方法中计算父类move方法执行时间(super.move())。2.使用组合方式,新建一个类叫Tank3也实现Moveable接口,把Tank对象t作为Tank3的一个属性。然后重写move()方法,在move方法里面调用t的方法move方法,测出执行时间。


3.思考:以上两种方法哪个更好?显然组合更好。因为当有新需求时,使用继承方法时,需要重新定义一个类,比较麻烦,尤其是需求多变的时候。如果用组合呢?此时当有新需求时,将属性t声明为moveable,此时,多态的威力又来了。我们把tank3重新命名为TankTimeProxy类。新需求可以添加新的类TankLogProxy。代码如下:

        Tank t =new Tank();

TankTimeProxyttp = new TankTimeProxy(t);

TankLogProxytlp = new TankLogProxy(ttp);

Moveablem = ttp;

m.move();


感觉有点装饰模式的意思哈。


4.至此就结束了吗?不是的。如果我再需要增加新的功能呢?如果每次都增加新的类,虽然满足设计基本原则,但会不会太麻烦,

因为每个类都很相似,只不过是增加了新的功能。能否用一种动态生成类的办法呢?也就是说类文件也自动生成。需要做如下步骤:

4.1 首先将代码拼装成字符串,参数是接口类型,使用了反射;

public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception { //JDK6 Complier API, CGLib, ASM
String methodStr = "";
String rt = "\r\n";

for(Method m : methods) {
methodStr += "@Override" + rt + 
"public void " + m.getName() + "() {" + rt +
"    try {" + rt +
"    Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
"    h.invoke(this, md);" + rt +
"    }catch(Exception e) {e.printStackTrace();}" + rt +

"}";
}

String src = 
"package com.bjsxt.proxy;" +  rt +
"import java.lang.reflect.Method;" + rt +
"public class $Proxy1 implements " + infce.getName() + "{" + rt +
"    public $Proxy1(InvocationHandler h) {" + rt +
"        this.h = h;" + rt +
"    }" + rt +


"    com.bjsxt.proxy.InvocationHandler h;" + rt +

methodStr +
"}";

4.2 将字符串写入指定文件;

String fileName = 
"d:/src/com/bjsxt/proxy/$Proxy1.java";
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();

4.3 获取系统的编译器,动态编译指定文件;

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();

4.4 获取系统的内存加载器,将指定目录中的文件加载到内存。

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

URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("com.bjsxt.proxy.$Proxy1");
System.out.println(c);

4.5 获取构造函数

Constructor ctr = c.getConstructor(InvocationHandler.class);
Object m = ctr.newInstance(h);


通过以上的步骤,就动态的获取对象了。


5.注意参数问题,InvocationHandler 里面用代理处理的。


public class Client {
public static void main(String[] args) throws Exception {
Tank t = new Tank();
InvocationHandler h = new TimeHandler(t);

Moveable m = (Moveable)Proxy.newProxyInstance(Moveable.class, h);

m.move();
}
}



package com.bjsxt.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;
}

@Override
public void invoke(Object o, Method m) {
try {
m.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
}
}


6.总结

6.1 代理模式本质:在代理类中关联另一个类,当调用代理类的方法时,本质上是执行被代理类的方法,实际上是被代理类对象在干活。

6.2 动态生成对象的本质:根据参数,自动生成代码,调用编译器动态编译,使用加载器自动将生成的对象加载到内存,返回给调用者。



//主测试文件

public class Client {
public static void main(String[] args) throws Exception {
Tank t = new Tank();
InvocationHandler h = new TimeHandler(t);

Moveable m = (Moveable)Proxy.newProxyInstance(Moveable.class, h);

m.move();
}
}



//Proxy.java文件/

package com.bjsxt.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 infce, InvocationHandler h) throws Exception { //JDK6 Complier API, CGLib, ASM
String methodStr = "";
String rt = "\r\n";

Method[] methods = infce.getMethods();
/*
for(Method m : methods) {
methodStr += "@Override" + rt + 
"public void " + m.getName() + "() {" + rt +
"   long start = System.currentTimeMillis();" + rt +
"   System.out.println(\"starttime:\" + start);" + rt +
"   t." + m.getName() + "();" + rt +
"   long end = System.currentTimeMillis();" + rt +
"   System.out.println(\"time:\" + (end-start));" + rt +
"}";
}
*/
for(Method m : methods) {
methodStr += "@Override" + rt + 
"public void " + m.getName() + "() {" + rt +
"    try {" + rt +
"    Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
"    h.invoke(this, md);" + rt +
"    }catch(Exception e) {e.printStackTrace();}" + rt +

"}";
}

String src = 
"package com.bjsxt.proxy;" +  rt +
"import java.lang.reflect.Method;" + rt +
"public class $Proxy1 implements " + infce.getName() + "{" + rt +
"    public $Proxy1(InvocationHandler h) {" + rt +
"        this.h = h;" + rt +
"    }" + rt +


"    com.bjsxt.proxy.InvocationHandler h;" + rt +

methodStr +
"}";
String fileName = 
"d:/src/com/bjsxt/proxy/$Proxy1.java";
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
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 memory and create an instance
URL[] urls = new URL[] {new URL("file:/" + "d:/src/")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("com.bjsxt.proxy.$Proxy1");
System.out.println(c);

Constructor ctr = c.getConstructor(InvocationHandler.class);
Object m = ctr.newInstance(h);
//m.move();


return m;
}
}


/InvocationHandler.java///

package com.bjsxt.proxy;


import java.lang.reflect.Method;


public interface InvocationHandler {
public void invoke(Object o, Method m);
}



//TimeHandler.java///

package com.bjsxt.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;
}






@Override
public void invoke(Object o, Method m) {
long start = System.currentTimeMillis();
System.out.println("starttime:" + start);
System.out.println(o.getClass().getName());
try {
m.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("time:" + (end-start));
}


}


///Moveable.java/

package com.bjsxt.proxy;


public interface Moveable {
void move();

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值