Jdk19 动态编译 Java源码为 Class 文件
动态编译 Java 源码为 Class
- 一.背景
- 1.Jdk 版本
- 2.需求
- 二.Java 源码动态编译实现
- 1.Maven 依赖
- 2.源码包装类
- 3.Java 文件对象封装类
- 4.文件管理器封装类
- 5.类加载器
- 6.类编译器
- 三.动态编译测试
- 1.普通测试类
- 2.接口实现类
- 3.测试
- 四.用动态编译 Class 替换 SpringBoot 的 Bean(未完)
一.背景
1.Jdk 版本
版本查看命令:java -version
2.需求
本来想看下项目热部署的实现,比如 SpringBoot 不停机热加载 Jar 实现功能修改;后来看到 Jdk 支持源码动态编译,如果可以实现,那么就可以在线直接修改代码,再利用 SpringBoot 管理起来,替换旧的 Bean,实现功能修改。可能实际应用场景不多,可以做应急修改,线上服务最终还是需要把修改后的代码重新部署更为稳妥。
其实动态修改代码还可以通过 Arthas 实现,包括反编译、编译等更多功能
二.Java 源码动态编译实现
源码编译需要用到的关键类:
类 | 说明 |
---|---|
JavaCompiler | 编译器 ToolProvider.getSystemJavaCompiler(); |
SimpleJavaFileObject | 文件对象类,可以表示源码、类文件 |
ClassLoader | 顶层类加载器,抽象类 |
ForwardingJavaFileManager | 文件管理器 |
项目结构如图
1.Maven 依赖
暂时只是一个 Maven 项目,未引入其他依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>DynamicDemo</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>19</maven.compiler.source><maven.compiler.target>19</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties></project>
2.源码包装类
基于 SimpleJavaFileObject 扩展,用于封装类名、源码信息
package org.example.demo.util;import javax.tools.SimpleJavaFileObject;
import java.io.IOException;
import java.net.URI;/*** @author moon* @date 2023-02-15 20:32* @since 1.8*/
public class CustomSourceCode extends SimpleJavaFileObject {/*** 类名称*/private String className;/*** 类源码*/private String contents;/*** 源码初始化* @param className* @param contents*/public CustomSourceCode(String className, String contents) {super(URI.create("string:///" + className.replace('.', '/')+ Kind.SOURCE.extension), Kind.SOURCE);this.contents = contents;this.className = className;}/*** 获取类名* @return*/public String getClassName() {return className;}/*** 源码字符序列* @param ignoreEncodingErrors ignore encoding errors if true* @return* @throws IOException*/public CharSequence getCharContent(boolean ignoreEncodingErrors)throws IOException {return contents;}
}
3.Java 文件对象封装类
基于 SimpleJavaFileObject 实现,封装了类名、类字节输出流信息
package org.example.demo.util;import javax.tools.SimpleJavaFileObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;/*** @author moon* @date 2023-02-15 20:52* @since 1.8*/
public class CustomJavaFileObject extends SimpleJavaFileObject {/*** 类名称*/private String className;/*** 输出的字节码流*/private ByteArrayOutputStream toByteArray = new ByteArrayOutputStream();/*** Construct a SimpleJavaFileObject of the given kind and with the* given URI.** @param className*/public CustomJavaFileObject(String className) throws URISyntaxException {super(new URI(className), Kind.CLASS);this.className = className;}@Overridepublic OutputStream openOutputStream() throws IOException {return toByteArray;}/*** 获取类名* @return*/public String getClassName() {return className;}/*** 获取字节信息* @return*/public byte[] getByteCode() {return toByteArray.toByteArray();}
}
4.文件管理器封装类
package org.example.demo.util;import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;/*** @author moon* @date 2023-02-15 20:00* @since 1.8*/
public class CustomJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {/*** 自定义类加载器*/private CustomClassLoader loader;/*** 初始化* @param fileManager* @param loader*/protected CustomJavaFileManager(JavaFileManager fileManager, CustomClassLoader loader) {super(fileManager);this.loader = loader;}@Overridepublic JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className,JavaFileObject.Kind kind, FileObject sibling) {try {CustomJavaFileObject innerClass = new CustomJavaFileObject(className);loader.addJavaCode(innerClass);return innerClass;} catch (Exception e) {throw new RuntimeException("exception when creating in-memory output stream for " + className, e);}}@Overridepublic ClassLoader getClassLoader(JavaFileManager.Location location) {return loader;}}
5.类加载器
用于从 CustomJavaFileObject 获取字节流,并通过 ClassLoader.defineClass 生成类
package org.example.demo.util;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** @author moon* @date 2023-02-15 20:50* @since 1.8*/
public class CustomClassLoader extends ClassLoader{/*** 缓存源代码对象*/private Map<String, CustomJavaFileObject> fileCacheMap = new ConcurrentHashMap<>(16);/*** 初始化类加载器* @param parent*/public CustomClassLoader(ClassLoader parent) {super(parent);}/*** 添加源码缓存* @param obj*/public void addJavaCode(CustomJavaFileObject obj) {fileCacheMap.put(obj.getName(), obj);}/*** 获取类* @param className* The <a href="#binary-name">binary name</a> of the class** @return* @throws ClassNotFoundException*/@Overrideprotected Class<?> findClass(String className) throws ClassNotFoundException {if (fileCacheMap.containsKey(className)){byte[] byteCode = fileCacheMap.get(className).getByteCode();return defineClass(className, byteCode, 0, byteCode.length);} else {return super.findClass(className);}}
}
6.类编译器
简要说明一下调用流程:读取源码 -> 编译 -> 加载为 Class => 构建对象及使用
package org.example.demo.util;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** @author moon* @date 2023-02-15 20:50* @since 1.8*/
public class CustomClassLoader extends ClassLoader{/*** 缓存源代码对象*/private Map<String, CustomJavaFileObject> fileCacheMap = new ConcurrentHashMap<>(16);/*** 初始化类加载器* @param parent*/public CustomClassLoader(ClassLoader parent) {super(parent);}/*** 添加源码缓存* @param obj*/public void addJavaCode(CustomJavaFileObject obj) {fileCacheMap.put(obj.getName(), obj);}/*** 获取类* @param className* The <a href="#binary-name">binary name</a> of the class** @return* @throws ClassNotFoundException*/@Overrideprotected Class<?> findClass(String className) throws ClassNotFoundException {if (fileCacheMap.containsKey(className)){byte[] byteCode = fileCacheMap.get(className).getByteCode();return defineClass(className, byteCode, 0, byteCode.length);} else {return super.findClass(className);}}
}
三.动态编译测试
1.普通测试类
定义一个普通测试类,包含:有、无参构造初始化,有、无参方法调用
用户积分器
package org.example.demo.common;public class UserSort {private String name;private int score;public UserSort (){}public UserSort (String name, int sort){this.name = name;this.score = sort;}public void reset(){this.score = 0;System.out.println("姓名: " + this.name + " 积分重置: " + this.score);}public void insert(int score){this.score += score;System.out.println("姓名: " + this.name + " 加分结果: " + this.score);}public void reduce(int score){this.score -= score;System.out.println("姓名: " + this.name + " 减分结果: " + this.score);}
}
封装一个静态方法:
public static void commonClass(CustomClassCompiler compiler) throws Exception{String sourceCode = "package org.example.demo.common;\n" +"\n" +"public class UserSort {\n" +"\n" +" private String name;\n" +" private int score;\n" +"\n" +" public UserSort (){\n" +" }\n" +"\n" +" public UserSort (String name, int sort){\n" +" this.name = name;\n" +" this.score = sort;\n" +" }\n" +"\n" +" public void reset(){\n" +" this.score = 0;\n" +" System.out.println(\"姓名: \" + this.name + \" 积分重置: \" + this.score);\n" +" }\n" +"\n" +" public void insert(int score){\n" +" this.score += score;\n" +" System.out.println(\"姓名: \" + this.name + \" 加分结果: \" + this.score);\n" +" }\n" +"\n" +" public void reduce(int score){\n" +" this.score -= score;\n" +" System.out.println(\"姓名: \" + this.name + \" 减分结果: \" + this.score);\n" +" }\n" +"}";String className = "org.example.demo.common.UserSort";compiler.addSource(className, sourceCode);compiler.compile(className);Class<?> clazz = compiler.getClassByName(className);System.out.println("无参构造及重置分数-----------------------");Object object = clazz.getDeclaredConstructor().newInstance();Method method = clazz.getDeclaredMethod("reset");method.invoke(object);System.out.println("有参构造及重置分数-----------------------");object = clazz.getDeclaredConstructor(String.class,int.class).newInstance("张三",0);method.invoke(object);System.out.println("加分-----------------------------------");method = clazz.getDeclaredMethod("insert",int.class);method.invoke(object,10);System.out.println("减分-----------------------------------");method = clazz.getDeclaredMethod("reduce",int.class);method.invoke(object,2);}
2.接口实现类
定义一个处理器接口,用于处理数据
package org.example.demo.handler;/*** @author moon* @date 2023-02-15 20:55* @since 1.8*/
public interface BaseHandler {/*** 处理器* @param content*/void deal(String content);
}
封装一个静态方法,实现类不再单独贴出,简单加了个打印,输出【春江花月夜】
public static void interfaceClass(CustomClassCompiler compiler) throws Exception {String sourceCode = "package com.demo.handler;\n" +"import org.example.demo.handler.BaseHandler;\n" +"public class DynamicHandler implements BaseHandler {\n" +" \n" +" @Override\n" +" public void deal(String content) {\n" +" System.out.println(content);\n" +" }\n" +"}";String className = "com.demo.handler.DynamicHandler";compiler.addSource(className, sourceCode);compiler.compile(className);BaseHandler handler = (BaseHandler) compiler.getClassByName(className).getDeclaredConstructor().newInstance();handler.deal("春江花月夜");}
3.测试
在 App 类内直接定义一个 main 方法,调用上面两个静态方法
package org.example.demo;import org.example.demo.handler.BaseHandler;
import org.example.demo.util.CustomClassCompiler;import java.lang.reflect.Method;/*** @author moon* @date 2023-02-15 20:42* @since 1.8*/
public class App {public static void main(String[] args) throws Exception {CustomClassCompiler compiler = CustomClassCompiler.newInstance(null);commonClass(compiler);System.out.println("\n--------------------------------------------------\n");interfaceClass(compiler);}//TODO 静态方法 commonClass//TODO 静态方法 interfaceClass}
调用效果如下:
四.用动态编译 Class 替换 SpringBoot 的 Bean(未完)
未完待续 . . .
相关文章:

Jdk19 动态编译 Java源码为 Class 文件
动态编译 Java 源码为 Class一.背景1.Jdk 版本2.需求二.Java 源码动态编译实现1.Maven 依赖2.源码包装类3.Java 文件对象封装类4.文件管理器封装类5.类加载器6.类编译器三.动态编译测试1.普通测试类2.接口实现类3.测试四.用动态编译 Class 替换 SpringBoot 的 Bean(…...

安装 GPU 版本的 tensorflow 完整版本
前言: 之前安装的 CPU 版本的 tensorflow 一直出问题,索性就直接安装 GPU 版本的 tensorflow 了(有了GPU 就不能浪费)。 安装过程: 1)看自己有无 GPU,找到对应 GPU 的版本:任务管理…...
BOM编程-设置地址栏上的URL
<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>设置地址栏上的URL</title> </head> <body> <script> function go(){ // 获…...

设计模式之原型模式与建造者模式详解和应用
目录1 原型模式1.1 原型模式定义1.2 原型模式的应用场景1.3 原型模式的通用写法(浅拷贝)1.4 使用序列化实现深度克隆1.5 克隆破坏单例模式1.6 原型模式在源码中的应用1.7 原型模式的优缺点1.8 总结2 建造者模式2.1 建造者模式定义2.2 建造者模式的应用场…...

C语言(函数和递归)
函数是完成特定任务的独立程序代码单元。 目录 一.函数 1.创建一个简单的函数 2.定义带形式参数的函数 3.使用return从函数中返回值 二.递归 一.函数 1.创建一个简单的函数 #include <stdio.h> void print(void); //函数原型 int main(){ print(); //函…...
快乐的shell命令行
快乐的shell命令行 PART1——基础 1.权限 #超级用户权限$普通用户 2.复制粘贴 复制:鼠标左键沿着文本拖动高亮的文本被复制到X管理的缓冲区(或者双击一个单词)粘贴:鼠标中键 3.简单命令 时间和日期date当前月份的日历cal磁…...

大数据面试题flume篇
1.Flume 的Source,Sink,Channel 的作用?你们Source 是什么类型? 1. 作用 (1)Source组件是专门用来收集数据的,可以处理各种类型、各种格式的日志数据,包括 avro、thrift、exec、jm…...

零信任-深信服零信任aTrust介绍(5)
深信服零信任aTrust介绍 深信服是国内领先的互联网信任服务提供商,也是国内首家通过认证的全球信任服务商。深信服零信任是其中一项核心的信任技术,主要针对身份认证、数字签名、数字证书等方面的信任问题。 深信服零信任提供了一种新的安全保护模式…...

UVa 1343 The Rotation Game 旋转游戏 IDA* BFS 路径还原
题目链接:The Rotation Game 题目描述: 给定二十四个整数,这二十四个整数由八个一,八个二,八个三组成,从左到右,从上到下依次描述下图方格中的数字: 例如上图左边对应的输入就是[1,…...

硬件学习 软件Cadence day02 画原理图的基本操作 (键盘快捷键 , 原理图设计流程 , 从开始到导出网表流程)
1. ORCAD Capture cls 界面的快捷键 键盘 按键对应的操作I放大 (可以滚轮操作)O缩小 (可以滚轮操作)W画线Esc退出现在的状态 (画图界面 右键 End xxx)N放置网络标号J放置节点 (控制…...

【python】基于Socket的聊天室Python开发
基于Socket的聊天室Python开发一、Socket简述二、创建服务端Server2.1 创建服务端初始化2.2 监听客户端连接2.3 处理客户端消息三、创建客户端Client3.1 创建服务端初始化3.2 发送消息3.3 接收消息3.3 线程工作3.4 线程工作是不是挺好玩的呢?也可以作为课程设计哦&a…...

2023想转行软件测试的看过来,你想要了解的薪资、前景、岗位方向、学习路线都讲明白了
在过去的一年中,软件测试行业发展迅速,随着数字化技术应用的广泛普及,业界对于软件测试的要求也在持续迭代与增加。 同样的,有市场就有需求,软件测试逐渐成为企业中不可或缺的岗位,作为一个高薪又需求广的…...

TortoiseSVN的使用
基本概念 版本库 SVN保持数据的地方,所有的文件都保存在这个库中,Tortoise访问的就是远程服务器上的Subversion版本库。 工作拷贝 就是工作副本,可将版本库的文件拷贝到本地中,可以任意修改, 不会影响版本库。在你…...

操作系统(day09) -- 连续分配管理方式
连续分配管理方式 单元连续分配 动态分区分配 1.系统要用什么样的数据结构记录内存的使用情况? 两种常用的数据结构 空闲分区表 每个空闲分区对应一个表项。表项中包含分区号、分区大小、分区起始地址等信息空闲分区链 每个分区的起始部分和末尾部分分别设置前向…...

APISpace 带你一起走进西湖美景
俗话说:“上有天堂,下有苏杭”。 “欲把西湖比西子,浓妆艳抹总相宜” 今天我就带大家走进杭州的西湖美景。自古以来,文人歌者面对西湖美景留下千古绝句,还以西湖为背景书写了一段段动人的爱情传说。 天生自带浪漫色…...

傻白探索Chiplet,Design Space Exploration for Chiplet-Assembly-Based Processors(十三)
阅读了Design Space Exploration for Chiplet-Assembly-Based Processors这篇论文,是关于chiplet设计空间探索的,个人感觉核心贡献有两个:1.提出使用整数线性规划算法进行Chiplet的选择;2.基于RE和NRE提出了一个cost模型ÿ…...
系统分析师真题2020试卷相关概念一
对象系统测试的基本概念: 面向对象系统的单元测试包括方法层次的测试、类层次的测试和类树层次的测试。方法层次的测试类似于传统软件测试中对单个函数的测试; 测试技术: 方法层次的测试,单个函数的测试;常用的技术:等价类划分测试、组合功能测试、递归函数的测试和多态…...
20230215_数据库过程_渠道业务计算过程
—20221209 渠道产能 —自有人员工号表 shzc.xc_qdcn_pgtx_opertype —select * from shzc.xc_qdcn_pgtx_opertype for update ; —渠道基础目录 shzc.xc_qdcn_pgtx_qdtype —select * from shzc.xc_qdcn_pgtx_qdtype for update ; SQL_STRING:‘update shzc.xc_qdcn_pgtx_q…...

【C++】Expression的学习笔记
关于不同类别表达式的举例,请参考博文《C 中的值类别》 1. 左值和右值的简单理解 左值对应了具有内存地址的对象,而右值仅仅是临时使用的值对象。(引用自博文《C 中的值类别》)左值有名称(变量或常量名称)…...

[数据库迁移]-MySQL常见问题
[数据库迁移]-MySQL常见问题 森格 | 2023年2月 介绍:记录在MySQL数据库迁移过程中遇到的问题,以及解决方案。 文章目录[数据库迁移]-MySQL常见问题一、背景二、常见问题2.1 ERROR 20032.2 ERROR 12732.3 ERROR 10712.4 视图权限2.5 ERROR 1062三、总结一…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...

Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...

Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...

SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...