spring解密_在运行时更新代码(已Spring解密)

spring解密

当从编译到部署再到测试的开发周期花费太长时间时,人们希望能够及时替换正在运行的代码,而无需重新启动应用程序服务器并等待部署完成。 在这种情况下,像JRebel这样的商业解决方案或像Grails这样的开源框架就可以提供帮助。

JVM不支持开箱即用地替换代码,例如您可以使用Class.forName()动态加载类。 基本上,您有以下选择:

  • HotSwap:Java 1.4引入的技术,可让您在调试器会话中重新定义类。 这种方法非常有限,因为它仅允许您更改方法的主体,而不能添加新的方法或类。
  • OSGi:此技术使您可以定义包。 在运行时,可用此软件包的较新版本替换该软件包。
  • 一次性的类加载器:通过在模块的所有类上包装单独的类加载器,可以在模块的新版本可用时丢弃该类加载器并进行替换。
  • 使用Java代理检测类:Java代理可以在定义类之前进行检测。 这样,它可以将代码注入到已加载的类中,该类将此类与类文件的一个版本连接起来。 一旦有新版本可用,将执行新代码。

Grails的技术称为“ 弹簧加载”,并使用“ Java代理”方法来处理从文件系统而不是从jar文件加载的类。 但是,这在后台如何工作?

为了了解弹簧负载,我们建立了一个小样本项目,使我们可以更详细地研究该技术。 该项目仅包含两个类: Main类调用ToBeChanged类的print()方法,并Hibernate一会儿:

public static void main(String[] args) throws InterruptedException {while (true) {ToBeChanged toBeChanged = new ToBeChanged();toBeChanged.print();Thread.sleep(500);}
}

print()方法仅打印出一个版本,以便我们可以看到它已更改。 另外,我们还打印出堆栈跟踪,以查看其随时间的变化:

public void print() {System.out.println("V1");StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();for (StackTraceElement element : stackTrace) {System.out.println("\t" + element.getClassName() + "." + element.getMethodName() + ":" + element.getLineNumber());}
}

启动应用程序时,我们必须使用选项javaagent提供包含Java Agent的jar文件。 当弹簧加载以验证者不喜欢的方式修改字节码时,我们必须通过将选项noverify传递给JVM来禁用字节码的验证。 最后,我们用cp传递包含类文件的文件夹,并告诉JVM包含main()方法的类:

java -javaagent:springloaded-1.2.4.BUILD-SNAPSHOT.jar -noverify -cp target/classes com.martinsdeveloperworld.springloaded.Main

ToBeChanged类的版本从V1更新到V2并使用mvn package重建项目后,我们将看到以下输出:

...
V1java.lang.Thread.getStackTrace:-1com.martinsdeveloperworld.springloaded.ToBeChanged.print:7com.martinsdeveloperworld.springloaded.Main.main:8
V2java.lang.Thread.getStackTrace:-1com.martinsdeveloperworld.springloaded.ToBeChanged$$EPBF0gVl.print:7com.martinsdeveloperworld.springloaded.ToBeChanged$$DPBF0gVl.print:-1com.martinsdeveloperworld.springloaded.ToBeChanged.print:-1com.martinsdeveloperworld.springloaded.Main.main:8
...

版本V1的stacktrace看起来像我们期望的那样。 从Main.main()方法ToBeChanged.print()被调用。 对于版本V2这是不同的。 在这里,方法ToBeChanged.print现在调用方法ToBeChanged$$DPBF0gVl.print() 。 还请注意,调用ToBeChanged.print()的行号已从8更改为-1,表示该行未知。

新的行号-1强烈表明Java代理已经以一种允许它调用新方法而不是执行旧代码的方式来检测ToBeChanged.print()方法。 为了证明这一假设,我在spring-loaded的代码中添加了一些日志记录语句,并添加了一个功能,可将每个插入的文件转储到本地硬盘驱动器中。 通过这种方式,我们可以检查ToBeChanged.print()后方法ToBeChanged.print()外观:

