玩转 Java 动态编译,太秀了~!

Java动态编译执行 在某些情况下,我们可能需要动态生成java代码,通过动态编译,然后执行代码。JAVA API提供了相应的工具(JavaCompiler)来实现动态编译。下面我们通过一个简单的例子介绍,如何通过JavaCompiler实现java代码动态编译。 阅读详情

点击上方“芋道源码”,选择“设为星标”

管她前浪,还是后浪?

能浪的浪,才是好浪!

每天 8:55 更新文章,每天掉亿点点头发...

源码精品专栏

 

来源:zhenbianshu.github.io


背景

问题

之前的文章 从 Spring 的环境到 Spring Cloud 的配置 中提到过我们在使用 Spring Cloud 进行动态化配置,它的实现步骤是先将动态配置通过 @Value 注入到一个动态配置 Bean,并将这个 Bean 用注解标记为 @RefreshScope,在配置变更后,这些动态配置 Bean 会被统一销毁,之后 Spring Cloud 的 ContextRefresher 会将变更后的配置作为一个新的 Spring Environment 加载进 ApplicationContext,由于 Scoped Bean 都是 Lazy Init 的,它们会在下一次使用时被使用新的 Environment 重新创建。

这套动态配置加载流程在使我们服务更加灵活的同时,也带来了很大的风险。首先从业务上,修改配置不像上线这么”重量级”,不必要找 QA 进行回归测试,这就有可能引发一系列奇怪的 Bug,而且长时间发现不了,另外,Spring Cloud 本身没有 “fallback” 机制,一旦配置的数据类型出了问题,就会导致服务不可用。为此,我给 Spring Cloud 提了个 issue scope refreshed but new properties invalid leads to application unavailable ,但作者认为变动太大,不好改也不必改。

其实我也明白这个问题的困境,每个人都得为自己要修改的配置负责,即使框架支持了 fallback,但将错误吞掉,配置修改后不生效也没什么变化可能也并不符合用户的期望。所以,尽量让用户要修改的配置正确成为了新的目标。

基于这种需求,我添加了一个动态配置的校验器,但实现里一部分代码来自 github,所以本文在总结思路的同时,也帮助我理解所有代码。

整体思路

由于框架层没法做太多事情,所以我的计划是将这些配置取出来,构造出一个独立的 Java 类,并在服务外新建一个 ApplicationContext 试图通过构造出来的 Java 类初始化一个 Spring Bean,如果这个 Spring Bean 初始化过程中报错了,说明配置是有问题的。

动态编译

通过配置构造 Java 类

首先要通过 .properties 文件构造出一个 Java 类,但问题是在配置里我们是不知道这些配置将要被怎么使用的,不知道它要被 Spring EL 如何处理,又将被转成什么类型。

这里我采用的策略是给配置添加注释,注释里使用一定的格式声明 EL 表达式和要生成的字段类型,当然这种实现有点 low,有人提议把这些信息放到配置项的 key 里,之后会再进行优化。

把各个字段解析完成后放到准备到的类模板中,就生成了一个 Config.java 类字符串,之后就要将这个字符串编译成字节码并由 Spring 加载成 Bean。

JavaCompiler

由于 Config.java 是在运行时生成的,所以编译也只能在运行时了,万幸 Java 有提供 javax.util.JavaCompiler 类进行 Java 类的动态编译,省去了”写入文件 —— 命令行编译 —— 类加载 —— 清理文件” 的复杂流程。

JavaCompiler 的典型应用示例如下:

        JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
        JavaFileManager fileManager = javaCompiler.getStandardFileManager(null, null, null);
        CompilationTask task = javaCompiler.getTask(out, fileManager, diagnosticListener, options, classes, compilationUnits);
        task.call();
        FileObject outputFile = fileManager.getFileForOutput(null, null, null, null);
        outputFile.getCharContent(true);

流程如下图:JavaCompiler 通过 JavaFileManager 管理输入和输出文件,使用时通过 getTask() 方法提交一个异步 CompilationTask 进行代码编译,代码编译时,JavaCompiler 通过 getCharContent() 从传入的 compilationUnits 获取到 .java 文件内容,把编译后的结果调用 CompiledByteCode 的 openOutputStream() 方法写到 CompiledByteCode 对象里。

委托模式

由于 JavaCompiler 的默认实现都是通过文件进行的,这不符合我的期望,我需要的是输入和输出都在内存进行,所以需要修改 JavaCompiler 的实现,JavaCompiler、JavaFileManager、JavaFileObject(Input/Output) 分别使用委托模式实现。其中 JavaFileManager 已经有 ForwardingJavaFileManager 的实现,JavaFileObject 也有 SimpleJavaFileObject 的实现,我们继承其实现后重写部分方法即可。

我参考的源码:GitHub-trung/InMemoryJavaCompiler

