手写JDK动态代理
下面模拟JDK动态代理写一个自己的动态代理。
类图
思路分析
具体实现
package com.morris.spring.proxy; public interface IUserService { String query(String name); }目标类
package com.morris.spring.proxy; public class UserService implements IUserService { @Override public String query(String name) { System.out.println("query user from database..."); return "hello " + name; } }代理对象生成类
package com.morris.spring.proxy.dynamic.myjdk; import javax.tools.JavaCompiler; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import .URL; import .URLClassLoader; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; public class Proxy { protected MyInvocationHandler h; public static Object newProxyInstance(Class inter, MyInvocationHandler myInvocationHandler) { try { // 生成java文件内容 String javaFileContent = generateJavaFileContent(inter); // java文件 File file = new File("D:\\com\\google\\$Proxy.java"); // 生成文件 generateFile(javaFileContent, file); // 编译 compile(file); // 加载class文件 URL[] urls = new URL[]{new URL("file:D:\\\\")}; URLClassLoader classLoader = new URLClassLoader(urls); Class<?> clazz = classLoader.loadClass("com.google.$Proxy"); // 不能直接clazz.newInstance(),这个方法是调用无参构造方法,代理对象构造方法有个参数为h Constructor<?> constructor = clazz.getConstructors()[0]; Object o = constructor.newInstance(myInvocationHandler); return o; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 将java文件编译成class文件 * @param targetFile */ private static void compile(File targetFile) { try { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager stdManager = compiler.getStandardFileManager(null, null, null); stdManager.getJavaFileObjects(targetFile); Iterable units = stdManager.getJavaFileObjects(targetFile); JavaCompiler.CompilationTask t = compiler.getTask(null, stdManager, null, null, null, units); t.call(); stdManager.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 生成java文件 * @param content * @param targetFile */ private static void generateFile(String content, File targetFile) { // 保存到文件 if (!targetFile.getParentFile().exists()) { targetFile.getParentFile().mkdirs(); } FileWriter fileWriter = null; try { fileWriter = new FileWriter(targetFile); fileWriter.write(content); fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 根据接口生成代理类内容,内容大概格式如下: * package com.google; * * import com.morris.spring.proxy.IUserService; * import com.morris.spring.proxy.dynamic.myjdk.Proxy; * import com.morris.spring.proxy.IUserService; * import java.lang.reflect.Method; * import com.morris.spring.proxy.dynamic.myjdk.MyInvocationHandler; * * public class $Proxy extends Proxy implements IUserService { * * public $Proxy(MyInvocationHandler h){ * this.h=h; * } * * public java.lang.String query(java.lang.String p0) { * try { * Method method = Class.forName("com.morris.spring.proxy.IUserService").getDeclaredMethod("query", new Class[]{java.lang.String.class}); * return (java.lang.String) h.invoke(this, method, new Object[]{p0}); * } catch (Throwable e) { * e.printStackTrace(); * } * return null; * } * * } * * @param inter * @return */ private static String generateJavaFileContent(Class inter) { StringBuilder sb = new StringBuilder(); sb.append("package com.google;").append("\n\n"); // 包名com.google // 导入必须的包名 sb.append("import ").append(inter.getName()).append(";\n"); sb.append("import com.morris.spring.proxy.dynamic.myjdk.Proxy;\n"); sb.append("import " + inter.getName() + ";\n"); sb.append("import java.lang.reflect.Method;\n"); sb.append("import com.morris.spring.proxy.dynamic.myjdk.MyInvocationHandler;\n\n"); // 类名$Proxy,继承Proxy,实现目标接口 sb.append("public class $Proxy extends Proxy implements ").append(inter.getSimpleName()).append(" {\n\n"); // 构造方法,传入自定义的MyInvocationHandler,h属性在父类Proxy中 sb.append("\tpublic $Proxy(MyInvocationHandler h){\n"); sb.append("\t\tthis.h=h;\n"); sb.append("\t}\n\n"); Method[] methods = inter.getMethods(); // 拼接代理方法 for (Method method : methods) { sb.append("\tpublic ").append(method.getReturnType().getName()).append(" ").append(method.getName()).append("("); Class<?>[] parameterTypes = method.getParameterTypes(); List<String> paramList = new ArrayList<>(); // ["java.lang.String p0"] List<String> paramClassList = new ArrayList<>(); // [java.lang.String.class] List<String> argsList = new ArrayList<>(); // [p0,p1] for (int i = 0; i < parameterTypes.length; i++) { Class<?> parameterType = parameterTypes[i]; paramList.add(parameterType.getName() + " p" + i); paramClassList.add(parameterType.getName() + ".class"); argsList.add("p" + i); } String argsStr = argsList.stream().collect(Collectors.joining(",", "new Object[]{", "}")); String paramContentStr = paramClassList.stream().collect(Collectors.joining(",", "new Class[]{", "}")); String paramStr = paramList.stream().collect(Collectors.joining(",")); sb.append(paramStr).append(") {\n"); sb.append("\t\ttry {\n"); sb.append("\t\t\tMethod method = Class.forName(\"" + inter.getName() + "\").getDeclaredMethod(\"" + method.getName() + "\", " + paramContentStr + ");\n"); sb.append("\t\t\treturn (" + method.getReturnType().getName() + ") h.invoke(this, method, " + argsStr + ");\n"); sb.append("\t\t} catch (Throwable e) {\n"); sb.append("\t\t\te.printStackTrace();\n"); sb.append("\t\t}\n"); sb.append("\t\treturn null;\n"); sb.append("\t}\n\n"); } sb.append("}"); return sb.toString(); } }测试类
package com.morris.spring.proxy.dynamic.myjdk; public class Test { public static void main(String[] args) { IUserService userService = (IUserService)ProxyFactory.newProxyInstance(new UserService()); String result = userService.query(); System.out.println(result); } }运行结果如下:
package com.morris.spring.proxy.dynamic.myjdk; import com.morris.spring.proxy.IUserService; import com.morris.spring.proxy.UserService; public class Test { public static void main(String[] args) { IUserService userService = (IUserService)Proxy.newProxyInstance(IUserService.class, new MyUserServiceInvocationHandler(new UserService())); String result = userService.query("morris"); System.out.println(result); } }运行结果如下:
MyUserServiceInvocationHandler invoke query user from database... hello morris动态代理生成的文件内容下:
package com.google; import com.morris.spring.proxy.IUserService; import com.morris.spring.proxy.dynamic.myjdk.Proxy; import com.morris.spring.proxy.IUserService; import java.lang.reflect.Method; import com.morris.spring.proxy.dynamic.myjdk.MyInvocationHandler; public class $Proxy extends Proxy implements IUserService { public $Proxy(MyInvocationHandler h){ this.h=h; } public java.lang.String query(java.lang.String p0) { try { Method method = Class.forName("com.morris.spring.proxy.IUserService").getDeclaredMethod("query", new Class[]{java.lang.String.class}); return (java.lang.String) h.invoke(this, method, new Object[]{p0}); } catch (Throwable e) { e.printStackTrace(); } return null; } }存在的问题
- 代码中会生成java文件,会操作IO(这个操作会成为瓶颈)。
待改进的地方
- 目前只实现了一个接口的动态代理。
- 代理类包名固定了,如果接口的访问修饰符是package级别的,则会报错。
- 代理类名固定了,如果产生多个代理类,则会报错。
代理模式-动态代理(基于接口,JDK动态代理)jdk动态代理和cglib动态代理
Java动态代理之JDK动态代理和CGLib动态代理jdk动态代理
谁与争锋,JDK动态代理大战CGLib动态代理jdk动态代理和cglib动态代理
Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理jdk静态代理和动态代理
静态代理,JDK动态代理,Cglib动态代理详解jdk动态代理
#yyds干货盘点# 设计模式之静态代理,jdk动态代理,cglib动态代理的区别jdk静态代理和动态代理
代理模式-动态代理详解(JDK动态代理和CGLIB动态代理)jdk cglib动态代理
代理模式-动态代理(基于接口,JDK动态代理)cglib代理和jdk动态代理区别
动态代理:JDK动态代理和CGLIB代理的区别cglib代理和jdk动态代理区别