虽然动态代理有jdk,高级的cglib,还有asm,这些高级玩意,为了复习一下反射,也为了给妹子讲讲,提前写个热个身
jdk里要被动态代理的类必须继承接口,然后通过 Proxy.newInstance(ClassLoader loader,Class cls,InvocationHandler hander)产生一个代理类。其实说到底就是根据第二个参数
也就是接口,产生一个新类也继承这个接口,同时里面的方法体全部变成 try{ handler.invoke(this,method,args);} catch(Exception e){e.printStack();}
好了,基本就这样,上代码吧
package com.whut.proxyutils; import java.io.File; import java.io.FileWriter; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import com.whut.invoker.InvocationHandler; import com.whut.invoker.SandyInvocationHandler; import com.whut.orign.IMovable; import com.whut.orign.Trunck; /* * 模拟jdk中的Proxy */ public class Proxy { public static Object newInstance(ClassLoader loader, Class intface, InvocationHandler h) throws Exception { String proxyName = intface.getName() + "$proxy"; String realInterfaceName = intface.getName(); String interName = realInterfaceName.substring(realInterfaceName .lastIndexOf('.') + 1) + "$proxy"; // 构建保持路径 String fullPath = intface.getName().replace('.', '/'); String filePath = System.getProperty("user.dir") + "/src/" + fullPath + "$proxy.java"; writeToFile(intface, filePath, interName); // 编译 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager filemanager = compiler.getStandardFileManager( null, null, null); Iterable units = filemanager.getJavaFileObjects(filePath); CompilationTask compileTask = compiler.getTask(null, filemanager, null, null, null, units); compileTask.call(); filemanager.close(); // load 到内存,生成对象 // URL[] urls=new URL[]{new // URL("file://"+System.getProperty("user.dir")+"/src")}; Class c = loader.loadClass(proxyName); // 含包的全限定名 Constructor ctr = c.getConstructor(InvocationHandler.class); // 通过接口的Class获得对应的constructor Object proxyObj = ctr.newInstance(h); return proxyObj; } // 接口中方法都是public的,即使没有修饰符也是public private static String getModifer(int modifer) { if (Modifier.isPublic(modifer)) return "public"; if (Modifier.isProtected(modifer)) return "protected"; if (Modifier.isPrivate(modifer)) return "private"; else return ""; } private static void writeToFile(Class intface, String path, String interName) throws Exception { StringBuilder sb = buildClass(intface, interName); String methodHead = "@Override public "; String realInterfaceName = intface.getName(); Method[] methods = intface.getMethods(); for (Method m : methods) { sb.append(methodHead).append(buildMethod(m, realInterfaceName)); } sb.append("}"); File f = new File(path); FileWriter fw = new FileWriter(f); fw.write(sb.toString()); fw.flush(); fw.close(); } /* * 构建类声明及构造部分 */ private static StringBuilder buildClass(Class intface, String interName) { String realInterfaceName = intface.getName(); String packageName = intface.getPackage().getName(); StringBuilder sb = new StringBuilder("package "); sb.append(packageName) .append(";public class ") .append(interName) .append(" implements ") .append(realInterfaceName) .append(" {private com.whut.invoker.InvocationHandler h;public ") .append(interName) .append("(com.whut.invoker.InvocationHandler h){this.h=h;}"); return sb; } /* * 构建方法体 */ private static StringBuilder buildMethod(Method method, String realIntFaceName) { StringBuilder sb = new StringBuilder(); int index = 0; Class<?>[] paramsClass = method.getParameterTypes(); // 得到方法的参数类型数组 SList sparams = new SList(); // 参数列表 SList reflectParams = new SList(); StringBuilder methodParams = new StringBuilder( "Class[] paramClasses=new Class[]{");// 通过反射找方法的参数Class列表 for (Class cls : paramsClass) { sparams.add(cls.getName() + " param" + index); ++index; reflectParams.add(cls.getName() + ".class"); } methodParams.append(reflectParams).append("};"); // 最后一项都多加了个 ',' 去掉 ---问题出来了,如果没有参数,那么这里还是会删除 // demn! 自己写个简易版list搞定 // 构建method.invoke 的参数args StringBuilder sargs = new StringBuilder(); sargs.append("Object[] args=new Object[").append(index).append("];"); for (int j = 0; j < index; j++) sargs.append("args[").append(j).append("]=").append("param") .append(j).append(";"); sb.append(method.getReturnType()) .append(" ") .append(method.getName()) .append("(") .append(sparams) .append("){try{") .append(methodParams) .append("java.lang.reflect.Method md=") .append(realIntFaceName) .append(".class.getMethod(\"") .append(method.getName()) .append("\",paramClasses);") .append(sargs) .append("h.invoke(this,md,args);}catch(Exception e){e.printStackTrace();}}"); return sb; } public static void main(String[] args) throws Exception { IMovable move = (IMovable) newInstance( ClassLoader.getSystemClassLoader(), IMovable.class, new SandyInvocationHandler(new Trunck())); move.move("SandyNie"); // Method[] methods=EveryTest.class.getMethods(); // for(Method m:methods){ // System.out.println(m.getModifiers()); // } } }
感觉用StringBuilder或者ArrayList都比较麻烦,本来想如果如果List有像javascript的Arrays.join()方法就好了,可是没有,只好写个很简单的工具类,关键就是想尽量在Proxy中把处理简化吧
package com.whut.proxyutils; import java.util.Arrays; public class SList { private String[] data; private int size; private int capacity = 5; public SList() { data = new String[capacity]; size = 0; } public void add(String adding){ if(size==capacity) extendLen(); data[size++]=adding; } private void extendLen(){ capacity*=2; data=Arrays.copyOf(data,capacity); } @Override public String toString() { StringBuilder sb=new StringBuilder(); if(size==0) return sb.toString(); for(int i=0;i<size;i++) sb.append(data[i]).append(","); sb.deleteCharAt(sb.length()-1); return sb.toString(); } }
下面是InvocationHandler
package com.whut.invoker; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public interface InvocationHandler { public Object invoke(Object proxy,Method md,Object[] args) throws Exception; }
基本上差不多了,还有个遗憾:JDK 的Proxy.newInstance的第二个参数传入的是个Interfaces数组,也就是说被代理的类可以继承多个借口,自己写的只能使用一个借口,以后再完善吧