0 getstatic #16 <com/martinsdeveloperworld/springloaded/ToBeChanged.r$type>3 ldc #72 <0>5 invokevirtual #85 <org/springsource/loaded/ReloadableType.changed>8 dup9 ifeq 42 (+33)12 iconst_113 if_icmpeq 26 (+13)16 new #87 <java/lang/NoSuchMethodError>19 dup20 ldc #89 <com.martinsdeveloperworld.springloaded.ToBeChanged.print()V>22 invokespecial #92 <java/lang/NoSuchMethodError.<init>>25 athrow26 getstatic #16 <com/martinsdeveloperworld/springloaded/ToBeChanged.r$type>29 invokevirtual #56 <org/springsource/loaded/ReloadableType.fetchLatest>32 checkcast #58 <com/martinsdeveloperworld/springloaded/ToBeChanged__I>35 aload_036 invokeinterface #94 <com/martinsdeveloperworld/springloaded/ToBeChanged__I.print> count 241 return42 pop43 getstatic #100 <java/lang/System.out>46 ldc #102 <V1>48 invokevirtual #107 <java/io/PrintStream.println>51 invokestatic #113 <java/lang/Thread.currentThread>54 invokevirtual #117 <java/lang/Thread.getStackTrace>57 astore_1
...
152 return

getstatic操作码检索新字段r$type的值并将其压入堆栈(操作码ldc )。 然后,调用方法ReloadableType.changed()来调用之前已推入堆栈的对象引用。 顾名思义,方法ReloadableType.changed()检查此类型的新版本是否存在。 如果方法未更改,则返回0;如果方法已更改,则返回1。 如果返回值为零,即方法未更改,则以下操作码ifeq跳至第42行。 从第42行开始,我们看到了原始实现,在这里我将其缩短了一点。

如果值为1,则if_icmpeq指令跳至第26行,在此再次读取静态字段r$type 。 此引用用于在其上调用方法ReloadableType.fetchLatest() 。 下面的checkcast指令验证返回的引用的类型为ToBeChanged__I 。 在这里,我们第一次偶然发现了这种弹簧接口为每种类型生成的人工接口。 它反映了原始类在检测时所具有的方法。 两行之后,此接口用于在ReloadableType.fetchLatest()返回的引用上调用方法print() ReloadableType.fetchLatest()

该引用不是对该类的新版本的引用,而是对所谓的调度程序的引用。 调度程序实现ToBeChanged__I接口,并通过以下指令实现方法print()

0 aload_1
1 invokestatic #21 <com/martinsdeveloperworld/springloaded/ToBeChanged$$EPBF0gVl.print>
4 return

动态生成的类ToBeChanged$$EPBF0gVl是所谓的执行程序,体现了该类型的新版本。 对于每个新版本,都会创建一个新的调度程序和执行程序,只有接口保持不变。 一旦有新版本可用,则在新调度程序上调用接口方法,并且在最简单的情况下,该接口方法将转发至执行程序中包含的代码的新版本。 不能直接在执行器上调用接口方法的原因是,弹簧加载还可以处理在新版本的类中添加方法的情况。 由于此方法在旧版本中不存在,因此将通用方法__execute()添加到接口和调度程序。 然后,此动态方法可以将调用调度到新方法,如从生成的调度程序中获取的以下指令集中所示:

0 aload_31 ldc #25 <newMethod()V>3 invokevirtual #31 <java/lang/String.equals>6 ifeq 18 (+12)9 aload_2
10 checkcast #33 <com/martinsdeveloperworld/springloaded/ToBeChanged>
13 invokestatic #36 <com/martinsdeveloperworld/springloaded/ToBeChanged$$EPBFaboY.newMethod>
16 aconst_null
17 areturn
18 aload_3
...
68 areturn

在这种情况下,我向类ToBeChanged添加了一个名为newMethod()的新方法。 __execute()方法的开头比较调用的描述符是否与新方法匹配。 在这种情况下,它将调用转发给新的执行者。 为了使此工作正常进行,必须将新方法的所有调用都重写为__execute()方法。 这也可以通过对原始类的检测来完成,并且也可以进行反射。

结论

Spring加载演示了可以在运行时用较新版本“替换”一个类。 为了实现这一点,利用了一系列Java技术,例如Java Agent和字节码检测。 通过仔细研究实现,可以大致了解有关JVM和Java的许多知识。

翻译自: https://www.javacodegeeks.com/2015/05/updating-code-at-runtime-spring-loaded-demystified.html

