上篇分析的是JDK动态代理实现原理,这篇是一个自实现的动态代理案例,这一篇我们自定义代理Proxy,代理业务需要实现的Handler接口,以及类加载器ClassLoader;最终我们以自己写的代码去生成代理类的代码,再用代理类的代码去代理执行我们的业务代码,完成一套标准的动态代理;
本博客关于Java动态代理相关内容直达链接:
JDK动态代理浅析 Cglib动态代理浅析 JDK动态代理深入理解分析并手写简易JDK动态代理(上) JDK动态代理深入理解分析并手写简易JDK动态代理(下) 上篇分析的是JDK动态代理实现原理,这个下篇是一个自实现的动态代理案例,这一篇我们自定义代理Proxy,代理业务需要实现的Handler接口,以及类加载器ClassLoader;最终我们以自己写的代码去生成代理类的代码,再用代理类的代码去代理执行我们的业务代码,完成一套标准的动态代理流程;
首先我们分析实现代理需要什么,下面是Proxy生成代理类的newProxyInstance()方法:
1 Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
一个代理类Proxy,一个ClassLoader,一个业务类实现的接口数组,一个InvocationHandler; 把这里的步骤拆分一下就是下面的两步:
1 2 1 . Proxy其实就是根据传递给它的参数Class<?>[] interfaces去生成代理类$Proxy0; 2 . 用ClassLoader loader去加载生成的这个代理类$Proxy0,然后返回$Proxy0实例的引用;
现在一步步来做,在Proxy中,我们大致可以细分为4步:
1 2 3 4 1 . 动态生成代理类的源代码.java文件,并写入到磁盘;2 . 把生成的.java文件编译成.class 文件; 3. 把编译的.class 文件加载到JVM ; 4. 返回动态生成的代理对象;
那么GuituDynamicProxy类完成后的代码如下(相当于Proxy):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 package com.guitu18.study.proxy.guitu;import javax.tools.JavaCompiler;import javax.tools.StandardJavaFileManager;import javax.tools.ToolProvider;import java.io.File;import java.io.FileWriter;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import java.lang.reflect.Parameter;public class GuituDynamicProxy { private static final String LN = "\r\n" ; private static final String SRC_NAME = "$GuituProxy0" ; private static final String PACKAGE_NAME = "com.guitu18.study.proxy.guitu" ; public static Object newProxyInstance (GuituClassLoader guituClassLoader, Class<?>[] interfaces, GuituInvocationHandler guituInvocationHandler) { try { File file = generateSrcToFile(interfaces); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager manage = compiler.getStandardFileManager(null , null , null ); Iterable iterable = manage.getJavaFileObjects(file); JavaCompiler.CompilationTask task = compiler.getTask(null , manage, null , null , null , iterable); task.call(); manage.close(); Class proxyClass = guituClassLoader.findClass(SRC_NAME); Constructor constructor = proxyClass.getConstructor(GuituInvocationHandler.class ) ; return constructor.newInstance(guituInvocationHandler); } catch (Exception e) { e.printStackTrace(); } return null ; } private static File generateSrcToFile (Class<?>[] interfaces) { try { StringBuffer sb = new StringBuffer(); sb.append("package " + PACKAGE_NAME + ";" + LN); sb.append("import java.lang.reflect.Method;" + LN); StringBuffer interfaceStr = new StringBuffer(); for (int i = 0 ; i < interfaces.length; i++) { interfaceStr.append(interfaces[i].getName()); if (interfaces.length > 1 && i < interfaces.length - 2 ) { interfaceStr.append("," ); } } sb.append("public class " + SRC_NAME + " implements " + interfaceStr.toString() + " {" + LN); sb.append(" GuituInvocationHandler guituInvocationHandler;" + LN); sb.append(" public " + SRC_NAME + "(GuituInvocationHandler guituInvocationHandler) { " + LN); sb.append(" this.guituInvocationHandler = guituInvocationHandler;" + LN); sb.append(" }" + LN); for (Class<?> anInterface : interfaces) { for (Method method : anInterface.getMethods()) { Parameter[] parameters = method.getParameters(); StringBuffer paramStr = new StringBuffer(); StringBuffer paramTypeStr = new StringBuffer(); StringBuffer paramNameStr = new StringBuffer(); for (int i = 0 ; i < parameters.length; i++) { Parameter parameter = parameters[i]; paramStr.append(parameter.getType().getName() + " " + parameter.getName()); paramTypeStr.append(parameter.getType().getName()).append(".class" ); paramNameStr.append(parameter.getName()); if (parameters.length > 1 && i < parameters.length - 2 ) { sb.append(", " ); paramTypeStr.append("," ); paramNameStr.append(", " ); } } String returnTypeName = method.getReturnType().getName(); sb.append(" public " + returnTypeName + " " + method.getName() + "(" + paramStr.toString() + ") {" + LN); sb.append(" try{" + LN); sb.append(" Method method = " + interfaces[0 ].getName() + ".class.getMethod(\"" + method.getName() + "\",new Class[]{" + paramTypeStr.toString() + "});" + LN); if (!"void" .equals(returnTypeName)) { sb.append(" " + returnTypeName + " invoke = (" + returnTypeName + ")this.guituInvocationHandler.invoke(this, method, new Object[]{" + paramNameStr.toString() + "});" + LN); sb.append(" return invoke;" + LN); } else { sb.append(" this.guituInvocationHandler.invoke(this, method, null);" + LN); } sb.append(" }catch(Throwable e){" + LN); sb.append(" e.printStackTrace();" + LN); sb.append(" }" + LN); if (!"void" .equals(method.getReturnType().getName())) { sb.append(" return null;" + LN); } sb.append(" }" + LN); } } sb.append("}" + LN); String path = GuituDynamicProxy.class.getResource("").getPath(); System.out.println(path); File file = new File(path + SRC_NAME + ".java" ); FileWriter fw = new FileWriter(file); fw.write(sb.toString()); fw.flush(); fw.close(); return file; } catch (Exception e) { e.printStackTrace(); } return null ; } }
在上面的步骤中,我们先生成了代理类,然后使用JavaCompiler将其编译成class文件,接着用类加载器将class文件加载到内存,这里用到了类加载器ClassLoader; 我们自定义类加载器需要继承ClassLoader类,重写findClass(String name)方法,代码如下(相当于ClassLoader):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 package com.guitu18.study.proxy.guitu;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;public class GuituClassLoader extends ClassLoader { private File classPathFile; public GuituClassLoader () { this.classPathFile = new File(GuituClassLoader.class.getResource("").getPath()); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { String className = GuituClassLoader.class.getPackage().getName() + "." + name; if (classPathFile != null ) { File classFile = new File(classPathFile, name.replaceAll("\\." , "/" ) + ".class" ); if (classFile.exists()) { FileInputStream in = null ; ByteArrayOutputStream out = null ; try { in = new FileInputStream(classFile); out = new ByteArrayOutputStream(); byte [] buff = new byte [1024 ]; int len; while ((len = in.read(buff)) != -1 ) { out.write(buff, 0 , len); } return defineClass(className, out.toByteArray(), 0 , out.size()); } catch (Exception e) { e.printStackTrace(); } finally { if (null != in) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if (out != null ) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } } return null ; } }
接着就是接口GuituInvocationHandler如下(相当于InvocationHandler):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.guitu18.study.proxy.guitu;import java.lang.reflect.Method;public interface GuituInvocationHandler { Object invoke (Object proxy, Method method, Object[] args) ; }
有了这三样东西,我们就可以使用它们编写我们的动态代理了,跟上篇使用JDK动态代理时一样的使用方式,只不过使用的全都是我们自己写的代码了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package com.guitu18.study.proxy.guitu;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class GuituProxy implements GuituInvocationHandler { private Object target; public Object getInstance (Object object) { try { this .target = object; return GuituDynamicProxy.newProxyInstance(new GuituClassLoader(), object.getClass().getInterfaces(), this ); } catch (Exception e) { e.printStackTrace(); } return null ; } @Override public Object invoke (Object proxy, Method method, Object[] args) { try { System.out.println("Guitu动态代理,代理执行前..." ); Object invoke = null ; invoke = method.invoke(this .target, args); System.out.println("执行后..." ); return invoke; } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null ; } }
至此一套自实现的JDK动态代理就完成了,这中间很多过程直接使用的简化操作,JDK动态代理的源码比这个要复杂的多,此篇主要为了强化理解JDK动态代理思想; 具体的步骤分析和流程说明我在上面代码的注释中已经写的非常详细了,这里就不做过多说明了;这里面稍微复杂一点的就是动态的生成代理类源代码这个步骤,这里需要非常细心,毕竟使用字符串拼接代码,丝毫不能出错;其他的流程只要明白了原理其实很容易; 下面简单贴上测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.guitu18.study.proxy.guitu;import com.guitu18.study.proxy.Persion;import com.guitu18.study.proxy.ZhangKuan;public class GuituProxyTest { public static void main (String[] args) { Persion instance = (Persion) new GuituProxy().getInstance(new ZhangKuan()); String love = instance.findLove("肤白貌美大长腿" ); System.out.println(love); instance.findWord(); } }
测试中的业务接口Persion和业务类ZhangKuan这里就不贴了,和上篇的代码一模一样; 执行结果如下:
1 2 3 4 5 6 7 Guitu动态代理,代理执行前... 肤白貌美大长腿 执行后... 叶青我爱你 Guitu动态代理,代理执行前... 我想找月薪15 -25 k的工作 执行后...
JDK动态代理深入分析到这里就结束了,Java学习还有很长的路要走,2019继续努力,再接再厉!