魔改xjar支持springboot3,

jar包加密方案xjar, 不支持springboot3。这个发个魔改文章希望大家支持

最近公司需要将项目部署在第三方服务器,于是就有了jar包加密的需求,了解了下目前加密方案现况如下:

  1. 混淆方案,就是在代码中添加大量伪代码,以便隐藏业务代码
  2. 加密方案,将jar包中的所有class加密,在运行时通过自定义classloader进行解密

目前有的所有加密方案基本思路都跟上面大差不差,在了解了一圈决定使用了xjar这个开源项目。它的实现思路就是方案2.

打开github首页,xjar项目文档还是不错的,clone下来,跟着文档上手,很容易就测试并通过了我的测试项目,接着便推给其他同事使用了。

好景不长,下午就有同事找我,说他的项目加密后不能成功运行。我去看了下,加密操作上没什么问题,但是就是加密包不能成功运行。
报错如下:

错误: 找不到或无法加载主类 null
原因: java.lang.ClassNotFoundException: null
panic: exit status 1

了解了下,同事那边使用了springboot3,而我测试项目是springboot2。 难道不支持springboot3? 我心里想到。

先简单看了下加密的jar包目录结构,很容易就发现以下问题:

  1. jar包中MANIFEST.MF文件中, Main-Class属性没有值
  2. jar包中没有将加解密相关的的class打进去

看样子需要进行二开了,唉。 clone项目到本地,拉个新分支。

首先看下源码,在XBootEncryptor中定义了springboot的classloaderfinal String jarLauncher = "org.springframework.boot.loader.JarLauncher"; 这里的jarlauncher在spring3中已经变包路径了。
没想到这么简单,心里暗喜。于是把这里修改为:org.springframework.boot.loader.launch.JarLauncher。 用spring3搭个demo, 重新打包。

再看jar文件, main-class已经写出去了,xjar相关的包也成功写到jar包。松了口气,看样子没太大问题。

开命令窗口,启动项目,一气呵成。看到终端输出springboot的logo时,心里已经松了口气。

可惜天不遂愿,又打印了几行日志后,抛出以下错误:

