最近一直在学习Spring的源码,Spring底层大量使用了动态代理。因此花一些时间对动态代理的知识作一下总结。java
咱们本身动手模拟一个动态代理web
对JDK动态代理的源码进行分析jvm
public interface MyService { void test01(); void test02(String s); } public class MyServiceImpl implements MyService { @Override public void test01() { System.out.println("test01"); } @Override public void test02(String s) { System.out.println(s); } } public class Main { public static void main(String[] args) { MyServiceImpl target = new MyServiceImpl(); } }
咱们如今要对target
对象进行代理。你们能够想一想,咱们如何去生成这个代理对象呢?ide
咱们先不考虑须要针对target生成一个代理对象,就单纯的生成一个对象来讲,咱们该怎么办呢?确定是不能new的,由于咱们根本没这个类。svg
因此为了动态的生成这个对象,咱们须要动态的生成一个类,也就是说动态的加载一个类到jvm中学习
因此咱们能够这么作:this
如今问题来了,咱们须要生成的java文件该是什么样子呢?咱们能够思考,若是要对这个类作静态代理咱们须要怎么作?url
package com.dmz.proxy; import com.dmz.proxy.target.MyService; /** * 静态代理 */ public class StaticProxy implements MyService { private MyService target; public StaticProxy(MyService target) { this.target = target; } @Override public void test01() { System.out.println("proxy print log for test01"); target.test01(); } @Override public void test02(String s) { System.out.println("proxy print log for test02"); target.test02(s); } }
上面就是静态代理的代码,若是咱们能够动态的生成这样的一个.java文件,而后调用jdk的方法进行编译,是否是就解决问题了呢?spa
因此咱们如今须要代理
拼接字符串,将上面的代码以字符串的形式拼接出来并写入到磁盘文件上,并命名为xxxx.java文件
编译.java文件,生成.class文件
加载这个class文件到JVM内存中(实际上就是方法区中),获得一个class对象
调用反射方法,class.newInstanc(…)
代码以下:
public class ProxyUtil { /** * @param target 目标对象 * @return 代理对象 */ public static Object newInstance(Object target) { Object proxy = null; // 开始拼接字符串 Class targetInf = target.getClass().getInterfaces()[0]; Method[] methods = targetInf.getDeclaredMethods(); String line = System.lineSeparator(); String tab = "\t"; String infName = targetInf.getSimpleName(); String content = ""; String packageContent = "package com.dmz.proxy;" + line; String importContent = "import " + targetInf.getName() + ";" + line; String clazzFirstLineContent = "public class $Proxy implements " + infName + "{" + line; String filedContent = tab + "private " + infName + " target;" + line; String constructorContent = tab + "public $Proxy (" + infName + " target){" + line + tab + tab + "this.target =target;" + line + tab + "}" + line; String methodContent = ""; for (Method method : methods) { String returnTypeName = method.getReturnType().getSimpleName(); String methodName = method.getName(); // Sting.class String.class Class args[] = method.getParameterTypes(); String argsContent = ""; String paramsContent = ""; int flag = 0; for (Class arg : args) { String temp = arg.getSimpleName(); //String //String p0,Sting p1, 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 + "System.out.println(\"proxy print log for " + methodName + "\");" + line + tab + tab + "target." + methodName + "(" + paramsContent + ");" + line + tab + "}" + line; } content = packageContent + importContent + clazzFirstLineContent + filedContent + constructorContent + methodContent + "}"; //字符串拼接结束 // 开始生成.java文件 File file = new File("g:\\com\\dmz\\proxy\\$Proxy.java"); try { if (!file.exists()) { file.createNewFile(); } FileWriter fw = new FileWriter(file); fw.write(content); fw.flush(); fw.close(); // .java文件生成结束 // 开始进行编译 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文件 // 从G盘中加载class文件 URL[] urls = new URL[]{new URL("file:G:\\\\")}; URLClassLoader urlClassLoader = new URLClassLoader(urls); // 加载 Class clazz = urlClassLoader.loadClass("com.dmz.proxy.$Proxy"); // 加载结束 // 构造代理对象 Constructor constructor = clazz.getConstructor(targetInf); proxy = constructor.newInstance(target); } catch (Exception e) { e.printStackTrace(); } return proxy; } }
咱们调用这个方法:
public class Main { public static void main(String[] args) { MyServiceImpl target = new MyServiceImpl(); MyService o = ((MyService) ProxyUtil.newInstance(target)); o.test01(); o.test02("test02"); } }
会在咱们的G盘中生成文件:
打开.java文件能够看到以下内容:
同时控制台会正常打印:
这样,咱们就完成了一个简单的代理。