一、概述
1.目标:动态代理的代理逻辑可以任意修改
2.思路:
(1)要把代理逻辑抽离,站在jvm的角度思考,应独立出InvocationHandler接口,并接收被代理的对象及方法作为参数invoke(Object o, Method m),并本身作为参数传给newProxyInstance(Class interfze,InvocationHandler handler)
(2)InvocationHandler本身聚合被代理类target,以便在target的方法前后增加代理逻辑
3.知识点:
(1)按名字找方法java.lang.reflect.Method md = proxy.Movable.class.getMethod("stop");
(2)按"."拆分字符串:String [] parts = m.toString().replace("abstract ", "").split("\\.");
二、代码
1.InvocationHandler.java
2.TimeHandler.java
3.Movable.java
4.Tank.java
5.Proxy.java
6.Client.java
1.InvocationHandler.java
1 package proxy; 2 3 import java.lang.reflect.Method; 4 5 public interface InvocationHandler { 6 public void invoke(Object o, Method m); 7 }
2.TimeHandler.java
1 package proxy; 2 3 import java.lang.reflect.InvocationTargetException; 4 import java.lang.reflect.Method; 5 6 public class TimeHandler implements InvocationHandler { 7 8 //保留被代理的对象 9 private Object target; 10 11 public TimeHandler(Object target) { 12 this.target = target; 13 } 14 15 @Override 16 public void invoke(Object o, Method m) { 17 System.out.println("Time Proxy start..........."); 18 long start = System.currentTimeMillis(); 19 try { 20 //除了静态方法,方法的调用都要先已知对象,所以要把对象o作为参数传进去 21 m.invoke(target); 22 } catch (Exception e) { 23 e.printStackTrace(); 24 } 25 long end = System.currentTimeMillis(); 26 System.out.println("花费时间:"+(end - start)); 27 System.out.println("Time Proxy end..........."); 28 29 } 30 31 }
3.Movable.java
1 package proxy; 2 3 public interface Movable { 4 public void move(); 5 public void stop(); 6 }
4.Tank.java
1 package proxy; 2 3 import java.util.Random; 4 5 public class Tank implements Movable { 6 7 @Override 8 public void move() { 9 System.out.println("Tank moving......."); 10 try { 11 Thread.sleep(new Random().nextInt(2000)); 12 } catch (InterruptedException e) { 13 e.printStackTrace(); 14 } 15 } 16 17 @Override 18 public void stop() { 19 System.out.println("Tank stopping......."); 20 21 } 22 23 }
5.Proxy.java
1 package proxy; 2 3 import java.io.File; 4 import java.io.FileWriter; 5 import java.lang.reflect.Constructor; 6 import java.lang.reflect.Method; 7 import java.net.URL; 8 import java.net.URLClassLoader; 9 10 import javax.tools.JavaCompiler; 11 import javax.tools.JavaCompiler.CompilationTask; 12 import javax.tools.StandardJavaFileManager; 13 import javax.tools.ToolProvider; 14 15 public class Proxy { 16 17 public static Object newProxyInstance(Class interfze,InvocationHandler handler) throws Exception { 18 19 String rt = "\n\r"; 20 21 //拼接"实现接口方法"的字符串 22 String methodStr = ""; 23 for(Method m: interfze.getMethods() ){ 24 25 //取出方法的修饰符和返回值类型 26 String [] parts = m.toString().replace("abstract ", "").split("\\."); 27 String [] parts2 = parts[0].split(" "); 28 29 methodStr += 30 "@Override" + rt + 31 parts2[0]+" "+parts2[1]+" "+m.getName()+"() {" + rt + 32 "try{"+ rt + 33 "java.lang.reflect.Method md = " + interfze.getName() + ".class.getMethod(\""+m.getName()+"\");" + rt + 34 //传this进去其实没什么用,invoke实际是调用target的方法m.invoke(target) 35 "handler.invoke(this, md);" + rt + 36 "}catch(Exception e){"+ rt + 37 " e.printStackTrace();" + rt + 38 "}" + rt + 39 40 41 "}"+ rt ; 42 } 43 44 45 //动态代理文件的源码 46 String str = 47 "package proxy;" + rt + 48 49 "public class TankTimeProxy implements " + interfze.getName() + " {"+rt+ 50 51 52 //聚合Handler 53 "private InvocationHandler handler;" + rt + 54 55 "public TankTimeProxy(InvocationHandler handler) {" + rt + 56 "this.handler = handler;" + rt + 57 "}" + rt + 58 59 methodStr + rt + 60 61 "}" ; 62 63 //把源码写到java文件里 64 File file = new File(System.getProperty("user.dir")+"/src/proxy/TankTimeProxy.java"); 65 FileWriter fw = new FileWriter(file); 66 fw.write(str); 67 fw.flush(); 68 fw.close(); 69 70 //编译源码,生成class,注意编译环境要换成jdk才有compiler,单纯的jre没有compiler,会空指针错误 71 JavaCompiler jc = ToolProvider.getSystemJavaCompiler(); 72 73 //文件管事器 74 StandardJavaFileManager fileMgr = jc.getStandardFileManager(null, null, null); 75 76 //编译单元 77 Iterable units = fileMgr.getJavaFileObjects(file); 78 79 //编译任务 80 CompilationTask t = jc.getTask(null, fileMgr, null, null, null, units); 81 82 //编译 83 t.call(); 84 fileMgr.close(); 85 86 //把类load到内存里 87 URL[] urls = new URL[] {new URL("file:/"+System.getProperty("user.dir")+"/src/proxy/TankTimeProxy.class")}; 88 URLClassLoader uc = new URLClassLoader(urls); 89 Class c = uc.loadClass("proxy.TankTimeProxy"); 90 91 //生成实例 92 //return c.newInstance(); //c.newInstance()会调用无参数的Construtor,若类没有无参的Constructor时会出错 93 //Constructor ctr = c.getConstructor(interfze); 94 Constructor ctr = c.getConstructor(InvocationHandler.class); 95 return ctr.newInstance(handler); 96 } 97 }
6.Client.java
1 package proxy; 2 3 import java.io.IOException; 4 5 import org.junit.Test; 6 7 public class Client { 8 9 @Test 10 public void testProxy() throws Exception{ 11 12 Movable m = (Movable)Proxy.newProxyInstance(Movable.class, new TimeHandler(new Tank())); 13 m.move(); 14 m.stop(); 15 16 } 17 }
三、运行结果
时间: 2024-10-29 05:22:57