spring解密

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/337642.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

组装台式计算机需要哪些配件,组装电脑需要哪些配件 电脑装机教程图解

DIY电脑一直都是电脑爱好者非常喜欢的&#xff0c;对于电脑爱好者来说&#xff0c;通过自己动手DIY组装电脑&#xff0c;不仅可以更为深刻的认识硬件&#xff0c;了解电脑主机内部结构&#xff0c;还能够学习到安装系统等知识&#xff0c;从中可以带来很多乐趣。今天我们为大家…

C语言代码优化的实用方法大总结

点击蓝字关注我们1、选择合适的算法和数据结构选择一种合适的数据结构很重要&#xff0c;如果在一堆随机存放的数中使用了大量的插入和删除指令&#xff0c;那使用链表要快得多。数组与指针语句具有十分密切的关系&#xff0c;一般来说&#xff0c;指针比较灵活简洁&#xff0c…

怎么取消苹果手机自动续费_手机腾讯视频的vip怎么取消自动续费?

如果是安卓手机直接在腾讯视频的【个人中心】找到【我的VIP会员】即可选择【管理自动续费】取消自动续费。如果是苹果手机需要在Apple ID中找到【订阅】选项&#xff0c;取消订阅即可。以下是详细介绍&#xff1a;1、如果您是安卓手机直接点击进入【腾讯视频】的应用&#xff0…

java转换为c#_将25k行C#转换为Java的经验教训

java转换为c#由于各种原因&#xff0c;我最近完成了一个将复杂的财务应用程序从C&#xff03;转换为Java的项目。 港口的原因大部分是非技术性的&#xff0c;而是对相关企业的一项战略举措。 这是一次有趣的经历&#xff0c;我在此过程中吸取了一些经验教训&#xff0c;可以分…

计算机专业去荷兰还是瑞典,给申请计算机专业的同学几点建议

计算机专业想来是广大美国研究生申请的热门专业&#xff0c; 这里所说的热门&#xff0c;不光指招生人数多&#xff0c; 就业前景优势也是无容置疑的。广大申请者的硬件条件无外乎分为以下几种情况&#xff1a;1、托福80&#xff0c;GRE 3002、托福90 GRE3153、托福100&#xf…

为何某些公司不允许使用C++STL

点击蓝字关注我们最初开始禁用 C STL&#xff0c;是因为早期项目编码实践中留下的惯例&#xff0c;被后来的程序员继承下来。老项目中这种选择尤其地多。不过如果有人将其上升到公司行为在不同项目中全面禁用 STL&#xff0c;则没有必要&#xff0c;而且我倾向于做这种决定的人…

支持向量机python代码_Python中的支持向量机SVM的使用(有实例)

除了在Matlab中使用PRTools工具箱中的svm算法&#xff0c;Python中一样可以使用支持向量机做分类。因为Python中的sklearn库也集成了SVM算法&#xff0c;本文的运行环境是Pycharm。 一、导入sklearn算法包 skleran中集成了许多算法&#xff0c;其导入包的方式如下所示&#xff…

swarm:pending_WildFly Swarm:使用Java EE构建微服务

swarm:pending“完美无缺&#xff0c;不是在没有其他可添加的东西时&#xff0c;而是在没有其他东西要带走时实现的” Antoine de Saint-Exupery 法国作家安托万德圣艾修伯里 &#xff08; Antoine de Saint-Exupery &#xff09;的这句话是为了证实通常少花钱多。 对于建筑师…

html中引入js柱状图,Echarts.js 图表在layui中的引用

添加部分源码,可以使layui使用第三方插件的方法引入,代码以下://打开源码,将代码头部的代码(function (global, factory) {typeof exports object && typeof module ! undefined ? factory(exports) :typeof define function && define.amd ? define([expo…

C++ 23将引入标准库模块支持,编译速度提升10倍

点击蓝字关注我们虽然模块是 C 20 的主要卖点&#xff0c;但与 C 20 不同的是&#xff0c;C 23 的标准库也将实现模块化。从标准库开始&#xff0c;模块有望改善编译速度和 C 开发者组织代码的方式。C 是丹麦计算机科学家 Bjarne Stroustrup 创建的编程语言&#xff0c;由 ISO …

