博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
类的热编译+热加载的功能
阅读量:2062 次
发布时间:2019-04-29

本文共 3908 字,大约阅读时间需要 13 分钟。

类加载机制实践

系统调用一个class,比如xx.class去查看xx类的信息的时候:

1,loadClass,先在loadedClass里找,找不到,就交给父类去load试试,如果父类还是找不到(返回null),就去调用findClass试试
2,findClass去找未加载进内存的.class方法,如果找到了,就做defineClass的步骤,把它加载进内存
3,defineClass本身是一个native方法,在这个native方法里,当解析到方法的父类的时候(每个方法都有父类,要么是自定义的,要么是Object),会回调当前classLoader的loadClass方法,然后loadClass方法会重复1-3步骤,寻找到父类以后,把父类交出去。

按照我的理解,defineClass里所做的把.class文件转化成loadedClass的步骤,就包括了类加载生命周期里的加载,验证,解析步骤

其中findClass里去寻找.class文件(或者其他的二进制流的方法)属于加载的第一步,“1,通过一个类的全限定名来获取定义此类的二进制流。 ”

defineClass这个native属于加载的第二步“2,把字节流所代表的静态存储结构转化为方法区的运行时数据结构”到这里都属于类加载中的加载阶段。

在这个过程中,defineClass回调当前类的loadClass方法来寻找被加载类的父类,这个过程是验证and解析的过程,属于类加载中的连接(验证,准备,解析)阶段。这里找到父类以及其他相关的各种引用的类之后,就已经把符号引用转化为直接引用了,所以到这里递归完以后,连接阶段就结束了

也就是说,在我们看来,调用defineClass的时候,等于把类给初始化了,类的加载,连接(验证,准备,解析)阶段+初始化等,都执行了。

绕回来,代码

这里做了一个类的热编译+热加载的功能

自定义类加载器

这里分两种情况,

1,代码是main方法运行的情况下(非web应用),默认的类加载器是appClassLoader,这种情况下,我们写的.java文件会被编译成.class文件,去进行加载(可以直观的看见和替换.class文件)
2,代码是web应用运行的情况下(以spring为例),默认的类加载器是WebAppClassLoader,这里的.java文件,在编译成.class文件之后,会再被编译成.jar文件,放置在web目录下的web-inf/lib里(不能直观的看见和替换.class文件)

但是不影响我们加载类的代码书写

流程大致如下:

1,得到文件转换来的二进制流

2,把二进制流给自定义的类加载器

3,调用自定义类加载器的loadClass方法,输入对应名字,

4,复写loadClass方法,做出判断,if name == 我要加载的类, 拿出对应的二进制流,defineClass

else 交给对应的加载器去加载。

因为defineClass会回调loadClass方法,所以一定要复写loadClass方法,不然很容易就会报错,比如找不到需要被加载的父类之类的。(虽然报错了也不影响实际使用)

public class HowswapClassLoader extends ClassLoader {
private Map
files = new HashMap<>(); public HowswapClassLoader(Map
files) { this.files.putAll(files); } @Override protected Class
findClass(String name) throws ClassNotFoundException { byte[] buf = getByte(files.get(name)); if (buf == null) { return HowswapClassLoader.class.getClassLoader().loadClass(name); } files.remove(name); return defineClass(name, buf, 0, buf.length); } @Override public Class loadClass(String name) throws ClassNotFoundException { return findClass(name); } }

类编译

rootPath是临时文件夹就行了,用来存放编译后的.class文件,

比如 System.getProperty(“user.dir”) + “\” + “temp\”;

public class XX{        private static void compilerJavaFiles(List
files,String rootPath) { JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); Iterable
options = buildOptions(rootPath); StandardJavaFileManager fileManager = javaCompiler .getStandardFileManager(null, null, null); Iterable
compilationUnits = fileManager .getJavaFileObjectsFromFiles(files); javaCompiler.getTask(null, fileManager, null, options, null, compilationUnits).call(); } private static List
buildOptions(String rootPath) { List
options = new ArrayList<>(); options.add("-encoding"); options.add("UTF-8"); //鉴于很多编译文件都需要引用到一些二方三方包,(非java.xx的包),这里通过直接找urls的方式,一次性取出所有编译时候可能用到的classpath,供使用。 options.add("-classpath"); options.add(getClasspath()); //编译目录是root目录,-d表示在根目录下创建(如果没有)package对应的文件夹,然后把.class文件放到对应的package文件夹下 options.add("-d"); options.add(rootPath); return options; } private static String getClasspath() { StringBuilder sb = new StringBuilder(); URL[] urls = ((URLClassLoader)XX.class.getClassLoader()).getURLs(); int length = urls.length; for (int i = 0; i < length; ++i) { URL url = urls[i]; String p = url.getFile(); sb.append(p).append(File.pathSeparator); } return sb.toString(); } }

类的热编译+热加载的功能

1,找到类.java的存放点,
2,读取类.java文件,通过XX.compilerJavaFiles()方法,得到编译后的.class文件
3,把.class文件给HowswapClassLoader去加载进内存
4,通过HowswapClassLoader去获取类(Class

转载地址:http://jtalf.baihongyu.com/

你可能感兴趣的文章
cgroup 挂载失败是什么鬼???
查看>>
深入 kubernetes API 的源码实现
查看>>
真香!使用 Goland 网页版实现真正的云开发
查看>>
又超时了!Etcd分布式锁你用明白了吗?
查看>>
工程师应该怎么学习
查看>>
记一次 Kubernetes 机器内核问题排查
查看>>
记一次 Kubernetes 中严重的安全问题
查看>>
在业务系统中寻找技术含量
查看>>
拥抱云原生,基于 eBPF 技术实现 Serverless 节点访问 K8S Service
查看>>
有了 Docker 就不用再深入学习 MySQL 了?
查看>>
持续监控集群中的镜像漏洞
查看>>
终于可以像使用 Docker 一样丝滑地使用 Containerd 了!
查看>>
张磊大神的《深入剖析Kubernetes》终于出书啦!
查看>>
KubeSphere 团队(青云QingCloud) 全职开源职位等你加入!
查看>>
真棒!3 种方法限制 Pod 磁盘容量,瞬间豁然开朗
查看>>
高并发、高可用、高可靠微服务架构7大顶级设计思维模型
查看>>
如何使用 registry 存储的特性
查看>>
凉了,stress 无论如何也无法打满 CPU
查看>>
除了 k8s,留给 k 和 s 中间的数字不多了!
查看>>
使用 wrk 压测并精细控制并发请求量
查看>>