2024-09-27T17:51:36.403+08:00 ERROR 3796 --- [demo] [           main] o.s.boot.SpringApplication               : Application run failedorg.springframework.beans.factory.BeanDefinitionStoreException: Incompatible class format in URL [jar:nested:/D:/workspace/xiudianer/workspace/transport-mqtt-device-v4/branches/4.0.0/transport-mqtt-device-xjar/src/main/java/com/xd/device/ad/jars/encrypted5.jar/!BOOT-INF/classes/!/com/example/demo/DemoApplication.class]: set system property 'spring.classformat.ignore' to 'true' if you mean to ignore such files during classpath scanningat org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.scanCandidateComponents(ClassPathScanningCandidateComponentProvider.java:504) ~[spring-context-6.1.13.jar!/:6.1.13]at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents(ClassPathScanningCandidateComponentProvider.java:351) ~[spring-context-6.1.13.jar!/:6.1.13]at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:277) ~[spring-context-6.1.13.jar!/:6.1.13]at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:128) ~[spring-context-6.1.13.jar!/:6.1.13]at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:306) ~[spring-context-6.1.13.jar!/:6.1.13]at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:246) ~[spring-context-6.1.13.jar!/:6.1.13]at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:197) ~[spring-context-6.1.13.jar!/:6.1.13]at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:165) ~[spring-context-6.1.13.jar!/:6.1.13]at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:417) ~[spring-context-6.1.13.jar!/:6.1.13]at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290) ~[spring-context-6.1.13.jar!/:6.1.13]at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) ~[spring-context-6.1.13.jar!/:6.1.13]at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) ~[spring-context-6.1.13.jar!/:6.1.13]at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:789) ~[spring-context-6.1.13.jar!/:6.1.13]at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:607) ~[spring-context-6.1.13.jar!/:6.1.13]at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.3.4.jar!/:3.3.4]at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.3.4.jar!/:3.3.4]at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.3.4.jar!/:3.3.4]at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.3.4.jar!/:3.3.4]at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.3.4.jar!/:3.3.4]at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.3.4.jar!/:3.3.4]at com.example.demo.DemoApplication.main(DemoApplication.java:11) ~[!/:0.0.1-SNAPSHOT]at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:91) ~[encrypted5.jar:0.0.1-SNAPSHOT]at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:53) ~[encrypted5.jar:0.0.1-SNAPSHOT]at io.xjar.boot.XJarLauncher.launch(XJarLauncher.java:27) ~[encrypted5.jar:0.0.1-SNAPSHOT]at io.xjar.boot.XJarLauncher.main(XJarLauncher.java:23) ~[encrypted5.jar:0.0.1-SNAPSHOT]
Caused by: org.springframework.core.type.classreading.ClassFormatException: ASM ClassReader failed to parse class file - probably due to a new Java class file version that is not supported yet. Consider compiling with a lower '-target' or upgrade your framework version. Affected class: URL [jar:nested:/D:/workspace/xiudianer/workspace/transport-mqtt-device-v4/branches/4.0.0/transport-mqtt-device-xjar/src/main/java/com/xd/device/ad/jars/encrypted5.jar/!BOOT-INF/classes/!/com/example/demo/DemoApplication.class]at org.springframework.core.type.classreading.SimpleMetadataReader.getClassReader(SimpleMetadataReader.java:59) ~[spring-core-6.1.13.jar!/:6.1.13]at org.springframework.core.type.classreading.SimpleMetadataReader.<init>(SimpleMetadataReader.java:48) ~[spring-core-6.1.13.jar!/:6.1.13]at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:103) ~[spring-core-6.1.13.jar!/:6.1.13]at org.springframework.core.type.classreading.CachingMetadataReaderFactory.getMetadataReader(CachingMetadataReaderFactory.java:122) ~[spring-core-6.1.13.jar!/:6.1.13]at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.scanCandidateComponents(ClassPathScanningCandidateComponentProvider.java:470) ~[spring-context-6.1.13.jar!/:6.1.13]... 28 common frames omitted
Caused by: java.lang.IllegalArgumentException: nullat org.springframework.asm.ClassReader.<init>(ClassReader.java:262) ~[spring-core-6.1.13.jar!/:6.1.13]at org.springframework.asm.ClassReader.<init>(ClassReader.java:180) ~[spring-core-6.1.13.jar!/:6.1.13]at org.springframework.asm.ClassReader.<init>(ClassReader.java:166) ~[spring-core-6.1.13.jar!/:6.1.13]at org.springframework.asm.ClassReader.<init>(ClassReader.java:287) ~[spring-core-6.1.13.jar!/:6.1.13]at org.springframework.core.type.classreading.SimpleMetadataReader.getClassReader(SimpleMetadataReader.java:56) ~[spring-core-6.1.13.jar!/:6.1.13]... 32 common frames omittedpanic: exit status 1

一脸蒙蔽,这tm是什么错啊。

再看源码,xjar是自定义classloader来加载并解密类,核心就在XBootClassLoader.findClass。 这里源码中修改并打印下class的文件数据,看看究竟加载的什么鬼~

启动项目发现,class文件已解密,但为啥报错呢?只能使用万能断点大法了,在异常堆栈SimpleMetadataReader.getClassReader处断点,从这里入手。

该方法反编译源码如下:

    private static ClassReader getClassReader(Resource resource) throws IOException {InputStream is = resource.getInputStream();ClassReader var2;try {try {var2 = new ClassReader(is);} catch (IllegalArgumentException var5) {throw new ClassFormatException("ASM ClassReader failed to parse class file - probably due to a new Java class file version that is not supported yet. Consider compiling with a lower '-target' or upgrade your framework version. Affected class: " + resource, var5);}} catch (Throwable var6) {if (is != null) {try {is.close();} catch (Throwable var4) {var6.addSuppressed(var4);}}throw var6;}if (is != null) {is.close();}return var2;}

这里有看到inputstrem,于是断点把inputstream打印输出看下内容,发现这里读取的class文件居然是加密的。那么为啥这里没解密呢? 继续往上跟堆栈。

根据参数Resource, 往上跟可以找到springboot扫描注解组件逻辑,也就是扫描项目中所有有注解的class,然后再调用这个方法读取加载class。 继续去看看这个Resource是怎么创建的。