python刷题_11.学习Python,刷题才能让你感受到快(差)乐(距)!

猜数字游戏可用一个简单的while循环来实现。 其中 随机数 的生成在第4篇介绍过&#xff0c; 键盘输入在上一篇介绍过 import random result round(random.random()*100) while result ! guess: guess int(input()) if guess>result: print(大了!) elif guess print(小了!)…

java jigsaw_Java 9和Project Jigsaw如何破坏您的代码

java jigsawJava 9迫在眉睫&#xff0c;它将带有完整的Project Jigsaw 。 在我从OpenJFX邮件列表的最近讨论中得知它可能会破坏现有代码之前&#xff0c;我并没有对此给予太多关注。 这对于Java非常不寻常&#xff0c;因此引起了我的兴趣。 我阅读了该项目的JEP和一些相关文章…

C语言的万能“三板斧”

点击蓝字关注我们作为嵌入式工程师&#xff0c;写一个效率高效&#xff0c;思路清晰的C语言程序是我们的终极目标。那么&#xff0c;怎样才能写好这样的程序呢&#xff1f;首先&#xff0c;我们要用C语言的思维方式来进行程序的构架构建&#xff1b;其次&#xff0c;要有良好的…

武汉城市职业学院计算机分数线,武汉城市职业学院历年分数线 2021武汉城市职业学院录取分数线...

一、2019年武汉城市职业学院各省录取分数线及录取位次统计表1、武汉城市职业学院2019年在福建录取分数线如下&#xff1a;文科录取批次高职专科301分&#xff0c;最低录取位次为68896名、理科录取批次高职专科229分,最低录取位次为130149名&#xff1b;2、武汉城市职业学院2019…

aws sqs_在Spring中将AWS SQS用作JMS提供程序

aws sqs最近AWS公布了新的客户端库&#xff0c;它实现了JMS 1.1规范 &#xff0c;并使用他们的简单队列服务 &#xff08;SQS&#xff09;作为JMS提供者 &#xff08;见杰夫巴尔的帖子在这里 &#xff09;。 在我的文章中&#xff0c;我将向您展示如何设置Maven项目以使用Sprin…

openmv串口数据 串口助手_STM32 串口接收不定长数据 STM32 USART空闲检测中断

编者注&#xff1a;单片机串口接收不定长数据时&#xff0c;必须面对的一个问题为&#xff1a;怎么判断这一包数据接收完成了呢&#xff1f;常见的方法主要有以下两种&#xff1a;1.在接收数据时启动一个定时器&#xff0c;在指定时间间隔内没有接收到新数据&#xff0c;认为数…

java 多线程性能_Java中多线程的性能比较

java 多线程性能Java中有多种用于多线程的技术。 可以通过同步关键字&#xff0c;锁或原子变量来并行化Java中的一段代码。 这篇文章将比较使用synced关键字ReentrantLock&#xff0c;getAndIncrement&#xff08;&#xff09;以及执行get&#xff08;&#xff09;和compareAnd…

C语言贪吃蛇小游戏 | 源码

点击蓝字关注我们该程序已经在VS2019上验证过了&#xff0c;可以直接运行。程序目录程序里就只有一个test.cpp文件&#xff0c;也就是说大家可以先创建工程&#xff0c;然后直运行效果C语言学习资源汇总【最新版】源码#include<stdio.h> #include<stdlib.h> #inclu…

东华大学计算机学院讲座单,计信学院成功举办2018级专业导师面对面系列讲座活动...

为帮助大一新生尽快适应大学生活&#xff0c;了解专业&#xff0c;掌握知识&#xff0c;计信学院积极响应学校号召&#xff0c;成立由教授、博士组成的专业导师团队&#xff0c;为各专业开展系列专业知识讲座。自10月17日晚7点至10月26日下午4点&#xff0c;各专业导师面对面系…

python 控制有线网卡_Python 使用指定的网卡发送HTTP请求的实例

需求: 一台机器上有多个网卡, 如何访问指定的 URL 时使用指定的网卡发送数据呢? $ curl --interface eth0 www.baidu.com # curl interface 可以指定网卡 阅读 urllib.py 的源码, 追述到 open_http –> httplib.HTTP –> httplib.HTTP._connection_class HTTPConnectio…