java一步一步实现动态代理

来源:互联网 发布:网络pos刷卡平台 编辑:程序博客网 时间:2024/05/24 03:19

项目开发中,用户的需求经常是变动的。面对用户多变的需求,系统的灵活性是很重要的。

在原来的系统上,想计算方法执行的时间,作为参评系统性能的数据。

方法一:最直观简单做法就是,在每个方法中都添加计算执行时间的逻辑代码。

/** * 移动 */public interface Moveable {public void move();}/** * 坦克 */public class Tank implements Moveable {@Override public void move(){try {System.out.println("tank--moving...");Thread.sleep(new Random().nextInt(10000));} catch (InterruptedException e) {e.printStackTrace();}}}/** * 坦克——计算了方法执行的时间 */public class Tank implements Moveable {@Override public void move(){//方法执行前,计算系统时间Long start = System.currentTimeMillis();try {System.out.println("tank--moving...");Thread.sleep(new Random().nextInt(10000));} catch (Exception e) {e.printStackTrace();}//方法执行后,计算系统时间Long end = System.currentTimeMillis();System.out.println("time=" + (end-start));}}
这样的做法明显违背了对修改关闭,扩展开放的原则。

方法二:利用继承重写方法

/** * Tank2继承Tank * */public class Tank2 extends Tank {/** * 重写move方法,加入计算执行时间代码 */public void move(){Long start = System.currentTimeMillis();super.move();Long end = System.currentTimeMillis();System.out.println("time=" + (start-end));} }
这样的做法同样有很大的问题,第一,Tank2继承了Tank就不能再继承其它的类。第二,Tank类一旦做了修改,子类必定跟着改。

方法三:另外添加Moveable接口的实现,利用组合。

/** * Tank3 实现了Moveable接口 */public class Tank3 implements Moveable {Moveable m;public Tank3(Moveable m){this.m = m;}/** * 借助原来的实现,再加工 */@Overridepublic void move() {Long start = System.currentTimeMillis();m.move();Long end = System.currentTimeMillis();System.out.println("time=" + (start - end));}}
这其实就是一个代理模式——静态代理,Tank3代理了Tank,在Tank原有的move方法上添加了自己的逻辑处理,大大增加了系统的灵活性,但是假设一个系统500个类500个方法都需要这样做,那就会添加500个类,工作量还是很大的。

方法四:动态代理。动态代理,其实就是不针对具体的类产生代理类,系统执行期间,动态的为某个类产生代理。

动态代理的实现:

1.借助jdk提供的API:被代理的类必须实现接口,JDK1.5以上才支持动态代理。

2.CGLib:不要求有接口,可以为任意类提供代理

这里介绍的是JDK的动态代理,一步一步的分析:

(1):动态的产生代理类:

/** * 代理类 * 动态生成代理对象 */public class Proxy{public static Object newInstance() throws Exception{String rt = "\r\n";//将代理类拼成字符串String src = "package com.tgb.proxy;" + rt +"public class $Proxy1 implements Moveable{" + rt +"Moveable m;" + rt +"public $Proxy1(Moveable m){" + rt + "this.m = m;" + rt +"}" + rt +"public void move(){" + rt +"Long start = System.currentTimeMillis();" + rt +"m.move();" + rt +"Long end = System.currentTimeMillis();" + rt + "System.out.println(\"time=\" + (start-end));" + rt +"}" + rt +"}" ;//将src写到文件中,生成$Proxy1.java源文件String filename = "E:/java/com/tgb/proxy/$Proxy1.java";File f = new File(filename);FileWriter fw = new FileWriter(f);fw.write(src);fw.flush();fw.close();//-----利用JDK的编译器,编译文件——就相当于静态编写了代理类,经过了编译----//编译器JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();//文件管理器StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null,null);//通过文件管理器,获得需要编译的文件Iterable unites = fileMgr.getJavaFileObjects(filename);//编译器获得编译任务CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, unites);//进行编译t.call();//关闭文件管理器fileMgr.close();//-----利用JDK的编译器,编译文件——就相当于静态编写了代理类,经过了编译----//读取编译好的文件,放入到内存中URL[] urls = new URL[] {new URL("file:/" + "E:/java/")};URLClassLoader ul = new URLClassLoader(urls);Class c = ul.loadClass("com.tgb.proxy.$Proxy1");//利用带参数的构造器,生成代理对象,并返回Constructor ctr = c.getConstructor(Moveable.class);Moveable m = (Moveable)ctr.newInstance(new Tank());return m;}}
通过代理类(Proxy)的newInstance方法,经过动态编写代理文件,经过编译,再加载的内存,生成真正的代理对象并返回。完成动态代理。

src这个字符串是固定的,所以需要做如下调整:1.动态的代理接口,方法 2.对原来的方法不仅仅是计算执行时间的处理。

(2)调整1:动态的代理接口、方法

/** * 代理类 * 调整1:动态的代理接口、方法 */public class Proxy{/** * 将要被代理的接口,作为参数传入 * @param inter * @return * @throws Exception */public static Object newInstance(Class inter) throws Exception{String rt = "\r\n";//代理类通过接口,动态生成代理方法String methodStr = "";Method[] methods = inter.getMethods();for(Method m : methods){methodStr +="@Override" + rt +"public void " + m.getName() + "(){" + rt + "Long start = System.currentTimeMillis();" + rt +"t." + m.getName() + "();" + rt +"Long end = System.currentTimeMillis();" + rt + "System.out.println(\"time=\" + (start-end));" + rt +"}";}//代理类的实现接口有传入的参数决定String src = "package com.tgb.proxy;" + rt +"public class $Proxy1 implements " + inter.getName() + "{" + rt +"Moveable t;" + rt +"public $Proxy1(Moveable t){" + rt + "this.t = t;" + rt +"}" + rt + methodStr + rt +"}" ;//将代码写入文件,生成源文件String filename = "E:/java/com/tgb/proxy/$Proxy1.java";File f = new File(filename);FileWriter fw = new FileWriter(f);fw.write(src);fw.flush();fw.close();//利用jdk将源文件进行编译JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);Iterable units = fileMgr.getJavaFileObjects(filename);CompilationTask  ct = compiler.getTask(null, fileMgr, null, null, null, units);ct.call();fileMgr.close();//将编译的文件加载到内存中URL[] urls = new URL[]{new URL("file:/" + "E:/java/")};URLClassLoader ul = new URLClassLoader(urls);Class c = ul.loadClass("com.tgb.proxy.$Proxy1");Constructor ctr = c.getConstructor(Moveable.class);Object o = (Object)ctr.newInstance(new Tank()); return o;}}
这样,基本实现了通过任意的接口,生成对应的代理对象,并返回。

(3):调整2:被代理的方法,不仅仅是做计算时间的处理

/** * 方法调用的处理器 */public interface InvocationHandler {public void invoke(Object o,Method m);}/** * 对被代理对象,添加 计算方法的执行时间 处理 */public class TimeHandler implements InvocationHandler{//被代理的对象private Object target;public TimeHandler(Object target) {this.target = target;}/** * 处理被代理对象的方法:添加 计算方法执行时间 处理 */@Overridepublic void invoke(Object o,Method m){try {Long start = System.currentTimeMillis();m.invoke(target, new Object[]{});Long end = System.currentTimeMillis();System.out.println("time=" + (start-end));} catch (Exception e) {e.printStackTrace();}}}/** * 代理类 */public class Proxy{/** * @param inter 代理类实现的接口 * @param h 代理类方法需要做的处理对象 * @return * @throws Exception */public static Object newInstance(Class inter,InvocationHandler h) throws Exception{String rt = "\r\n";//通过接口参数,代理类动态生成代理方法//代理方法中,通过方法处理参数,动态调用方法的处理类String methodStr = "";Method[] methods = inter.getMethods();  for(Method m : methods){methodStr +="@Override" + rt +"public void " + m.getName() + "(){" + rt +"try {" + rt +"Method md = " + inter.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +"h.invoke(this, md);" + rt + "}c atch(Exception e){" + rt +"e.printStackTrace();" + rt + "}" + rt +"}";}//通过接口参数,代理类动态决定实现接口String src = "package com.tgb.proxy;" + rt +"import java.lang.reflect.Method;" + rt + "public class $Proxy1 implements " + inter.getName() + "{" + rt + "com.tgb.proxy.InvocationHandler h;" + rt + "public $Proxy1(InvocationHandler h){" + rt +"this.h = h;" + rt +"}" + rt +methodStr + rt + "}"; String filename = "E:/java/com/tgb/proxy/$Proxy1.java";File file = new File(filename);FileWriter fw = new FileWriter(file);fw.write(src);fw.flush();fw.close();//编译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();//加载到内存URL[] urls = new URL[]{new URL("file:/" + "E:/java/")};URLClassLoader ul = new URLClassLoader(urls);Class c = ul.loadClass("com.tgb.proxy.$Proxy1");//生成代理对象,并返回Constructor ctr = c.getConstructor(InvocationHandler.class);Object o = (Object)ctr.newInstance(h);return o;}}

客户端:

public class Client {public static void main(String[] args) throws Exception{Tank t = new Tank();InvocationHandler h = new TimeHandler(t);Moveable m = (Moveable)Proxy.newInstance(Moveable.class,h);m.move();}}

这样,完全实现了动态生成代理对象,并动态添加对方法的处理。
0 0