Spring Bean 实例化

要将 Config 类实例化成 Bean,我们可以在 xml 里预定义它,在编译结束后创建一个简易的 FileSystemXmlApplicationContext 实例化这个 xml 内的 Bean。

类加载器

首先要让 Spring 能够加载到这些编译好的字节码,这就需要 ClassLoader 的配合。类加载器的默认实现不可能知道去加载我们内存里编译好的字节码,只好新加一个 ClassLoader,实现也很简单,继承 ClassLoader 抽象类,并实现 findClass 方法即可。

class MemoryClassLoader extends ClassLoader {
 @Override
 protected Class<?> findClass(String name) throws ClassNotFoundException {
     // 在 CompiledByteCode 类里将编译后的字节码放到 classLoader 的 classBytes 字段内。
  byte[] buf = classBytes.get(name);
  if (buf == null) {
   return super.findClass(name);
  }
  return defineClass(name, buf, 0, buf.length);
 }
}
配置和实现

由于 Config Bean 的初始化依赖动态配置,我们还要把这些配置也添加到 Spring 环境内,我们知道 Spring 环境配置是由多个 PropertySource 构成的,向里面添加一个实现即可。然后就可以调用 application 的 refresh() 方法初始化上下文了,另外 Config Bean 被设置为懒加载了,不要忘记 get 一下使其被创建。

最终的代码如下:

        FileSystemXmlApplicationContext applicationContext = new FileSystemXmlApplicationContext();
        applicationContext.setClassLoader(memoryClassLoader);
        applicationContext.setConfigLocation("classpath*:/test.xml");
        Map<String, Object> propertyMap = buildDynamicPropertyMap();
        MapPropertySource mapPropertySource = new MapPropertySource("validate_source", propertyMap);
        applicationContext.getEnvironment().getPropertySources().addFirst(mapPropertySource);
        applicationContext.refresh();
        applicationContext.getBean("config");

小结

小项目完成的过程中,复习了很多知识,也尝试了业务代码中几乎不会用到的设计模式,充满了挑战性。

当然它现在还有配置不够方便、错误提示不够明确、没解决配置 namespace 等问题,留到后面慢慢优化吧~

- END -

欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢:

已在知识星球更新源码解析如下:

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 20 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
【java】深入理解Java的动态编译 文章目录1.概述2. 前提3. 基本原理#4. JDK动态编译4.1 实现JavaFileObject4.2 实现ClassLoader4.3 实现JavaFileManager4.4 动态编译和运行5.Javassist动态编译5.2 进阶例子小结 1.概述 转载:深入理解Java的动态编译 2. 前提 笔者很久之前就有个想法:参考现有的主流ORM框架的设计,造一个ORM轮子,在基本不改变使用体验的前提下把框架依赖的大量的反射设计去掉,这些反射API构筑的组件使用动态编译加载的实例去替代,从而可以得到接近 阅读详情

相关推荐

从BIOS到路由器:深入拆解SPI NOR Flash的硬件连接与‘芯片内执行’(XIP)奥秘

本文深入解析SPI NOR Flash的硬件连接与‘芯片内执行’(XIP)技术,从BIOS到路由器的应用场景出发,详细介绍了其架构精髓、典型连接方案、时序设计及性能优化方法。通过实际案例和参数对比,帮助开发者掌握NOR Flash在嵌入式系统中的关键作用与设计技巧,提升电路设计效率与可靠性。

weixin_30522183的博客 492

一文学会 Java 动态编译

Java动态编译技术可以让我们在程序运行时动态地生成和加载Java代码,从而实现更加灵活和高效的应用程序。本文介绍了Java动态编译的基本概念、实现方法和相关案例。如果你对Java动态编译技术感兴趣,可以通过阅读的官方文档,进一步了解该技术的实现细节。同时,需要注意的是,Java动态编译技术虽然可以提供更高的灵活性和可扩展性,但在实际使用时也存在一些安全隐患。因为动态编译可以在程序运行时生成和加载代码,如果不加以限制和控制,可能会导致恶意代码的注入和执行。

Java是世界上最好的语言 3433

AES128加密算法开发(含源码)

本文详解了AES128加密算法的原理与C语言实现。AES128是当前广泛使用的对称加密标准,采用128位密钥和10轮变换,具有高安全性和高效性。文章从算法背景、核心概念(混淆与扩散)入手,详细解析了4×4状态矩阵表示、4个核心变换(字节替换、行移位、列混合、轮密钥加)以及密钥扩展过程。特别针对汽车电子应用场景,如ECU通信加密、车钥匙认证等,说明了AES128在资源受限环境中的优势。通过分步图解和原理剖析,帮助开发者深入理解这一加密标准的技术细节与实现方法。

汽车电子软件开发笔记 2918

编译class文件遇坑

