当前位置: 首页 > news >正文

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&#xff08;…...

安装 GPU 版本的 tensorflow 完整版本

前言&#xff1a; 之前安装的 CPU 版本的 tensorflow 一直出问题&#xff0c;索性就直接安装 GPU 版本的 tensorflow 了&#xff08;有了GPU 就不能浪费&#xff09;。 安装过程&#xff1a; 1&#xff09;看自己有无 GPU&#xff0c;找到对应 GPU 的版本&#xff1a;任务管理…...

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 原型模式的通用写法&#xff08;浅拷贝&#xff09;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.复制粘贴 复制&#xff1a;鼠标左键沿着文本拖动高亮的文本被复制到X管理的缓冲区&#xff08;或者双击一个单词&#xff09;粘贴&#xff1a;鼠标中键 3.简单命令 时间和日期date当前月份的日历cal磁…...

大数据面试题flume篇

1.Flume 的Source&#xff0c;Sink&#xff0c;Channel 的作用&#xff1f;你们Source 是什么类型&#xff1f; 1. 作用 &#xff08;1&#xff09;Source组件是专门用来收集数据的&#xff0c;可以处理各种类型、各种格式的日志数据&#xff0c;包括 avro、thrift、exec、jm…...

零信任-深信服零信任aTrust介绍(5)

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

UVa 1343 The Rotation Game 旋转游戏 IDA* BFS 路径还原

题目链接&#xff1a;The Rotation Game 题目描述&#xff1a; 给定二十四个整数&#xff0c;这二十四个整数由八个一&#xff0c;八个二&#xff0c;八个三组成&#xff0c;从左到右&#xff0c;从上到下依次描述下图方格中的数字&#xff1a; 例如上图左边对应的输入就是[1,…...

硬件学习 软件Cadence day02 画原理图的基本操作 (键盘快捷键 , 原理图设计流程 , 从开始到导出网表流程)

1. ORCAD Capture cls 界面的快捷键 键盘 按键对应的操作I放大 &#xff08;可以滚轮操作&#xff09;O缩小 &#xff08;可以滚轮操作&#xff09;W画线Esc退出现在的状态 &#xff08;画图界面 右键 End xxx&#xff09;N放置网络标号J放置节点 (控制…...

【python】基于Socket的聊天室Python开发

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

2023想转行软件测试的看过来,你想要了解的薪资、前景、岗位方向、学习路线都讲明白了

在过去的一年中&#xff0c;软件测试行业发展迅速&#xff0c;随着数字化技术应用的广泛普及&#xff0c;业界对于软件测试的要求也在持续迭代与增加。 同样的&#xff0c;有市场就有需求&#xff0c;软件测试逐渐成为企业中不可或缺的岗位&#xff0c;作为一个高薪又需求广的…...

TortoiseSVN的使用

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

操作系统(day09) -- 连续分配管理方式

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

APISpace 带你一起走进西湖美景

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

傻白探索Chiplet,Design Space Exploration for Chiplet-Assembly-Based Processors(十三)

阅读了Design Space Exploration for Chiplet-Assembly-Based Processors这篇论文&#xff0c;是关于chiplet设计空间探索的&#xff0c;个人感觉核心贡献有两个&#xff1a;1.提出使用整数线性规划算法进行Chiplet的选择&#xff1b;2.基于RE和NRE提出了一个cost模型&#xff…...

系统分析师真题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的学习笔记

关于不同类别表达式的举例&#xff0c;请参考博文《C 中的值类别》 1. 左值和右值的简单理解 左值对应了具有内存地址的对象&#xff0c;而右值仅仅是临时使用的值对象。&#xff08;引用自博文《C 中的值类别》&#xff09;左值有名称&#xff08;变量或常量名称&#xff09…...

[数据库迁移]-MySQL常见问题

[数据库迁移]-MySQL常见问题 森格 | 2023年2月 介绍&#xff1a;记录在MySQL数据库迁移过程中遇到的问题&#xff0c;以及解决方案。 文章目录[数据库迁移]-MySQL常见问题一、背景二、常见问题2.1 ERROR 20032.2 ERROR 12732.3 ERROR 10712.4 视图权限2.5 ERROR 1062三、总结一…...