一路跟,在PathMatchingResourcePatternResolver.doFindPathMatchingJarResources发现了创建Resource资源的代码result.add(rootDirResource.createRelative(relativePath));

protected Set<Resource> doFindPathMatchingJarResources(Resource rootDirResource, URL rootDirUrl, String subPattern) throws IOException {URLConnection con = rootDirUrl.openConnection();JarFile jarFile;String jarFileUrl;String rootEntryPath;boolean closeJarFile;if (con instanceof JarURLConnection jarCon) {jarFile = jarCon.getJarFile();jarFileUrl = jarCon.getJarFileURL().toExternalForm();JarEntry jarEntry = jarCon.getJarEntry();rootEntryPath = jarEntry != null ? jarEntry.getName() : "";closeJarFile = !jarCon.getUseCaches();} else {String urlFile = rootDirUrl.getFile();try {int separatorIndex = urlFile.indexOf("*/");if (separatorIndex == -1) {separatorIndex = urlFile.indexOf("!/");}if (separatorIndex != -1) {jarFileUrl = urlFile.substring(0, separatorIndex);rootEntryPath = urlFile.substring(separatorIndex + 2);jarFile = this.getJarFile(jarFileUrl);} else {jarFile = new JarFile(urlFile);jarFileUrl = urlFile;rootEntryPath = "";}closeJarFile = true;} catch (ZipException var17) {if (logger.isDebugEnabled()) {logger.debug("Skipping invalid jar class path entry [" + urlFile + "]");}return Collections.emptySet();}}try {if (logger.isTraceEnabled()) {logger.trace("Looking for matching resources in jar file [" + jarFileUrl + "]");}if (StringUtils.hasLength(rootEntryPath) && !rootEntryPath.endsWith("/")) {rootEntryPath = rootEntryPath + "/";}Set<Resource> result = new LinkedHashSet(64);Enumeration<JarEntry> entries = jarFile.entries();while(entries.hasMoreElements()) {JarEntry entry = (JarEntry)entries.nextElement();String entryPath = entry.getName();if (entryPath.startsWith(rootEntryPath)) {String relativePath = entryPath.substring(rootEntryPath.length());if (this.getPathMatcher().match(subPattern, relativePath)) {result.add(rootDirResource.createRelative(relativePath)); // 注意这里,创建了resource资源}}}LinkedHashSet var22 = result;return var22;} finally {if (closeJarFile) {jarFile.close();}}}

上面的代码创建了resource资源,于是继续跟进继续跟进createRelative方法,那么问题来了,为啥springboot2没问题,spring3读取出来的却又是加密的,这是为毛?

最简单的方法就是对比着看, 两个版本方法如下:

springboot3

    protected URL createRelativeURL(String relativePath) throws MalformedURLException {if (relativePath.startsWith("/")) {relativePath = relativePath.substring(1);}return ResourceUtils.toRelativeURL(this.url, relativePath);}

springboot2

	@Overridepublic Resource createRelative(String relativePath) throws MalformedURLException {if (relativePath.startsWith("/")) {relativePath = relativePath.substring(1);}return new UrlResource(new URL(this.url, relativePath));}

上边代码一眼看,也没太大问题。都是通过url创建了一个资源链接而已,为毛就是跑步起来呢。 再次祭出断点大法。

断点后发现,两个创建的资源中, URL 属性中的URLStreamHandler有很大区别。springboot2中该属性为xjar的解密器,二springboot3中却是一个简单的文件读取器。

为啥呢,喔翻了下源码发现在springboot2中,创建Resouces实例时,url属性实例是直接new出来的,当前类的解密器也就是this.urlURLStreamHandler,将会在URL的构造器中得到继承,所以新Resource读取时也就会解密了

但在springboot3中,ResourceUtils.toRelativeURL方法中创建URL时是先构建URI实例,再创建URL实例,这个过程中把this.URLStreamHandler丢失了。

原因找到了,看了下springboot github的issue,官方确实变更了,原因是因为springboot2中的URL的构造函数在之后的jdk20标记为废弃,所以就改了实现方法。不过官方表示下个版本会兼容处理这个问题,目前spring3最新版为3.3.4

但问题是我们现在就要用啊,只能拉个分支魔改了。

想想思路,既然旧版本没问题,那么喔先把spring-core包中的这个方法还原为老版本,看看spring能正常跑起来不。测试了下,没问题。

其实这里基本就没问题了,实际项目中只要把这个spring-core包从私仓中替换,项目加密也就没有太大的毛病,但是这样对于追求完美的我来说,方便性还差点。毕竟如果使用了springboot3的多个版本,不可能每个都去修改替换下啊,想想都好麻烦

那就再扩展下吧,既然自定义了classloader,那我可以在类加载过程中通过asm修改加载中的该方法的字节码,将UrlResource.createRelative方法逻辑替换为老版本。

由于asm是通过字节码来修改方法的,根据java源码来写出字节码,小弟还没有那个功力。

取个巧,直接使用javap -verbose URLResource.class 来查看老版本这个方法的指令流程

 public org.springframework.core.io.Resource createRelative(java.lang.String) throws java.net.MalformedURLException;descriptor: (Ljava/lang/String;)Lorg/springframework/core/io/Resource;flags: (0x0001) ACC_PUBLICCode:stack=6, locals=2, args_size=20: aload_11: ldc           #36                 // String /3: invokevirtual #37                 // Method java/lang/String.startsWith:(Ljava/lang/String;)Z6: ifeq          159: aload_110: iconst_111: invokevirtual #38                 // Method java/lang/String.substring:(I)Ljava/lang/String;14: astore_115: new           #39                 // class org/springframework/core/io/UrlResource18: dup19: new           #13                 // class java/net/URL22: dup23: aload_024: getfield      #6                  // Field url:Ljava/net/URL;27: aload_128: invokespecial #40                 // Method java/net/URL."<init>":(Ljava/net/URL;Ljava/lang/String;)V31: invokespecial #41                 // Method "<init>":(Ljava/net/URL;)V34: areturnLineNumberTable:line 238: 0line 239: 9line 241: 15LocalVariableTable:Start  Length  Slot  Name   Signature0      35     0  this   Lorg/springframework/core/io/UrlResource;0      35     1 relativePath   Ljava/lang/String;StackMapTable: number_of_entries = 1frame_type = 15 /* same */Exceptions:throws java.net.MalformedURLException

然后使用asm转译下,以下为实现核心代码:

@Overridepublic MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);if (name.equals("createRelative") && desc.equals("(Ljava/lang/String;)Lorg/springframework/core/io/Resource;")) {// 修改原方法的字节码mv = new MyMethodVisitor(mv) ;}return mv;}//MyMethodVisitor的结构public class MyMethodVisitor extends MethodVisitor {private final MethodVisitor target;public MyMethodVisitor(MethodVisitor mv) {super(ASM9, null);this.target = mv;}//此方法在目标方法调用之前调用,所以前置操作可以在这处理@Overridepublic void visitCode() {target.visitCode();target.visitCode();target.visitVarInsn(ALOAD, 1);Label A = new Label();Label B = new Label();target.visitLdcInsn("/");target.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "startsWith", "(Ljava/lang/String;)Z", false);target.visitJumpInsn(IFEQ, A);target.visitVarInsn(ALOAD, 1);target.visitInsn(ICONST_1);target.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "substring", "(I)Ljava/lang/String;", false);target.visitVarInsn(ASTORE, 1);target.visitJumpInsn(GOTO,B);target.visitLabel(A);target.visitLabel(B);target.visitTypeInsn(Opcodes.NEW, "org/springframework/core/io/UrlResource");target.visitInsn(DUP);target.visitTypeInsn(Opcodes.NEW, "java/net/URL");target.visitInsn(DUP);target.visitVarInsn(ALOAD, 0);target.visitFieldInsn(GETFIELD, "org/springframework/core/io/UrlResource", "url", "Ljava/net/URL;");target.visitVarInsn(ALOAD, 1);target.visitMethodInsn(INVOKESPECIAL, "java/net/URL", "<init>", "(Ljava/net/URL;Ljava/lang/String;)V", false);target.visitMethodInsn(INVOKESPECIAL, "org/springframework/core/io/UrlResource", "<init>", "(Ljava/net/URL;)V", false);target.visitInsn(ARETURN); //target.visitMaxs(6, 2);target.visitEnd();}@Overridepublic void visitMaxs(int maxStack, int maxLocals) {super.visitMaxs(maxStack + 1, maxLocals);}}

最后,由于classloader中使用了asm,所以也需要将asm提前打到加密jar包 中。

然后再来编译一个加密包,再启动,成功!

这样道友们就可以直接使用项目,不必去修改spring-core包了。

项目传送地址:https://github.com/MisterChangRay/xjar4springboot3
这个只支持springboot3哟~ 好用点个赞。把

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

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

相关文章

常见电脑品牌BIOS设置与进入启动项快捷键

常见电脑品牌BIOS与引导项快捷键速查表 | 电脑品牌 | BIOS快捷键 | 引导项快捷键 | 备注 ||------------|------------|--------------|------------------------------ || 联想 | F2/F1 | F12 | 笔记本通常为F2&#xff0c;台式机通常为F1 || IBM/ThinkPad | F1 | 未知 | ||…

【AI】AIOT简介

随着技术的快速发展&#xff0c;人工智能AI和物联网IoT已经成为当今最热门的技术领域。AIOT是人工智能和物联网的结合&#xff0c;使物联网设备更加智能化&#xff0c;能够进行自主决策和学习的技术。 通过物联网产生、收集来自不同维度的、海量的数据存储于云端、边缘端&#…

Ubuntu上安装Git:简单步骤指南

Git是目前世界上最流行的版本控制系统&#xff0c;广泛用于软件开发中。无论你是开发者还是版本控制的新手&#xff0c;Git都是你不可或缺的工具。本文将为你介绍如何在Ubuntu操作系统上安装Git。 什么是Git&#xff1f; Git是一个开源的分布式版本控制系统&#xff0c;由Lin…

数据治理006-数据标准的管理

元数据的分类和标准有哪些&#xff1f; 一、元数据的分类 元数据可以根据其描述的对象和属性不同&#xff0c;被分为不同的类型。以下是几种常见的元数据分类方法&#xff1a; 基于数据的类型&#xff1a;根据数据的类型&#xff0c;元数据可以被分为结构化元数据、非结构化元…

SQL连接Python

对于运营部门的Yoyo来说&#xff0c;她想要知道夜曲优选的订单都来自哪些省份&#xff0c;每个省份的总订单数以及总订单金额分别是多少。 这时小鹿就会通过SQL对连接的数据库进行查询&#xff0c;再将结果传递给Python处理&#xff0c;并帮助Yoyo生成可视化图表。 我们先来快…

拆解维修飞科剃须刀

原因 用了好几年的剃须刀&#xff0c;经过一次更换电池。后来上面的盖帽松动&#xff0c;无法合盖&#xff0c;经过把弹片矫正后修复。最近一次”大力出奇迹“的操作直接断送了这个老伤员最后的可能性。最终只能花了将近十块大洋买了一套盖着和中间座。简单更换了一下。 记录…

单元测试进阶-Mock使用和插桩

目录 一、基本概念 1、Mock 2、插桩&#xff08;Sutbbing&#xff09; 二、参考文章 一、基本概念 1、Mock Mock的作用就是不直接new对象&#xff0c;而是使用Mock方法或者注解Mock一个对象。 这个对象他不是new创建的对象&#xff0c;Mock对该对象的一些成员变量和方法…

【RabbitMQ 项目】服务端:消费者管理模块

文章目录 一.概念辨析1.什么是消费者&#xff1f;2.服务端为什么要管理消费者&#xff1f;3.怎么管理消费者&#xff1f;4.需要管理生产者吗&#xff1f; 二.编写思路1.定义消费者2.定义队列消费者管理管理类3.定义消费者管理类 三.代码实践 一.概念辨析 1.什么是消费者&#…

html嵌入百度地图

html嵌入百度地图 key地址 https://lbsyun.baidu.com/apiconsole/key#/home &#xff0c;点进去注册应用、然后复制key换掉即可显示地图 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>百度地图搜索…

C++随心记 续一

C中的模板 在其它语言中如Java或者C#中可能叫做泛型&#xff0c;在C中为模板&#xff0c;泛型的限制通常比模板多。模板可以解决多次的代码重复问题&#xff0c;如以下场景 #include <iostream> #include <string>void print(int value) {std::cout << val…

MySQL存储和处理XML数据

文章目录 一、实战概述二、准备数据三、实战步骤1、创建数据库2、创建数据表3、插入XML数据4、查询XML数据5、修改XML数据6、删除XML数据7、注意事项四、实战小结一、实战概述 MySQL不直接支持XML数据类型,但可以通过TEXT或BLOB类型字段存储XML数据,利用ExtractValue和Update…

Spark Job 对象 详解

在 Apache Spark 中&#xff0c;Job 对象是执行逻辑的核心组件之一&#xff0c;它代表了对一系列数据操作&#xff08;如 transformations 和 actions&#xff09;的提交。理解 Job 的本质和它在 Spark 中的运行机制&#xff0c;有助于深入理解 Spark 的任务调度、执行模型和容…

滚雪球学Oracle[8.3讲]:区块链与Oracle

全文目录&#xff1a; 前言一、Oracle Blockchain的架构与应用1.1 Oracle Blockchain的基本架构1.1.1 Oracle Blockchain的核心组件 1.2 Oracle Blockchain的优势示例&#xff1a;Oracle Blockchain的实际应用 二、区块链与传统数据库的集成2.1 区块链与传统数据库的关系2.2 区…

PWM:控制信号的秘密武器

什么是PWM&#xff1f; PWM&#xff08;Pulse Width Modulation&#xff0c;脉宽调制&#xff09;是一种通过改变信号的占空比来控制电压输出的技术。简单来说&#xff0c;PWM信号由一系列高低电平组成&#xff0c;通过调节高电平持续的时间比例&#xff0c;可以控制信号的平均…

目前最好用的爬虫软件是那个?

作为一名数据工程师&#xff0c;三天两头要采集数据&#xff0c;用过十几种爬虫软件&#xff0c;也用过Python爬虫库&#xff0c;还是建议新手使用现成的软件比较方便。 这里推荐3款不错的自动化爬虫工具&#xff0c;八爪鱼、亮数据、Web Scraper 1. 八爪鱼爬虫 八爪鱼爬虫是一…

ARM base instruction -- ccmp

Conditional Compare (register) sets the value of the condition flags to the result of the comparison of two registers if the condition is TRUE, and an immediate value otherwise. 条件比较&#xff08;寄存器&#xff09;如果条件为真&#xff0c;则将条件标志的值…

Linux:深入理解冯诺依曼结构与操作系统

目录 1. 冯诺依曼体系结构 1.1 结构分析 1.2 存储结构分布图 2. 操作系统 2.1 概念 2.2 如何管理 2.3 什么是系统调用和库函数 1. 冯诺依曼体系结构 1.1 结构分析 不管是何种计算机&#xff0c;如个人笔记本电脑&#xff0c;服务器&#xff0c;都是遵循冯诺依曼结构。…

[leetcode]674_最长连续递增序列

给定一个未经排序的整数数组&#xff0c;找到最长且 连续递增的子序列&#xff0c;并返回该序列的长度。 连续递增的子序列 可以由两个下标 l 和 r&#xff08;l < r&#xff09;确定&#xff0c;如果对于每个 l < i < r&#xff0c;都有 nums[i] < nums[i 1] &am…

可视化图表与源代码显示配置项及页面的动态调整功能分析

可视化图表与源代码显示配置项及页面的动态调整功能分析 文章目录 可视化图表与源代码显示配置项及页面的动态调整功能分析1.分析图表源代码2.分析源代码显示功能**完整代码参考&#xff1a;** 3.分析源代码显示及动态调整**完整代码参考&#xff1a;** 4.分析代码编辑器及运行…

【AI知识点】泛化(Generalization)与过拟合(Overfitting)

泛化&#xff08;generalization&#xff09; 是机器学习中的一个核心概念&#xff0c;指的是模型在训练数据之外的新数据上表现得如何。换句话说&#xff0c;泛化能力衡量的是模型能否在未见过的样本上做出正确的预测或推断。 1. 泛化的背景 当我们训练机器学习模型时&#…