当前位置:首页 > CN2资讯 > 正文内容

手写JDK动态代理

11小时前CN2资讯


下面模拟JDK动态代理写一个自己的动态代理。

类图

思路分析

  • 先根据目标类所实现的接口生成java文件内容。
  • 将java文件内容生成到.java文件。
  • 编译.java文件为.class文件。
  • 加载.class文件。
  • 根据Class对象new一个对象。
  • 具体实现

    目标类接口

    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级别的,则会报错。
    • 代理类名固定了,如果产生多个代理类,则会报错。


      你可能想看:

      扫描二维码推送至手机访问。

      版权声明:本文由皇冠云发布,如需转载请注明出处。

      本文链接:https://www.idchg.com/info/25023.html

      分享给朋友:

      “手写JDK动态代理” 的相关文章

      CN2 GIA是什么?探索高效国际网络连接的解决方案

      在当今这个数字化时代,网络连接的稳定性与速度成为了企业和个人活动的重中之重。CN2 GIA,或称为全球互联网接入(Global Internet Access),是由中国电信提供的一项高级国际专线网络服务。这项服务在CN2产品线中占据了顶级位置,专为那些需要快速且稳定的国际网络连接的用户而设计。通过...

      UCloud优:云计算服务平台的领先者与优势分析

      UCloud优的基本介绍 谈到UCloud,首先让我想起它成立的背景以及它是如何从一颗种子成长为今天的云计算巨头。UCloud,或者说优刻得科技股份有限公司,于当时顺应了数字化转型的浪潮。这是一个中立、安全的云计算服务平台,专注于为各行各业提供云服务。它的创立背景与各种市场需求紧密相连,尤其是企业对...

      专业网站被墙检测工具及应对措施攻略

      网站被墙检测工具概述 网络环境的日益复杂,使得网站被墙的问题变得越来越普遍。这种封锁不仅影响了网站的访问量,还可能损害企业的形象和信誉。了解网站被墙的定义及其影响,是我们拥有更好网络体验的基础。 网站被墙,简单来说,指的是某些网站因各种政策或技术原因,无法在特定地区被访问的现象。这种情况会导致用户无...

      HostYun:高性价比VPS服务的理想选择

      HostYun,最早被称作主机分享,成立于2008年,专注于提供性价比极高的VPS服务。在众多IDC品牌中,HostYun凭借其低价策略迅速占领了一席之地。作为一个以KVM和XEN虚拟化技术为基础的平台,HostYun不仅满足了用户对低成本服务的需求,也为学习、测试和小型项目的部署提供了理想的选择。...

      详细指南:如何进行Linux扩容以解决存储不足问题

      什么是Linux扩容? 在使用Linux操作系统时,随着数据的增加,我们常常面临磁盘空间不足的问题。Linux扩容就是通过添加新的磁盘、扩展现有磁盘容量或利用逻辑卷管理(LVM)等方式,来增加系统的存储空间。扩容可以帮助我更好地管理数据,提高服务器的运行效率。 我记得第一次接触扩容时,面对不断增加的...

      VPS硬盘清理:提升服务器性能的全面指南

      当我的VPS(虚拟专用服务器)磁盘满了时,事情往往会变得非常棘手。这不仅会导致应用程序的运行速度变慢,甚至可能引发系统崩溃或数据丢失。这样的状况让我不得不思考,定期进行硬盘清理的重要性。其实,维护良好的磁盘使用状况,对于确保服务器的整体性能至关重要。 首先,当VPS磁盘满了,系统的反应速度会明显下降...