JDK * 过程原理及手写实现详解
作者:丨Jack_Chen丨 发布时间:2022-07-07 00:35:23
标签:JDK, ,
JDK * 的过程
JDK * 采用字节重组,重新生成对象来替代原始对象,以达到 * 的目的。
JDK中有一个规范,在ClassPath下只要是$开头的.class文件,一般都是自动生成的。
要实现JDK * 生成对象,首先得弄清楚JDK * 的过程。
1.获取被代理对象的引用,并且使用反射获取它的所有接口。
2.JDK * 类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口。
3.动态生成Java代码,新添加的业务逻辑方法由一定的逻辑代码调用。
4.编译新生成的Java代码(.class文件)。
5.重新加载到VM中运行。
手写实现JDK *
JDK * 功能非常强大, 接下来就模仿JDK * 实现一个属于自己的 * 。
创建MyInvocationHandler接口
参考JDK * 的InvocationHandler
接口,创建属于自己的MyInvocationHandler
接口
public interface MyInvocationHandler {
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
创建MyClassLoader类加载器
public class MyClassLoader extends ClassLoader {
private File classPathFile;
public MyClassLoader() {
String classPath = MyClassLoader.class.getResource("").getPath();
this.classPathFile = new File(classPath);
}
@Override
protected Class<?> findClass(String name) {
String className = MyClassLoader.class.getPackage().getName() + "." + name;
if (classPathFile != null) {
File classFile = new File(classPathFile, name.replaceAll("\\.", "/") + ".class");
if (classFile.exists()) {
FileInputStream in = null;
ByteArrayOutputStream out = null;
try {
in = new FileInputStream(classFile);
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();
}
}
}
return null;
}
}
创建代理类
创建的代理类是整个JDK * 的核心
public class MyProxy {
// 回车、换行符
public static final String ln = "\r\n";
/**
* 重新生成一个新的类,并实现被代理类实现的所有接口
*
* @param classLoader 类加载器
* @param interfaces 被代理类实现的所有接口
* @param invocationHandler
* @return 返回字节码重组以后的新的代理对象
*/
public static Object newProxyInstance(MyClassLoader classLoader, Class<?>[] interfaces, MyInvocationHandler invocationHandler) {
try {
// 动态生成源代码.java文件
String sourceCode = generateSourceCode(interfaces);
// 将源代码写入到磁盘中
String filePath = MyProxy.class.getResource("").getPath();
File f = new File(filePath + "$Proxy0.java");
FileWriter fw = new FileWriter(f);
fw.write(sourceCode);
fw.flush();
fw.close();
// 把生成的.java文件编译成.class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manage = compiler.getStandardFileManager(null, null, null);
Iterable iterable = manage.getJavaFileObjects(f);
JavaCompiler.CompilationTask task = compiler.getTask(null, manage, null, null, null, iterable);
task.call();
manage.close();
// 编译生成的.class文件加载到JVM中来
Class proxyClass = classLoader.findClass("$Proxy0");
Constructor c = proxyClass.getConstructor(MyInvocationHandler.class);
//删除生成的.java文件
f.delete();
// 返回字节码重组以后的新的代理对象
return c.newInstance(invocationHandler);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 动态生成源代码.java文件
*
* @param interfaces 被代理类实现的所有接口
* @return .java文件的源代码
*/
private static String generateSourceCode(Class<?>[] interfaces) {
StringBuffer sb = new StringBuffer();
sb.append(MyProxy.class.getPackage() + ";" + ln);
sb.append("import " + interfaces[0].getName() + ";" + ln);
sb.append("import java.lang.reflect.*;" + ln);
sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
sb.append("MyInvocationHandler invocationHandler;" + ln);
sb.append("public $Proxy0(MyInvocationHandler invocationHandler) { " + ln);
sb.append("this.invocationHandler = invocationHandler;");
sb.append("}" + ln);
for (Method m : interfaces[0].getMethods()) {
Class<?>[] params = m.getParameterTypes();
StringBuffer paramNames = new StringBuffer();
StringBuffer paramValues = new StringBuffer();
StringBuffer paramClasses = new StringBuffer();
for (int i = 0; i < params.length; i++) {
Class clazz = params[i];
String type = clazz.getName();
String paramName = toLowerFirstCase(clazz.getSimpleName());
paramNames.append(type + " " + paramName);
paramValues.append(paramName);
paramClasses.append(clazz.getName() + ".class");
if (i > 0 && i < params.length - 1) {
paramNames.append(",");
paramClasses.append(",");
paramValues.append(",");
}
}
sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "(" + paramNames + ") {" + ln);
sb.append("try{" + ln);
sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{" + paramClasses + "});" + ln);
sb.append((hasReturnValue(m.getReturnType()) ? "return " : "") + getCaseCode("this.invocationHandler.invoke(this,m,new Object[]{" + paramClasses + "})", m.getReturnType()) + ";" + ln);
sb.append("}catch(Error ex) { }");
sb.append("catch(Throwable e){" + ln);
sb.append("throw new UndeclaredThrowableException(e);" + ln);
sb.append("}");
sb.append(getReturnEmptyCode(m.getReturnType()));
sb.append("}");
}
sb.append("}" + ln);
return sb.toString();
}
/**
* 定义返回类型
*/
private static Map<Class, Class> mappings = new HashMap<Class, Class>();
/**
* 初始化一些返回类型
*/
static {
mappings.put(int.class, Integer.class);
mappings.put(Integer.class, Integer.class);
mappings.put(double.class, Double.class);
mappings.put(Double.class, Double.class);
}
private static String getReturnEmptyCode(Class<?> returnClass) {
if (mappings.containsKey(returnClass)) {
if (returnClass.equals(int.class) || returnClass.equals(Integer.class)) {
return "return 0;";
} else if (returnClass.equals(double.class) || returnClass.equals(Double.class)) {
return "return 0.0;";
} else {
return "return 0;";
}
} else if (returnClass == void.class) {
return "";
} else {
return "return null;";
}
}
/**
* 判断返回值类型
*
* @param code
* @param returnClass
* @return
*/
private static String getCaseCode(String code, Class<?> returnClass) {
if (mappings.containsKey(returnClass)) {
// ((java.lang.Double) this.invocationHandler.invoke(this, m, new Object[]{})).doubleValue();
String re = "((" + mappings.get(returnClass).getName() + ")" + code + ")." + returnClass.getSimpleName().toLowerCase() + "Value()";
return re;
}
return code;
}
/**
* 判断代理接口的方法的返回值是否为void
*
* @param clazz 方法的返回值类型
* @return
*/
private static boolean hasReturnValue(Class<?> clazz) {
return clazz != void.class;
}
/**
* 参数首字母小写
*
* @param src
* @return
*/
private static String toLowerFirstCase(String src) {
char[] chars = src.toCharArray();
if (chars[0] >= 'A' && chars[0] <= 'Z') {
chars[0] += 32;
}
return String.valueOf(chars);
}
/**
* 首字母大写
*
* @param src
* @return
*/
private static String toUpperFirstCase(String src) {
char[] chars = src.toCharArray();
if (chars[0] >= 'a' && chars[0] <= 'z') {
chars[0] -= 32;
}
return String.valueOf(chars);
}
}
使用自定义 * 类
创建接口
public interface IUser {
void shopping();
Double expenses();
}
创建被代理接口
public class User implements IUser {
@Override
public void shopping() {
System.out.println("user shopping....");
}
@Override
public Double expenses() {
return 50.5;
}
}
创建代理接口
public class UseProxy implements MyInvocationHandler {
private Object target;
public Object myJDKProxy(Object target){
this.target = target;
Class<?> clazz = target.getClass();
return MyProxy.newProxyInstance(new MyClassLoader(),clazz.getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理user,执行shopping()开始...");
Object result = method.invoke(this.target, args);
System.out.println("代理user,执行shopping()结束...");
return result;
}
}
客户端调用
public static void main(String[] args) {
UseProxy useProxy = new UseProxy();
IUser user = (IUser) useProxy.myJDKProxy(new User());
user.shopping();
System.out.println(user.expenses());
}
执行结果
代理user,执行shopping()开始...
user shopping....
代理user,执行shopping()结束...
--------------------------------
代理user,执行shopping()开始...
代理user,执行shopping()结束...
--------------------------------
50.5
生成源代码
查看生产的Java文件源代码
package cn.ybzy.demo.proxy.proxy;
import cn.ybzy.demo.proxy.client.IUser;
import java.lang.reflect.*;
public class $Proxy0 implements cn.ybzy.demo.proxy.client.IUser{
MyInvocationHandler invocationHandler;
public $Proxy0(MyInvocationHandler invocationHandler) {
this.invocationHandler = invocationHandler;}
public java.lang.Double expenses() {
try{
Method m = cn.ybzy.demo.proxy.client.IUser.class.getMethod("expenses",new Class[]{});
return ((java.lang.Double)this.invocationHandler.invoke(this,m,new Object[]{})).doubleValue();
}catch(Error ex) { }catch(Throwable e){
throw new UndeclaredThrowableException(e);
}return 0.0;}public void shopping() {
try{
Method m = cn.ybzy.demo.proxy.client.IUser.class.getMethod("shopping",new Class[]{});
this.invocationHandler.invoke(this,m,new Object[]{});
}catch(Error ex) { }catch(Throwable e){
throw new UndeclaredThrowableException(e);
}}}
来源:https://juejin.cn/post/7140519841558954021


猜你喜欢
- 前言数据驱动测试是相同的测试脚本使用不同的测试数据执行,测试数据和测试行为完全分离。数据驱动是做自动化测试中很重要的一部分,数据源的方案也是
- Sentinel数据双向同步上面实现了Nacos单向同步配置规则到Sentinel,但是只是单向的,没有实现Sentinel向Nacos同步
- 显示当前运行java代码的运行时的各种参数。不带显String操作。package systeminfo;import java.util.
- 发布:一个对象是使它能够被当前范围之外的代码所引用:常见形式:将对象的的引用存储到公共静态域;非私有方法中返回引用;发布内部类实例,包含引用
- 目录1、什么是Java的内存模型2、为什么需要Java内存模型3、Java内存模型及操作规范4、Java内存模型规定的原子操作5、Java内
- 排列组合的概念排列:从n个不同元素中取出m(m≤n)个元素,按照一定的顺序排成一列,叫做从n个元素中取出m个元素的一个排列(Arrangem
- 简介使用 SpringBoot 配置 FTP 服务器,上传、删除、下载文件。配置 FTP检查是否安装 vsftpdrpm -qa | gre
- OkHttp流程图OkHttp基本使用gradle依赖implementation 'com.squareup.okhttp3:ok
- 本文实例讲述了Android开发之搜索框SearchView用法。分享给大家供大家参考,具体如下:介绍:SearchView时搜索组件,可以
- 最近在折腾一些控制相关的软件设计,想起来状态机这个东西,对解决一些控制系统状态切换还是挺有用的。状态机(有限状态自动机)网上有很多介绍。简单
- 前言:这里所说的全局Dialog是指无论当前应用是处于哪一个页面上,都能够及时弹出Dialog来提示用户一些信息,用户体验不会很好,一般应用
- RecyclerView是什么 RecycleView是Androi
- 本文实例讲述了Android开发实现popupWindow弹出窗口自定义布局与位置控制方法。分享给大家供大家参考,具体如下:布局文件:主布局
- 1、SQLite介绍SQLite,是一款轻型的数据库,是遵守的ACID关系型数据库管理系统,它包含在一个相对小的C库中。它的设计目标嵌入式是
- 结构:安装NuGet包:using SAP.Middleware.Connector;using System.Data;namespace
- 目录前言示例参考:前言按需加载对象延迟加载实际是推迟进行创建对象,直到对其调用后才进行创建初始化,延迟(懒加载)的好处是提高系统性能,避免不
- 先通过一个页面看下事情的来龙去脉,页面如下所示: 这个页面刚好一屏幕大小,所以没有滚动条,因为“保存”键上面那个项目备注是需要用户
- 一、在drawable下面添加xml文件rounded_editview.xml<?xml version="1.0&quo
- java 数据结构中栈和队列的实例详解栈和队列是两种重要的线性数据结构,都是在一个特定的范围的存储单元中的存储数据。与线性表相比,它们的插入
- 前言虽然从学java的第一个程序——helloworld至今,已经有好几个年头了。当时自己找资料,看视频,学习了java的输入输出流,多线程