实现自己的JDK动态代理- 2020-11-1 22:19- 设计模式: 设计模式,动态代理,JDK


jdk动态代理其实很简单,一般看过jdk动态代理生成的源码就明白了。

接口:HelloService
真实被代理类:HelloServiceImpl
自定义Proxy类(模拟原生Proxy类):MyProxy
自定义InvocationHandler(模拟InvocationHandler类):MyInvocationHandler
自定义ClassLoader(模拟ClassLoader):MyClassLoader

自定义动态代理 流程梳理

1.动态生成 代理类 .java文件
2.将java文件编译成class文件
3.将class文件加载进jvm

真实对象

  1. /**
  2. * 接口类
  3. *
  4. * @Author Bai
  5. * @Date 2020-10-26 14:25
  6. */
  7. public interface HelloService {
  8. void say(String msg);
  9. }

实现自己的Proxy类

  1. import javax.tools.JavaCompiler;
  2. import javax.tools.StandardJavaFileManager;
  3. import javax.tools.ToolProvider;
  4. import java.io.File;
  5. import java.io.FileWriter;
  6. import java.lang.reflect.Constructor;
  7. import java.lang.reflect.Method;
  8. /**
  9. * 实现自己的Proxy类
  10. *
  11. * @Author Bai
  12. * @Date 2020-11-08 12:14
  13. */
  14. public class MyProxy {
  15. public static final String ln = "\r\n";
  16. public static Object newProxyInstance(MyClassLoader classLoader, Class<?>[] interfaces, MyInvocationHandler h) {
  17. try {//1、动态生成源代码.java 文件
  18. String src = generateSrc(interfaces);
  19. System.out.println(src);
  20. // 2、Java 文件输出磁盘
  21. String filePath = MyProxy.class.getResource("").getPath();
  22. System.out.println(filePath);
  23. File f = new File(filePath + "$Proxy0.java");
  24. FileWriter fw = new FileWriter(f);
  25. fw.write(src);
  26. fw.flush();
  27. fw.close();
  28. // 3、把生成的.java 文件编译成.class 文件
  29. JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
  30. StandardJavaFileManager manage = compiler.getStandardFileManager(null, null, null);
  31. Iterable iterable = manage.getJavaFileObjects(f);
  32. JavaCompiler.CompilationTask task = compiler.getTask(null, manage, null, null, null, iterable);
  33. task.call();
  34. manage.close();
  35. //4、编译生成的.class 文件加载到 JVM 中来
  36. Class proxyClass = classLoader.findClass("$Proxy0");
  37. Constructor c = proxyClass.getConstructor(MyInvocationHandler.class);
  38. f.delete();
  39. //5、返回字节码重组以后的新的代理对象
  40. return c.newInstance(h);
  41. } catch (Exception e) {
  42. e.printStackTrace();
  43. }
  44. return null;
  45. }
  46. private static String generateSrc(Class<?>[] interfaces) {
  47. StringBuffer sb = new StringBuffer();
  48. sb.append("package com.xy.blog.test.poxy.my.jdkdynamic;" + ln);
  49. sb.append("import com.xy.blog.test.poxy.my.jdkdynamic.MyInvocationHandler;" + ln);
  50. sb.append("import com.xy.blog.test.poxy.my.jdkdynamic.MyProxy;" + ln);
  51. sb.append("import com.xy.blog.test.poxy.HelloService;" + ln);
  52. sb.append("import java.lang.reflect.*;" + ln);
  53. sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
  54. sb.append("MyInvocationHandler h;" + ln);
  55. sb.append("public $Proxy0(MyInvocationHandler h) { " + ln);
  56. sb.append("this.h = h;");
  57. sb.append("}" + ln);
  58. for (Method m : interfaces[0].getMethods()) {
  59. Class<?>[] params = m.getParameterTypes();
  60. StringBuffer paramNames = new StringBuffer();
  61. StringBuffer paramValues = new StringBuffer();
  62. StringBuffer paramClasses = new StringBuffer();
  63. for (int i = 0; i < params.length; i++) {
  64. Class clazz = params[i];
  65. String type = clazz.getName();
  66. String paramName = toLowerFirstCase(clazz.getSimpleName());
  67. paramNames.append(type + " " + paramName);
  68. paramValues.append(paramName);
  69. paramClasses.append(clazz.getName() + ".class");
  70. if (i > 0 && i < params.length - 1) {
  71. paramNames.append(",");
  72. paramClasses.append(",");
  73. paramValues.append(",");
  74. }
  75. }
  76. sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "(" + paramNames.toString() + ") {" + ln);
  77. sb.append("try{" + ln);
  78. sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{" + paramClasses.toString() + "});" + ln);
  79. sb.append((hasReturnValue(m.getReturnType()) ? "return " : "") + "this.h.invoke(this,m,new Object[]{" + paramValues + "});" + ln);
  80. sb.append("}catch(Error _ex) { }");
  81. sb.append("catch(Throwable e){" + ln);
  82. sb.append("throw new UndeclaredThrowableException(e);" + ln);
  83. sb.append("}");
  84. sb.append(getReturnEmptyCode(m.getReturnType()));
  85. sb.append("}");
  86. }
  87. sb.append("}" + ln);
  88. return sb.toString();
  89. }
  90. private static boolean hasReturnValue(Class<?> clazz) {
  91. return clazz != void.class;
  92. }
  93. private static String toLowerFirstCase(String src) {
  94. char[] chars = src.toCharArray();
  95. chars[0] += 32;
  96. return String.valueOf(chars);
  97. }
  98. private static String getReturnEmptyCode(Class<?> returnClass) {
  99. if (returnClass == void.class) {
  100. return "";
  101. } else {
  102. return "return null;";
  103. }
  104. }
  105. }

