自己动手实现java 动态代理

2023-11-08

前言:

上一篇讲到jdk动态代理的实现
现在来讲一下自己动手实现java 动态代理

上一篇: jdk 动态代理

2. 自定义实现:

分析:

想要自定义实现,有5个步骤:

  1. 生成增强类源代码
  2. 把源代码输出到磁盘
  3. 编译成class文件
  4. 加载到jvm中
  5. 返回被代理后的代理对象

首先我们需要拥有自己的GPProxy, GPInvocationHandler,GPMeipo, GPClassLoader这样四个类

  1. GPProxy: 自定义的Proxy类,里面有生成源代码的方法,把源代码输出到磁盘的方法,把.java编译成.class的方法
  2. GPInvocationHandler: 自定义的媒婆需要实现的接口
  3. GPMeipo: 自定义的媒婆
  4. GPClassLoader: 自定义的类加载器,里面有把生成的.class文件加载到jvm中的方法
代码

GPPRoxy:

public class GPProxy {

	// 定义换行符
	private static String ln = "\r\n";

	protected GPInvocationHandler h;

	public static Object newProxyInstance(GPClassLoader loader, Class<?>[] interfaces, GPInvocationHandler h)
			throws IllegalArgumentException {

		try {
			String className = "$Proxy";
			// 1.生成源代码
			String classSrc = generateJava(className, interfaces[0]);

			// 2.把源代码输出到磁盘
			String filepath = GPProxy.class.getResource("").getPath() + className + ".java";
			File f = generateSrcOnDisk(classSrc, filepath);

			// 3.编译成class文件
			compilerClass(f);

			// 4.加载到jvm中
			// 找到对应类
			Class<?> proxyClass = loader.findClass(className);

			// 5.返回被代理后的代理对象

			// 选择构造方法
			Constructor<?> constructor = proxyClass.getConstructor(GPInvocationHandler.class);
			// 构造对象
			return constructor.newInstance(h);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {

		}
		return h;
	}

	/**
	 * 通过接口生成class文件
	 * 
	 * @param interfece
	 * @return
	 */
	public static String generateJava(String className, Class<?> intface) {
		String packageName = GPProxy.class.getPackage().getName();
		StringBuffer sb = new StringBuffer();
		sb.append("package " + packageName + ";" + ln);
		sb.append("import java.lang.reflect.Method;" + ln);

		sb.append("public class " + className + " implements " + intface.getName() + "{" + ln);

		sb.append("GPInvocationHandler h;" + ln);
		// 构造方法
		sb.append("public " + className + "(GPInvocationHandler h){" + ln);
		sb.append("this.h = h;" + ln);
		sb.append("}" + ln);

		for (Method m : intface.getMethods()) {
			// 成员方法
			sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln);

			sb.append("try{" + ln);
			sb.append("Method m = " + intface.getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});"
					+ ln);
			sb.append("this.h.invoke(this,m,null);" + ln);
			sb.append("}catch(Throwable e){" + ln);
			sb.append("	e.printStackTrace();" + ln);
			sb.append("}" + ln);

