魔改xjar支持springboot3,
jar包加密方案xjar, 不支持springboot3。这个发个魔改文章希望大家支持
最近公司需要将项目部署在第三方服务器,于是就有了jar包加密的需求,了解了下目前加密方案现况如下:
- 混淆方案,就是在代码中添加大量伪代码,以便隐藏业务代码
- 加密方案,将jar包中的所有class加密,在运行时通过自定义classloader进行解密
目前有的所有加密方案基本思路都跟上面大差不差,在了解了一圈决定使用了xjar这个开源项目。它的实现思路就是方案2.
打开github首页,xjar项目文档还是不错的,clone下来,跟着文档上手,很容易就测试并通过了我的测试项目,接着便推给其他同事使用了。
好景不长,下午就有同事找我,说他的项目加密后不能成功运行。我去看了下,加密操作上没什么问题,但是就是加密包不能成功运行。
报错如下:
错误: 找不到或无法加载主类 null
原因: java.lang.ClassNotFoundException: null
panic: exit status 1
了解了下,同事那边使用了springboot3,而我测试项目是springboot2。 难道不支持springboot3? 我心里想到。
先简单看了下加密的jar包目录结构,很容易就发现以下问题:
- jar包中MANIFEST.MF文件中, Main-Class属性没有值
- 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.url
中URLStreamHandler
,将会在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哟~ 好用点个赞。把
相关文章:
魔改xjar支持springboot3,
jar包加密方案xjar, 不支持springboot3。这个发个魔改文章希望大家支持 最近公司需要将项目部署在第三方服务器,于是就有了jar包加密的需求,了解了下目前加密方案现况如下: 混淆方案,就是在代码中添加大量伪代码,以便隐藏业务代…...
python json文件读写
在Python中处理JSON文件是一个常见的任务。JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。Python提供了内置的json模块来帮助我们读取和写入JSON格式的数据。 如何读…...
Android常用C++特性之std::find_if
声明:本文内容生成自ChatGPT,目的是为方便大家了解学习作为引用到作者的其他文章中。 std::find_if 是 C 标准库中的一个算法,用于在给定范围内查找第一个满足特定条件的元素。它接受一个范围(由迭代器指定)和一个谓词…...
19 vue3之自定义指令Directive按钮鉴权
directive-自定义指令(属于破坏性更新) Vue中有v-if,v-for,v-bind,v-show,v-model 等等一系列方便快捷的指令 今天一起来了解一下vue里提供的自定义指令 Vue3指令的钩子函数 created 元素初始化的时候beforeMount 指令绑定到元素后调用 只调用一次mounted 元素插入父级dom…...

数据资产新范式,URP城市焕新平台东博会首发!
城市数据资产蕴藏着巨大的宝藏。今年1月,国家数据局印发《“数据要素”三年行动计划(2024—2026年)》,将“数据要素智慧城市”上升为“数据要素”计划的重要部分,加速释放城市数据资产价值。 高质量发展以数据要素驱动…...

儿童乐园软件下载安装 佳易王游乐场会员扣次管理系统操作教程
一、前言 儿童乐园软件下载安装 佳易王游乐场会员扣次管理系统操作教程 软件为绿色免安装版,已经内置数据库,不需再安装数据库文件,软件解压即可。 二、软件程序教程 1、软件可同时管理多个项目,项目设置方法如图,点…...

windows下 Winobj.exe工具使用说明c++
1、winobj.exe工具下载地址 WinObj - Sysinternals | Microsoft Learn 2、接下来用winobj.exe查看全局互斥,先写一个小例子 #include <iostream> #include <stdlib.h> #include <tchar.h> #include <string> #include <windows.h>…...

提示词工程 (Prompt Engineering) 最佳实践
prompt Engineering 概念解析 提示工程是一门较新的学科,关注提示词开发和优化,帮助用户将大语言模型(Large Language Model, LLM)用于各场景和研究领域。研究人员可利用提示工程来提升大语言模型处理复杂任务场景的能力…...