实现自己的JDK动态代理handler

  1. import java.lang.reflect.Method;
  2. /**
  3. * 实现自己的JDK动态代理handler
  4. *
  5. * @Author Bai
  6. * @Date 2020-11-08 12:14
  7. */
  8. public interface MyInvocationHandler {
  9. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
  10. }

实现自己的classLoader

  1. import java.io.ByteArrayOutputStream;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.IOException;
  5. /**
  6. * 实现自己的classLoader
  7. *
  8. * @Author Bai
  9. * @Date 2020-11-08 12:45
  10. */
  11. public class MyClassLoader extends ClassLoader {
  12. private File classPathFile;
  13. public MyClassLoader() {
  14. String classPath = MyClassLoader.class.getResource("").getPath();
  15. this.classPathFile = new File(classPath);
  16. }
  17. @Override
  18. protected Class<?> findClass(String name) throws ClassNotFoundException {
  19. String className = MyClassLoader.class.getPackage().getName() + "." + name;
  20. if (classPathFile != null) {
  21. File classFile = new File(classPathFile, name.replaceAll("\\.", "/") + ".class");
  22. if (classFile.exists()) {
  23. FileInputStream in = null;
  24. ByteArrayOutputStream out = null;
  25. try {
  26. in = new FileInputStream(classFile);
  27. out = new ByteArrayOutputStream();
  28. byte[] buff = new byte[1024];
  29. int len;
  30. while ((len = in.read(buff)) != -1) {
  31. out.write(buff, 0, len);
  32. }
  33. return defineClass(className, out.toByteArray(), 0, out.size());
  34. } catch (Exception e) {
  35. e.printStackTrace();
  36. } finally {
  37. if (null != in) {
  38. try {
  39. in.close();
  40. } catch (IOException e) {
  41. e.printStackTrace();
  42. }
  43. }
  44. if (out != null) {
  45. try {
  46. out.close();
  47. } catch (IOException e) {
  48. e.printStackTrace();
  49. }
  50. }
  51. }
  52. }
  53. }
  54. return null;
  55. }
  56. }

实现HelloInvocationHandler

  1. import com.xy.blog.test.poxy.HelloService;
  2. import sun.misc.ProxyGenerator;
  3. import java.io.FileOutputStream;
  4. import java.io.IOException;
  5. import java.lang.reflect.Method;
  6. /**
  7. * JDK动态代理handler
  8. *
  9. * @Author Bai
  10. * @Date 2020-10-26 14:52
  11. */
  12. public class HelloMyInvocationHandler implements MyInvocationHandler {
  13. /**
  14. * 真实对象:被代理的对象引用
  15. */
  16. private Object target;
  17. public HelloMyInvocationHandler(Object target) {
  18. super();
  19. this.target = target;
  20. }
  21. /**
  22. * @param proxy 代理类
  23. * @param method 目标方法
  24. * @param args 入参
  25. * @return
  26. * @throws Throwable
  27. */
  28. @Override
  29. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  30. System.out.println("MyInvocationHandler#invoke start");
  31. //两者执行有何区别?target or proxy
  32. //proxy 会造成死循环,猜测可能是因为invoke内部实现 需要生成proxy的实例,去调用proxy实例的invoke,就会造成循环调用
  33. // Object invoke = method.invoke(proxy, args);
  34. Object invoke = method.invoke(target, args);
  35. System.out.println("MyInvocationHandler#invoke end");
  36. return invoke;
  37. }
  38. /**
  39. * 获取jdk生成的代理对象
  40. *
  41. * @return
  42. */
  43. public Object getProxy() {
  44. //下面的写法会报错 因为第二个入参需要的是接口对象,而target是具体实现类的引用 并发接口
  45. // return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{target.getClass()}, this);
  46. //第一个入参 使用哪个loader将proxy对象加载到内存中,一般是跟被代理对象同个loader就可以
  47. //第二个入参 proxy要代理的是哪些接口
  48. Class<?> aClass = target.getClass();
  49. return MyProxy.newProxyInstance(new MyClassLoader(), aClass.getInterfaces(), this);
  50. }
  51. private void saveProxyClass0() {
  52. //通过反编译工具可以查看源代码
  53. byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{HelloService.class});
  54. try {
  55. FileOutputStream os = new FileOutputStream("E://$Proxy0.class");
  56. os.write(bytes);
  57. os.close();
  58. } catch (IOException e) {
  59. e.printStackTrace();
  60. }
  61. }
  62. }

测试

  1. /**
  2. * 实现自己的jdk动态代理
  3. */
  4. @Test
  5. public void jdkMyProxyTest() throws Throwable {
  6. //真实对象
  7. HelloService helloService = new HelloServiceImpl();
  8. //代理handler
  9. HelloMyInvocationHandler handler = new HelloMyInvocationHandler(helloService);
  10. // 代理对象
  11. HelloService helloProxyService = (HelloService) handler.getProxy();
  12. //使用代理对象执行
  13. System.out.println("--- helloProxyService.result ---");
  14. helloProxyService.say("helloProxyService.自己的jdk动态代理");
  15. helloProxyService.eat("累了 开吃");
  16. }

**