			if (!m.getReturnType().getName().equals("void")) {
				sb.append("return null;" + ln);
			}
			sb.append("}" + ln);
		}
		sb.append("}" + ln);

		return sb.toString();
	}

	/**
	 * 在磁盘生成.java文件
	 * 
	 * @param classSrc
	 * @param filepath
	 * @return
	 */
	public static File generateSrcOnDisk(String classSrc, String filepath) {
		File f = null;
		FileWriter fw = null;
		try {
			f = new File(filepath);
			fw = new FileWriter(f);
			fw.write(classSrc);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				fw.flush();
				fw.close();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
		return f;
	}

	/**
	 * 编译.java文件
	 * 
	 * @param f
	 */
	public static void compilerClass(File f) {
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager sm = compiler.getStandardFileManager(null, null, null);
		Iterable<? extends JavaFileObject> iterable = sm.getJavaFileObjects(f);
		CompilationTask task = compiler.getTask(null, sm, null, null, null, iterable);
		task.call();
		try {
			sm.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

GPMeipo :

public class GPMeipo implements GPInvocationHandler {

	private Person source;

	public Object getInstance(Person source) {
		this.source = source;
		Class<? extends Person> clazz = source.getClass();
		return GPProxy.newProxyInstance(new GPClassLoader(), clazz.getInterfaces(), this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("我是媒婆,相亲开始");
		method.invoke(this.source, args);
		System.out.println("相亲结束");
		return null;
	}

}

GPClassLoader :

public class GPClassLoader extends ClassLoader {

	private File baseDir;

	public GPClassLoader() {
		String basepath = GPClassLoader.class.getResource("").getPath();
		this.baseDir = new File(basepath);
	}

	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		String className = GPClassLoader.class.getPackage().getName() + "." + name;
		if (this.baseDir != null) {
			File f = new File(this.baseDir, name.replace(".", "//") + ".class");
			FileInputStream in = null;
			ByteArrayOutputStream out = null;
			if (f.exists()) {
				try {
					in = new FileInputStream(f);
					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 {
					try {
						in.close();
						out.close();
					} catch (Exception e2) {
						e2.printStackTrace();
					}
				}
			}
		}
		return null;
	}
}

总结:

能够自己动手实现一下提升还是挺大的,能够更深入的理解动态代理的实现过程以及动态代理的应用场景.

参考

泡沽学院spring源码第二讲

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

自己动手实现java 动态代理 的相关文章

随机推荐

  • [JAVAee]IP数据包的组包与分包

    目录 数据包是什么 数据包的结构 数据包 分组与分组交换 分包是什么 为什么需要分包呢 组包是什么 分包组包过程中和哪些 IP 报头字段有关联 本篇文章主要围绕三个问题来展开 为什么要分包 分包组包过程中和哪些 IP 报头字段有关联 组包时
  • matlab怎么显示当前文件夹和工作区

    参考 matlab怎么显示当前文件夹和工作区 云 社区 腾讯云 1 第一步在我们的电脑上打开matlab 可以看到界面上目前没有显示当前文件夹和工作区 如下图所示 2 第二步我们点击主页右侧的布局 可以看到显示下的当前文件夹和工作区没有勾选
  • 遗传算法入门到掌握(一)

    遗传算法入门到掌握 一 心得 把解决方案做染色体 遗传算法的有趣应用很多 诸如寻路问题 8数码问题 囚犯困境 动作控制 找圆心问题 这是一个国外网友的建议 在一个不规则的多边形 中 寻找一个包含在该多边形内的最大圆圈的圆心 TSP问题 在以
  • node.js HTTP模块 URL模块 supervisor工具

    在vscode 上安装一个插件 Node Snippets 创建一个js文件 直接输入node 就能创建一个服务器 node http server var http require http 表示引入一个HTTP模块 request 表示
  • Texture Format全解析

    Texture Format全解析 Texture Format全解析 What internal representation is used for the texture This is a tradeoff between size
  • sqlmap的安装及使用教程

    1 sqlmap简介 sqlmap 是一个开源渗透测试工具 可自动检测和利用 SQL 注入缺陷并接管数据库服务器 支持多种数据库和多种注入技术 2 sqlmap安装 sqlmap是基于python环境的 因此安装前需要安装python环境
  • Vue + element中table来回切换页面显示差异问题

    这次做项目遇到多个table切换页面显示差异的问题 当我切换到上个页面的时候 表格有一列是下个页面的内容 切换到下个页面的时候那一列的内容消失了 就很奇葩 这个问题也是我都一次遇到 问了Baidu阿姨 上面的解决办法好像都不行 这个时候感觉
  • esxi能直通的显卡型号_虚拟黑群也可以NVMe加速?还能万兆?wa!

    之前我发了三篇关于FreeBSD虚拟机安装黑群晖的文章 没有阅读的同学可以看一下 钱乎 如何拥有一台100T容量的NAS Let s do it zhuanlan zhihu com 钱乎 100TB的NAS之后续填坑 虚拟硬件篇 zhua
  • Gradle sync failed: Sync failed: reason unknown

    android项目复制后打开总是报这个错误 打开build查看红色错误提示 比如提示缺少android 23 打开file gt setting 展开后把对应的SDK勾选上再ok
  • 2021计算机考硕复试线,2021考研复试线汇总:国家线+34所中5所院校复试线①

    原标题 2021考研复试线汇总 国家线 34所中5所院校复试线 突如其来 昨天晚上 中国教育发布 公布了2021年硕士研究生考试学术学位类和专业学位类的国家线 随后 34中的5所院校也更新了2021年复试线成绩 包含以下这5所院校 清华大学
  • vue项目Error: Cannot find module ‘xxx’类报错的解决方法

    现发现只要是报错 Error Cannot find module xxx 例如 Error Cannot find module webpack 这类的问题都可以用下面的方法解决 报错内容如下 运行 npm install 没问题 运行
  • 消息队列的两种模式

    Java消息服务 Java Message Service JMS 应用程序接口是一个Java平台中关于面向消息中间件 MOM 的API 用于在两个应用程序之间 或分布式系统中发送消息 进行异步通信 点对点与发布订阅最初是由JMS定义的 这
  • Box2D C++ 教程-物体

    Box2D C 教程 物体 物体 Bodies 物体是物理场景中的基本对象 但是这里的物体并不是你看到的实际互相弹跳碰撞的实物 听起来很费解吗 挺住 马上做解释 http ohcoder com blog 2012 11 29 bodies
  • springboot+log4j2遇到的坑

    情景再现 按照网上一般的教程 这样 并添加 去除 common logging的jar 实际过程中 仍然报错会发出警告 大概意思如下 slfj仍然用着springboot默认的logback 包有冲突 所以第一图并没有起作用 而且logba
  • 【20200326】数据挖掘课程课业打卡三

    20200326 数据挖掘课业打卡三之数据质量 数据挖掘课业打卡三之数据质量 一 单选题 二 填空题 三 判断题 知识点汇总 1 数据中可能存在的问题 2 数据预处理方法 3 关于欧几里得距离 叮嘟 这里是小啊呜的学习课程资料整理 好记性不
  • the host '192.168.2.100'is unreachable,The host may be down,or there may be a problem with......解决办法

    我这里遇到这样的问题 是因为网关和网络IP没有配置对造成的 解决办法如下 第一步 打开VM 点中要操作的虚拟机 鼠标点击左上角 编辑 虚拟网络编辑器 如下图所示 第二步 若是选择自定义网关 即选择如上图的VMnet8模式 选中VMnet8
  • WebSocket整合spring 一文全部搞定

    文章声明 本文简单整合了webSocket 组件 涉及到的源码分解 原理什么的以后再说 本文只适合入门小白体验 不涉及复杂业务逻辑 文章目录 1 引入webSocket依赖包 2 声明式整合WebSocket 这是一道硬菜 2 1 webS
  • C++STL模板库——deque容器

    今天介绍的是双端队列deque 也是STL中非常重要的内容 include
  • QT/QTCreator开发/使用技巧

    调试模式完整的展示过长的字符串 如图 当字符串过长时在调试模式下 无法非常清晰的看到全部的字符串 此时可以通过 右键菜单 change value display format spearate Window 此时字符串将单独显示在一个独立
  • 自己动手实现java 动态代理

    前言 上一篇讲到jdk动态代理的实现 现在来讲一下自己动手实现java 动态代理 上一篇 jdk 动态代理 2 自定义实现 分析 想要自定义实现 有5个步骤 生成增强类源代码 把源代码输出到磁盘 编译成class文件 加载到jvm中 返回被