在测试编译class文件的方法的时候,在这个类里面创建了一个main方法进行测试是可以的。但是如果去其他的类里面去调取这个方法就会报错。找不到自定义代码里面的类符号。原本我的配置是jar启动。配置成红框内的两个任意一个就好了。none没有试,不做评价了。我改的是classpath。

mannery19140的博客 331

[基于STM32底盘控制与ROS上层导航小车制作] 第一节 stm32电机驱动与编码器读取反馈

系列文章目录 第一节stm32电机驱动与编码器读取反馈 第二节 stm32电机pid控制 第三节 stm32线速度标定 第四节 stm32添加mpu6050得到angle角度 第五节 实现STM32与ubuntu系统下的ROS串口DMA通信,传输底盘速度等信息 第六节 ROS计算和发布里程计 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 系列文章目录 前言 1.pwm与电机正反转 2.编码器初始化 二、pwm控制与编码器使用 1.pwm使电机 .

LanLanLan1927的博客 1万+

ThinkBook 16 2024 Ubuntu驱动全攻略:从无线网卡到触控板

本文详细介绍了ThinkBook 16 2024在Ubuntu系统下的驱动问题解决方案,涵盖无线网卡、蓝牙、显卡和触控板等关键硬件的驱动修复步骤。通过具体命令和配置指南,帮助用户快速解决兼容性问题,提升Ubuntu在ThinkBook上的使用体验。

weixin_29200741的博客 829

java 动态编译_Java动态编译

概述:Java的动态编译就是在运行期直接编译.java文件,执行.class,并且能够获得相关的输入输出,甚至还能监听相关的事件。步骤:1、创建或自动生成.java文件2、调用JavaCompiler获取编译器,该类允许开发人员编译java文件为class文件JavaCompiler compiler =ToolProvider.getSystemJavaCompiler();3、获取文件管理器S...

weixin_35980160的博客 2608

0.嵌入式控制器EC实战 Embedded Controller开发概述

文章目录前言1.嵌入式控制器EC概述2.EC芯片框图(IT8502为例)3.General Purpose I/O Port (GPIO)4.SMBus(System Management Bus,系统管理总线)5.PWM、TACH6.KBC(KeyBoard Controller)7.PS/28.ADC(模数转换)总结 前言 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。 1.嵌入式控制器EC概述 嵌入式控制器EC是挂在CPU的L

qq_30135687的博客 1万+

【Java核心-进阶】运行时动态生成Java类

. 技术上可行的方式 生成源码文件 -> 编译源码 -> 加载类 方式1:javac编译 可以用 ProcessBuilder 这个类启动 javac 进程,编译源码文件 方式2:Java Compiler API 编译 JDK中的 Java Compiler API 提供了与 javac 对等的编译能力。(示例:InMemoryJavaCompiler) ...

一个被IT搞的 968

PyTorch实战(30)——使用TorchScript和ONNX导出通用PyTorch模型

在本节中,我们将深入探讨使用 TorchScript 导出 PyTorch 模型。通过序列化,TorchScript 使模型与 Python 生态系统独立,从而使得模型可以在其他环境中加载,例如基于 C++ 的环境。我们还跨越 Torch 框架与 Python 生态的边界,研究机器学习通用开放格式 ONNX,该技术能帮助我们将 PyTorch 训练的模型导出至非 PyTorch 甚至非 Python 环境。

盼小辉丶的博客 3532

AFSim2.9.0学习笔记 —— 1、AFSim及完整工具介绍(文末附:完整afsim2.9.0源码、编译好的完整工具包、中文教材等)

AFSim2.9.0学习笔记 —— 1、AFSim及完整工具介绍(附:完整afsim2.9.0源码、编译好的完整工具包、中文教材等)

5183

玩转Java动态编译,太秀了~!

问题之前的文章从Spring 的环境到 Spring Cloud 的配置中提到过,我们在使用 Spring Cloud 进行动态化配置,它的实现步骤是先将动态配置通过@Value注入到...

hnjsjsac的博客 127

手把手教你学基于 Linux 的 NPU 固件开发-第 10 讲:可靠性设计(RAS)——让系统坚如磐石错误检测、纠正与上报机制(ECC、Parity)