英雄联盟智能助手:5个核心功能让你的游戏体验提升300%

英雄联盟智能助手&#xff1a;5个核心功能让你的游戏体验提升300% 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power &#x1f680;. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit 你是否曾因错过对局接受而被…...

大模型动态计算:按需推理更高效

一种让大语言模型更智能地思考难题的方法 这项新技术使大语言模型能够根据问题的难度&#xff0c;动态调整用于推理的计算量。 为了使大语言模型在回答较难问题时更加准确&#xff0c;研究人员可以让模型花费更多时间来思考潜在解决方案。但是&#xff0c;赋予大语言模型这种能…...

正点原子阿尔法开发板uboot编译避坑指南:从源码到SD卡启动的完整流程

正点原子阿尔法开发板uboot编译全流程实战&#xff1a;从环境搭建到SD卡启动的深度解析 第一次接触正点原子阿尔法开发板时&#xff0c;最令人头疼的莫过于uboot的编译和烧录过程。那些看似简单的命令背后&#xff0c;隐藏着无数新手容易踩中的"暗坑"——从文件格式的…...

别再重复画框了!用AutoCAD 2019/2023的DWG to PDF,一次搞定所有非标图纸尺寸

高效图纸输出&#xff1a;AutoCAD自定义纸张尺寸的终极管理方案 在工程设计领域&#xff0c;图纸输出是工作流程中不可或缺的一环。无论是建筑平面图、机械零件图还是电气布线图&#xff0c;专业设计人员经常面临各种非标准尺寸图纸的输出需求。传统解决方案往往需要反复创建相…...

强力掌控电脑散热:FanControl让你告别风扇噪音与高温烦恼

强力掌控电脑散热&#xff1a;FanControl让你告别风扇噪音与高温烦恼 【免费下载链接】FanControl.Releases This is the release repository for Fan Control, a highly customizable fan controlling software for Windows. 项目地址: https://gitcode.com/GitHub_Trending…...

国光黑苹果教程:OpenCore完整安装指南,快速打造完美macOS系统

国光黑苹果教程&#xff1a;OpenCore完整安装指南&#xff0c;快速打造完美macOS系统 【免费下载链接】Hackintosh 国光的黑苹果安装教程&#xff1a;手把手教你配置 OpenCore 项目地址: https://gitcode.com/gh_mirrors/hac/Hackintosh 国光黑苹果教程是一个专为新手设…...

用STM32F103C8点亮你的第一块LED点阵屏:HUB08接口F3.75单元板保姆级驱动教程

从零玩转STM32驱动LED点阵屏&#xff1a;HUB08接口实战指南 第一次拿到LED点阵屏时&#xff0c;看着密密麻麻的引脚和陌生的HUB08接口&#xff0c;确实容易让人望而生畏。但别担心&#xff0c;本文将带你用最常见的STM32F103C8T6开发板&#xff0c;一步步点亮这块32x64双色点阵…...

BUUCTF实战:从内存镜像到加密容器——[VN2020]内存取证全流程解析

1. 初识内存取证&#xff1a;从CTF赛题到实战思维 第一次接触内存取证是在去年的BUUCTF比赛中&#xff0c;当时看到那道[V&N2020]公开赛题目完全摸不着头脑。现在回想起来&#xff0c;内存取证就像是在案发现场收集指纹——只不过我们面对的是计算机运行时留下的"数字…...

AI架构绘图副驾驶:用自然语言生成专业Excalidraw架构图

1. 项目概述与核心价值 如果你和我一样&#xff0c;每天都要面对陌生的、动辄几十个微服务的复杂代码库&#xff0c;或者需要向团队解释一个新系统的设计&#xff0c;那你一定理解那种“认知过载”的痛苦。在脑海里构建整个系统的架构图&#xff0c;试图理清服务间的调用关系和…...

Video2X:用AI魔法让老旧视频重获新生

Video2X&#xff1a;用AI魔法让老旧视频重获新生 【免费下载链接】video2x A machine learning-based video super resolution and frame interpolation framework. Est. Hack the Valley II, 2018. 项目地址: https://gitcode.com/GitHub_Trending/vi/video2x 你是否曾…...