【读写分离?聊聊Mysql多数据源实现读写分离的几种方案】
文章目录 一.什么是MySQL 读写分离二.读写分离的几种实现方式(手动控制)1.基于Spring下的AbstractRoutingDataSource1.yml2.Controller3.Service实现4.Mapper层5.定义多数据源6.继承Spring的抽象路由数据源抽象类,重写相关逻辑7. 自定义注解WR,用于指定当…...
C++游戏
宠粉福利! 目录 1.猜数字 2.五子棋 3.打怪 4.跑酷 5.打飞机 6.扫雷 1.猜数字 #include <iostream> #include <cstdlib> #include <ctime>int main() {std::srand(static_cast<unsigned int>(std::time(0))); // 设置随机数种子int …...

探索顶级低代码开发平台,实现创新
文章盘点ZohoCreator、OutSystems等10款顶尖低代码开发平台,各平台以快速开发、集成、数据安全等为主要特点,适用于不同企业需求,助力数字化转型。 一、Zoho Creator Zoho Creator 是一个低代码开发平台,它简化了应用开发中的复杂…...

Html--笔记01:使用软件vscode,简介Html5--基础骨架以及标题、段落、图片标签的使用
一.使用VSC--全称:Visual Studio Code vscode用来写html文件,打开文件夹与创建文件夹:①选择文件夹 ②拖拽文件 生成浏览器的html文件的快捷方式: !enter 运行代码到网页的方法: 普通方法:…...

探索反向传播:深度学习中优化神经网络的秘密武器
反向传播的概念: 反向传播(Backpropagation) 是深度学习中训练神经网络的核心算法。它通过有效计算损失函数相对于模型参数的梯度,使得模型能够通过梯度下降等优化方法逐步调整参数,从而最小化损失函数,提…...

K8S精进之路-控制器DaemonSet -(3)
介绍 DaemonSet就是让一个节点上只能运行一个Daemonset Pod应用,每个节点就只有一个。比如最常用的网络组件,存储插件,日志插件,监控插件就是这种类型的pod.如果集群中有新的节点加入,DaemonSet也会在新的节点创建出来…...

【JVM】类加载机制
文章目录 类加载机制类加载过程1. 加载2. 验证3. 准备4. 解析偏移量符号引用和直接引用 5. 初始化 类加载机制 类加载指的是,Java 进程运行的时候,需要把 .class 文件从硬盘读取到内存,并进行一些列的校验解析的过程(程序要想执行…...
ENV | 5步安装 npm node(homebrew 简洁版)
1. 操作步骤 1.1 安装 homebrew /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"1.2 安装 node # 安装最新版 brew install node # 安装指定版本,如18 brew install node181.3 安装 nvm(…...
EasyExcel全面实战:掌握多样化的Excel导出能力
1 概述 本文将通过实战案例全面介绍EasyExcel在Excel导出方面的多种功能。内容涵盖多表头写入、自定义格式、动态表头生成、单元格合并应用等。通过这些实例,读者可以掌握EasyExcel的各种高级功能,并在实际项目中灵活应用。 白日依山尽,黄河入海流。 欲穷千里目,更上一层楼…...

基于springcloud的药品销售系统
文未可获取一份本项目的java源码和数据库参考。 一、选题背景与意义 1. 选题背景 在日常医药管理中,面对众多的药品和众多不同需求的顾客,每天都会产生大量的数据信息。以传统的手工方式来处理这些信息,操作比较繁琐,且效率低下…...

基于组网分割的超大规模设计 FPGA 原型验证解决方案
引言 如何快速便捷的完成巨型原型验证系统的组网,并监测系统的连通性及稳定性? 如何将用户设计快速布局映射到参与组网的原型验证系统的每一块 FPGA? 随着用户设计规模的日益增大,传统基于单片 FPGA 或单块电路板的原型验证系统…...

C# 面向对象基础,简单的银行存钱取钱程序
题目: 代码实现: BankAccount部分: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace Bank {internal class BankAccount{private decimal balance 0;//账…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
uniapp 实现腾讯云IM群文件上传下载功能
UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中,群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS,在uniapp中实现: 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...
SpringAI实战:ChatModel智能对话全解
一、引言:Spring AI 与 Chat Model 的核心价值 🚀 在 Java 生态中集成大模型能力,Spring AI 提供了高效的解决方案 🤖。其中 Chat Model 作为核心交互组件,通过标准化接口简化了与大语言模型(LLM࿰…...