组件推荐保护机制备注HBM/DDRScrubbing必须开启,定期巡检片上 SRAMECC(权重/激活值),Parity(指令)权衡面积与可靠性计算单元ABFT或DMR(关键路径)防止计算逻辑错误片上网络CRC重传保证数据传输完整性系统软件AER 上报CE 监控自动恢复闭环管理,杜绝 SDC运维策略预测性维护Checkpoint变被动为主动核心心法(信任硬件,但必须验证。在纳米尺度下,错误是必然发生的物理现象。

MHD0815的博客 91

玩转 Java 动态编译,秀了秀了~!

来源:https://zhenbianshu.github.io 问题 之前的文章从Spring 的环境到 Spring Cloud 的配置中提到过,我们在使用 Spring Cloud 进行动态化配置,它的实现步骤是先将动态配置通过 @Value 注入到一个动态配置 Bean,并将这个 Bean 用注解标记为 @RefreshScope,在配置变更后,这些动态配置 Bean 会被统一销毁。 之后 Spring Cloud 的 ContextRefresher 会将变更后的配置作为一个新的 Spring E

Java技术栈,分享最主流的Java技术 377

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Java动态编译

程序产生过程下图展示了从源代码到可运行程序的过程,正常情况下先编译(明文源码到字节码),后执行(JVM加载字节码,获得类模板,实例化,方法使用)。本文来探索下当程序已经开始执行,但在.class甚至.java还未就绪的情况下,程序如何获得指定的实现。这就是我们下面的主题,动态编译。 相关类介绍JavaCompiler: 负责读取源代码,编译诊断,输出class JavaFileObject: 文

文武天下 4614

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

科普文:Java基础系列之[java动态编译]

在 Java 中,动态编译是指在运行时动态地编译 Java 源代码,生成字节码,并加载到 JVM 中执行。动态编译可以用于实现动态代码生成、动态加载、插件化等功能。动态编译可能在日常工作中所使用的场景不多,但在特定的场景下能够很好的解决我们所遇到的问题,本篇文章可以给大家提供一些视野,当你遇到类似场景时或许动态编译能够很好的解决它!

为无为,事无事,味无味。 1878

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

【Java动态编译】动态编译的应用

动态编译

weixin_43760020的博客 1036

Java动态编译和运行

一、tools.jar问题: Java在进行动态编译的时候需要用到tools.jar资源包,此包在jdk\lib目录中。若tools.jar不存在则会出现进行编译时提示空指针异常: 对于缺少jar包,首先想到的解决办法是找到需要的jar包并且将其加入到buildpath,但是我们发现只是将tools.jar加入到java buildpath中仍然会出现空指针异常。后来经过查询相关资

菜菜的博客 4243

【Java动态编译】使用Java Compiler动态编译、使用示例代码

动态编译的应用场景 Java 6.0之后要引入了动态编译。 动态编译可以实现:让用户提交一段Java代码,上传到服务器,运行,实现在线评测系统。 Java 6.0之前,可以使用这种方式,达到动态编译的效果: Java 6.0之后,使用如下方式,也就是本文要讲的: 使用JavaCompiler动态编译 动态编译 动态编译生成一个class文件 编译前: 编译后: 代码示例 下列代码返回值为...

寒泉 2万+

Java之动态编译,静态编译简单理解和实例

开心一笑【年底是各种案件的高发期,我们去ATM取钱的时候,一定要注意遮挡,不要被陌生人看到你的余额,要不然啊,就,,,非常容易被人嘲笑。其实对于胖子来说,买衣服最简单了,都不用进去。直接就在门口问一句:“老板,有我能穿的衣服么”? 老板瞅了我一眼:“没有”! 那我就毫不犹豫的走到下一家。反正在胖子的眼里,衣服没有好看不好看,只有能穿和不能穿! 】提出问题java中的动态编译和静态编译如何理解???解

IT技术分享 1万+

Java中动态编译

JDK6.0推出了动态编译的新功能,能够在程序中动态的写一个类,再对之进行编译。编译成class文件后就可以通过类加载方式把动态编译的类加载到内存中。当然也能通过RunTime类调用javac命令来动态编译。   动态编译类的主要步骤:   ①、写一个字符串,这个字符串就是要编译的类的全部内容。   ②、通过输出流,把该字符串的内容写到工程下面指定的包中。   ③、对创建的java文件进行

berber78的专栏 1111

java 动态编译_Java 动态编译技术

Java实现动态创建Java文件和动态加载有两种途径1、将创建的Java字符串写入磁盘文件,然后由磁盘文件中读取再加载到虚拟机中执行2、将创建的Java字符串写入到内存,通过内存中的对象读取到虚拟机中摘自:https://www.cnblogs.com/flyoung2008/archive/2011/11/14/2249017.html一、使用JavaCompiler接口编译java源程序我们可...

weixin_35721505的博客 757

Java动态编译要慎用

动态编译一直是Java 的梦想,从Java 6 版本它开始支持动态编译了,可以在运行期直接编译.java 文件,执行.class,并且能够获得相关的输入输出,甚至还能监听相关的事件。 不过,我们最期望的还是给定一段代码,直接编译,然后运行,也就是空中编译执行(onthe-fly),来看如下代码: public class Client {     public static void m

芝麻软件工作室的专栏 1756
上一篇: 干掉cms,zgc才是未来
下一篇: 别去外包!
公众号-芋道源码
博客等级 码龄10年 1631粉丝 377原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值