实现自己动态代理,首先需要关注的点就是,代理对象需要为哪些方法代理? 原生JDK的动态代理的实现是往上抽象出一层接口,让目标对象和代理对象都实现这个接口,怎么把接口的信息告诉jdk原生的动态代理呢? 如下代码所示, Proxy.newProxyInstance()
方法的第二个参数将接口的信息传递了进去第一个参数的传递进去一个类加载器,在jdk的底层用它对比对象是否是同一个,标准就是 相同对象的类加载器是同一个
ServiceInterface) Proxy.newProxyInstance(service.getClass().getClassLoader() , new Class[]{ServiceInterface.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("前置通知"); method.invoke(finalService,args); System.out.println("后置通知"); return proxy; } });
我们也效仿它的做法. 代码如下:
public class Test { public static void main(String[] args) { IndexDao indexDao = new IndexDao(); Dao dao =(Dao) ProxyUtil.newInstance(Dao.class,new MyInvocationHandlerImpl(indexDao)); assert dao != null; System.out.println(dao.say("changwu")); } }
拿到了接口的 Class
对象后,通过反射就得知了接口中有哪些方法描述对象 Method
, 获取到的所有的方法,这些方法就是我们需要增强的方法
JDK的做法是通过 InvocationHandler
的第三个参数完成,他是个接口,里面只有一个抽象方法如下: 可以看到它里面有三个入参,分别是 代理对象,被代理对象的方法,被代理对象的方法的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
InvocationHandler
接口必须存在必要性1:
为什么不传递进去 Method
,而是传递进去 InvocationHandler
对象呢? 很显然,我们的初衷是借助 ProxyUtil
工具类完成对代理对象的拼串封装,然后让这个代理对象去执行 method.invoke()
, 然而事与愿违,传递进来的Method对象的确可以被 ProxyUtil
使用,调用 method.invoke()
,
但是我们的代理对象不能使用它,因为代理对象在这个 ProxyUtil
还以一堆等待拼接字符串, ProxyUtil
的作用只能是往代理对象上叠加字符串,却不能直接传递给它一个对象,所以只能传递一个对象进来,然后通过反射获取到这个对象的实例,继而有可能实现method.invoke()
InvocationHandler
接口必须存在必要性2:
通过这个接口的规范,我们可以直接得知回调方法的名字就是 invoke()
所以说,在拼接字符串完成对代理对象的拼接时,可以直接写死它
我们需要通过上面的 ProxyUtil.newInstance(Dao.class,new MyInvocationHandlerImpl(indexDao))
方法完成如下几件事
ProxyUtil
使用类加载器,将XXX.java读取JVM中,形成Class对象 ProxyUtil
的实现如下: public static Object newInstance(Class targetInf, MyInvocationHandler invocationHandler) { Method methods[] = targetInf.getDeclaredMethods(); String line = "/n"; String tab = "/t"; String infName = targetInf.getSimpleName(); String content = ""; String packageContent = "package com.myproxy;" + line; // 导包,全部导入接口层面,换成具体的实现类就会报错 // String importContent = "import " + targetInf.getName() + ";" + line + "import com.changwu.代理技术.模拟jdk实现动态代理.MyInvocationHandler;" + line + "import java.lang.reflect.Method;" + line + "import java.lang.Exception;" + line; String clazzFirstLineContent = "public class $Proxy implements " + infName +"{"+ line; String filedContent = tab + "private MyInvocationHandler handler;"+ line; String constructorContent = tab + "public $Proxy (MyInvocationHandler handler){" + line + tab + tab + "this.handler =handler;" + line + tab + "}" + line; String methodContent = ""; // 遍历它的全部方法,接口出现的全部方法进行增强 for (Method method : methods) { String returnTypeName = method.getReturnType().getSimpleName(); method.getReturnType().getSimpleName()); String methodName = method.getName(); Class<?>[] parameterTypes = method.getParameterTypes(); // 参数的.class String paramsClass = ""; for (Class<?> parameterType : parameterTypes) { paramsClass+= parameterType.getName()+","; } String[] split = paramsClass.split(","); //方法参数的类型数组 Sting.class String.class String argsContent = ""; String paramsContent = ""; int flag = 0; for (Class arg : parameterTypes) { // 获取方法名 String temp = arg.getSimpleName(); argsContent += temp + " p" + flag + ","; paramsContent += "p" + flag + ","; flag++; } // 去掉方法参数中最后面多出来的, if (argsContent.length() > 0) { argsContent = argsContent.substring(0, argsContent.lastIndexOf(",") - 1); paramsContent = paramsContent.substring(0, paramsContent.lastIndexOf(",") - 1); } methodContent += tab + "public " + returnTypeName + " " + methodName + "(" + argsContent + ") {" + line + tab + tab+"Method method = null;"+line + tab + tab+"String [] args0 = null;"+line + tab + tab+"Class<?> [] args1= null;"+line // invoke入参是Method对象,而不是上面的字符串,所以的得通过反射创建出Method对象 + tab + tab+"try{"+line // 反射得到参数的类型数组 + tab + tab + tab + "args0 = /""+paramsClass+"/".split(/",/");"+line + tab + tab + tab + "args1 = new Class[args0.length];"+line + tab + tab + tab + "for (int i=0;i<args0.length;i++) {"+line + tab + tab + tab + " args1[i]=Class.forName(args0[i]);"+line + tab + tab + tab + "}"+line // 反射目标方法 + tab + tab + tab + "method = Class.forName(/""+targetInf.getName()+"/").getDeclaredMethod(/""+methodName+"/",args1);"+line + tab + tab+"}catch (Exception e){"+line + tab + tab+ tab+"e.printStackTrace();"+line + tab + tab+"}"+line + tab + tab + "return ("+returnTypeName+") this.handler.invoke(method,/"暂时不知道的方法/");" + line; // methodContent+= tab + "}"+line; } content = packageContent + importContent + clazzFirstLineContent + filedContent + constructorContent + methodContent + "}"; File file = new File("d://com//myproxy//$Proxy.java"); try { if (!file.exists()) { file.createNewFile(); } FileWriter fw = new FileWriter(file); fw.write(content); fw.flush(); fw.close(); // 将生成的.java的文件编译成 .class文件 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null); Iterable units = fileMgr.getJavaFileObjects(file); JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units); t.call(); fileMgr.close(); // 使用类加载器将.class文件加载进jvm // 因为产生的.class不在我们的工程当中 URL[] urls = new URL[]{new URL("file:D:////")}; URLClassLoader urlClassLoader = new URLClassLoader(urls); Class clazz = urlClassLoader.loadClass("com.myproxy.$Proxy"); return clazz.getConstructor(MyInvocationHandler.class).newInstance(invocationHandler); } catch (Exception e) { e.printStackTrace(); } return null; } }
package com.myproxy; import com.changwu.myproxy.pro.Dao; import com.changwu.myproxy.pro.MyInvocationHandler; import java.lang.reflect.Method; import java.lang.Exception; public class $Proxy implements Dao{ private MyInvocationHandler handler; public $Proxy (MyInvocationHandler handler){ this.handler =handler; } public String say(String p) { Method method = null; String [] args0 = null; Class<?> [] args1= null; try{ args0 = "java.lang.String,".split(","); args1 = new Class[args0.length]; for (int i=0;i<args0.length;i++) { args1[i]=Class.forName(args0[i]); } method = Class.forName("com.changwu.myproxy.pro.Dao").getDeclaredMethod("say",args1); }catch (Exception e){ e.printStackTrace(); } return (String) this.handler.invoke(method,"暂时不知道的方法"); } }
newInstance()
用户获取到的代理对象就像上面的代理一样,这个过程是在java代码运行时生成的,但是直接看他的结果和静态代理差不错,这时用户再去调用代理对象的say(), 实际上就是在执行用户传递进去的InvocationHandeler里面的invoke方法, 但是亮点是我们把目标方法的描述对象 Method
同时给他传递进去了,让用户可以执行目标方法+增强的逻辑 public class MyInvocationHandlerImpl implements MyInvocationHandler { private Object obj; public MyInvocationHandlerImpl(Object obj) { this.obj = obj; } @Override public Object invoke(Method method, Object[] args) { System.out.println("前置通知"); try { method.invoke(obj,args); } catch (Exception e) { e.printStackTrace(); } System.out.println("后置通知"); return null; } }