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

【JVM】总结篇-类的加载篇之 类的加载器 和ClassLoader分析

文章目录

    • 类的加载器
    • ClassLoader
    • 自定义类加载器
    • 双亲委派机制
      • 概念
      • 源码分析
      • 优势劣势
      • 如何打破
      • Tomcat
    • 沙箱安全机制
    • JDK9 双亲委派机制变化

类的加载器

在这里插入图片描述

获得当前类的ClassLoader clazz.getClassLoader()
获得当前线程上下文的ClassLoader Thread.currentThread().getContextClassLoader()
获得系统的ClassLoader ClassLoader.getSystemClassLoader()

ClassLoader

在这里插入图片描述
抽象类ClassLoader的主要方法:(内部没有抽象方法)

public final ClassLoader getParent()
返回该类加载器的超类加载器
public Class<?> loadClass(String name) throws ClassNotFoundException
加载名称为name的类,返回结果为java.lang.Class类的实例。
如果找不到类,则返回 ClassNotFoundException 异常。该方法中的逻辑就是双亲委派模式的实现。
protected Class<?> findClass(String name) throws ClassNotFoundException
查找二进制名称为name的类,返回结果为java.lang.Class类的实例。这是一个受保护的方法,
JVM鼓励我们重写此方法,需要自定义加载器遵循双亲委托机制,
该方法会在检查完父类加载器之后被loadClass()方法调用。

在JDK1.2之前,在自定义类加载时,总会去继承ClassLoader类并重写loadClass方法,从而实现自定义的类加载类。但是在JDK1.2之后已不再建议用户去覆盖loadClass()方法,而是建议把自定义的类加载逻辑写在findClass()方法中,从前面的分析可知,findClass()方法是在loadClass()方法中被调用的,当loadClass()方法中父加载器加载失败后,则会调用自己的findClass()方法来完成类加载,这样就可以保证自定义的类加载器也符合双亲委托模式。
需要注意的是ClassLoader类中并没有实现findClass()方法的具体代码逻辑,取而代之的是抛出ClassNotFoundException异常,同时应该知道的是findClass方法通常是和defineClass方法一起使用的。一般情况下,在自定义类加载器时,会直接覆盖ClassLoader的findClass()方法并编写加载规则,取得要加载类的字节码后转换成流,然后调用defineClass()方法生成类的Class对象。

protected final Class<?> defineClass(String name, byte[] b, int off, int len)
根据给定的字节数组b转换为Class的实例,off和len参数表示实际Class信息在byte数组中的位置和长度,
其中byte数组b是ClassLoader从外部获取的。这是受保护的方法,只有在自定义ClassLoader子类中可以使用。

defineClass()方法是用来将byte字节流解析成JVM能够识别的Class对象(ClassLoader中已实现该方法逻辑),通过这个方法不仅能够通过class文件实例化class对象,也可以通过其他方式实例化class对象,如通过网络接收一个类的字节码,然后转换为byte字节流创建对应的Class对象。
defineClass()方法通常与findClass()方法一起使用,一般情况下,在自定义类加载器时,会直接覆盖ClassLoader的findClass()方法并编写加载规则,取得要加载类的字节码后转换成流,然后调用defineClass()方法生成类的Class对象

简单举例:

protected Class<?> findClass(String name) throws ClassNotFoundException {// 获取类的字节数组byte[] classData = getClassData(name);if (classData == null) {throw new ClassNotFoundException();} else {//使用defineClass生成class对象return defineClass(name, classData, 0, classData.length);}
}
protected final void resolveClass(Class<?> c)
链接指定的一个Java类。使用该方法可以使用类的Class对象创建完成的同时也被解析。
前面我们说链接阶段主要是对字节码进行验证,
为类变量分配内存并设置初始值同时将字节码文件中的符号引用转换为直接引用。
protected final Class<?> findLoadedClass(String name)
查找名称为name的已经被加载过的类,返回结果为java.lang.Class类的实例。这个方法是final方法,无法被修改。
 private final ClassLoader parent;
它也是一个ClassLoader的实例,这个字段所表示的ClassLoader也称为这个ClassLoader的双亲。
在类加载的过程中,ClassLoader可能会将某些请求交予自己的双亲处理。

在这里插入图片描述
在这里插入图片描述

自定义类加载器

在这里插入图片描述
在这里插入图片描述


public class UserClassLoader extends ClassLoader {private String rootDir;public UserClassLoader(String rootDir) {this.rootDir = rootDir;}/*** 编写findClass方法的逻辑*/@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {// 获取类的class文件字节数组byte[] classData = getClassData(name);if (classData == null) {throw new ClassNotFoundException();} else {//直接生成class对象return defineClass(name, classData, 0, classData.length);}}/*** 编写获取class文件并转换为字节码流的逻辑 * @param className * @return*/private byte[] getClassData(String className) {// 读取类文件的字节String path = classNameToPath(className);try {InputStream ins = new FileInputStream(path);ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int len = 0;// 读取类文件的字节码while ((len = ins.read(buffer)) != -1) {baos.write(buffer, 0, len);}return baos.toByteArray();} catch (IOException e) {e.printStackTrace();}return null;}/*** 类文件的完全路径*/private String classNameToPath(String className) {return rootDir + "\\" + className.replace('.', '\\') + ".class";}public static void main(String[] args) {String rootDir = "D:\\Java_course\\JVM\\JVMdachang\\chapter02_classload\\src\\";try {//创建自定义的类的加载器1UserClassLoader loader1 = new UserClassLoader(rootDir);Class clazz1 = loader1.findClass("com.cz.java3.User");//创建自定义的类的加载器2UserClassLoader loader2 = new UserClassLoader(rootDir);Class clazz2 = loader2.findClass("com.cz.java3.User");System.out.println(clazz1 == clazz2); //clazz1与clazz2对应了不同的类模板结构。System.out.println(clazz1.getClassLoader());System.out.println(clazz2.getClassLoader());//######################Class clazz3 = ClassLoader.getSystemClassLoader().loadClass("com.cz.java3.User");System.out.println(clazz3.getClassLoader());System.out.println(clazz1.getClassLoader().getParent());} catch (ClassNotFoundException e) {e.printStackTrace();}}}
false
com.atguigu.java3.UserClassLoader@14ae5a5
com.atguigu.java3.UserClassLoader@6d6f6e28
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2

双亲委派机制

双亲委派好处有哪些?(亚信)
类加载器双亲委派模型机制?(苏宁)
双亲委派机制 (蚂蚁金服)
双亲委派机制及使用原因 (蚂蚁金服)
类加载器的双亲委派模型是什么? (蚂蚁金服)
双亲委派模型介绍一下 (小米)
讲一下双亲委派模型,以及其优点 (滴滴)
类加载器的双亲委派模型是什么? (京东)

概念

1.定义
如果一个类加载器在接到加载类的请求时,它首先不会自己尝试去加载这个类,而是把这个请求任务委托给父类加载器去完成,依次递归,如果父类加载器可以完成类加载任务,就成功返回。只有父类加载器无法完成此加载任务时,才自己去加载。

2.本质
规定了类加载的顺序是:引导类加载器先加载,若加载不到,由扩展类加载器加载,若还加载不到,才会由系统类加载器或自定义的类加载器进行加载。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

源码分析

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}

在这里插入图片描述
在这里插入图片描述

优势劣势

在这里插入图片描述在这里插入图片描述

如何打破

双亲委派机制可以打破吗?为什么 (京东)
可以打破双亲委派么,怎么打破。(拼多多)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Tomcat

什么是tomcat类加载机制?(猎聘)
请解释tomcat的类加载机制?(阿里)

Tomcat8 和 Tomcat6比较大的区别是 :
Tomcat8可以通过配置 表示遵循双亲委派机制。

Tomcat的类加载机制是违反了双亲委托原则的,对于一些未加载的非基础类,各个web应用自己的类加载器(WebAppClassLoader)会优先查看自己的仓库加载,加载不到时再交给commonClassLoader走双亲委托。

Tomcat 如何实现自己独特的类加载机制?
在这里插入图片描述
当tomcat启动时,会创建几种类加载器:

  1. Bootstrap 引导类加载器
    加载JVM启动所需的类,以及标准扩展类(位于jre/lib/ext下)
  2. System 系统类加载器
    加载tomcat启动的类,比如bootstrap.jar,通常在catalina.bat或者catalina.sh中指定。位于CATALINA_HOME/bin下。
    在这里插入图片描述
  3. CommonClassLoader、CatalinaClassLoader、SharedClassLoader和WebappClassLoader
    这些是Tomcat自己定义的类加载器,它们分别加载/common/、/server/、/shared/*(在tomcat 6之后已经合并到根目录下的lib目录下)和/WebApp/WEB-INF/*中的Java类库。其中WebApp类加载器和Jsp类加载器通常会存在多个实例,每一个Web应用程序对应一个WebApp类加载器,每一个JSP文件对应一个Jsp类加载器。
    commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;(加载CATALINA_HOME/lib下的结构,比如servlet-api.jar)
    catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
    sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
    WebappClassLoader:单个Tomcat实例中各个Web应用程序私有的类加载器,加载路径中的class只对当前Webapp可见;(加载WEB-INF/lib和WEB-INF/classes下的结构)

从图中的委派关系中可以看出:
CommonClassLoader能加载的类都可以被Catalina ClassLoader和SharedClassLoader使用,从而实现了公有类库的共用,而CatalinaClassLoader和Shared ClassLoader自己能加载的类则与对方相互隔离。
WebAppClassLoader可以使用SharedClassLoader加载到的类,但各个WebAppClassLoader实例之间相互隔离。
而JasperLoader的加载范围仅仅是这个JSP文件所编译出来的那一个.class文件,它出现的目的就是为了被丢弃:当Web容器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能。

当应用需要到某个类时,则会按照下面的顺序进行类加载:
1 使用bootstrap引导类加载器加载
2 使用system系统类加载器加载
3 使用应用类加载器在WEB-INF/classes中加载
4 使用应用类加载器在WEB-INF/lib中加载
5 使用common类加载器在CATALINA_HOME/lib中加载

在这里插入图片描述
好了,至此,我们已经知道了tomcat为什么要这么设计,以及是如何设计的,那么,tomcat 违背了java 推荐的双亲委派模型了吗?答案是:违背了。 我们前面说过:双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都应当由自己的父类加载器加载。
很显然,tomcat 不是这样实现,tomcat 为了实现隔离性,没有遵守这个约定,每个webappClassLoader加载自己的目录下的class文件,不会传递给父类加载器。

Tomcat中WebappClassLoader的源码分析:

 1 public Class loadClass(String name, boolean resolve)2             throws ClassNotFoundException {3         Class clazz = null;4         // (0) 先从自己的缓存中查找,有则返回,无则继续5         clazz = findLoadedClass0(name);6         if (clazz != null) {7             if (resolve) resolveClass(clazz);            8             return (clazz);9         }
10         // (0.1) 再从parent的缓存中查找
11         clazz = findLoadedClass(name);
12         if (clazz != null) {
13             if (resolve) resolveClass(clazz);
14             return (clazz);
15         }
16         // (0.2) 缓存中没有,则首先使用system类加载器来加载
17         clazz = system.loadClass(name);
18          if (clazz != null) {
19              if (resolve) resolveClass(clazz);
20              return (clazz);
21          }
22         //判断是否需要先让parent代理
23         boolean delegateLoad = delegate || filter(name);
24         // (1) 先让parent加载,通常delegateLoad == false,即这一步不会执行
25 
26         if (delegateLoad) {
27             ClassLoader loader = parent;
28             if (loader == null)
29                 loader = system;
30             clazz = loader.loadClass(name);
31             if (clazz != null) {
32                 if (resolve) resolveClass(clazz);
33                 return (clazz);
34             }
35         }
36         // (2) delegateLoad == false 或者 parent加载失败,调用自身的加载机制
37         clazz = findClass(name);
38         if (clazz != null) {
39             if (resolve) resolveClass(clazz);
40             return (clazz);
41         }
42         // (3) 自己加载失败,则请求parent代理加载
43 
44         if (!delegateLoad) {
45             ClassLoader loader = parent;
46             if (loader == null)
47                 loader = system;
48             clazz = loader.loadClass(name);
49             if (clazz != null) {
50                 return (clazz);
51             }
52         }
53         throw new ClassNotFoundException(name);
54     }

总结一下:
首先findLoadedClass0()和findLoadedClass()分别从本地和父类加载器的缓存中查找当前要加载的类是否已经加载过了。之后为了避免上面提到的安全问题,Tomcat类加载器会将加载请求委派给系统类加载器。接下来根据delegate变量的设置,决定是先由自己加载,还是先由父类加载器去加载。

1、既然 Tomcat 不遵循双亲委派机制,那么如果我自己定义一个恶意的HashMap,会不会有风险呢?(阿里面试问题)
答: 显然不会有风险,如果有,Tomcat都运行这么多年了,那能不改进吗?
tomcat不遵循双亲委派机制,只是自定义的classLoader顺序不同,但顶层还是相同的,还是要去顶层请求classloader。

2、我们思考一下:Tomcat是个web容器, 那么它要解决什么问题?

  1. 一个web容器可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类库都是独立的,保证相互隔离。
  2. 部署在同一个web容器中相同的类库相同的版本可以共享。否则,如果服务器有10个应用程序,那么要有10份相同的类库加载进虚拟机,这是扯淡的。
  3. web容器也有自己依赖的类库,不能于应用程序的类库混淆。基于安全考虑,应该让容器的类库和程序的类库隔离开来。
  4. web容器要支持jsp的修改,我们知道,jsp 文件最终也是要编译成class文件才能在虚拟机中运行,但程序运行后修改jsp已经是司空见惯的事情,否则要你何用? 所以,web容器需要支持 jsp 修改后不用重启。

3、Tomcat 如果使用默认的类加载机制行不行?
答案是不行的。为什么?我们看:
第一个问题,如果使用默认的类加载器机制,那么是无法加载两个相同类库的不同版本的,默认的累加器是不管你是什么版本的,只在乎你的全限定类名,并且只有一份。
第二个问题,默认的类加载器是能够实现的,因为他的职责就是保证唯一性。
第三个问题和第一个问题一样。
我们再看第四个问题,我们想我们要怎么实现jsp文件的热替换,jsp 文件其实也就是class文件,那么如果修改了,但类名还是一样,类加载器会直接取方法区中已经存在的,修改后的jsp是不会重新加载的。那么怎么办呢?我们可以直接卸载掉这jsp文件的类加载器,所以你应该想到了,每个jsp文件对应一个唯一的类加载器,当一个jsp文件修改了,就直接卸载这个jsp类加载器。重新创建类加载器,重新加载jsp文件。

4、如果tomcat 的 Common ClassLoader 想加载 WebApp ClassLoader 中的类,该怎么办?
看了前面的关于破坏双亲委派模型的内容,我们心里有数了,我们可以使用线程上下文类加载器实现,使用线程上下文加载器,可以让父类加载器请求子类加载器去完成类加载的动作。

5、为什么java文件放在Eclipse/IDEA中的src文件夹下会优先jar包中的class?
tomcat类加载机制的理解,就不难明白。因为Eclipse/IDEA中的src文件夹中的文件java以及webContent中的JSP都会在tomcat启动时,被编译成class文件放在 WEB-INF/class 中。
而Eclipse/IDEA外部引用的jar包,则相当于放在 WEB-INF/lib 中。
因此肯定是 java文件或者JSP文件编译出的class优先加载。

沙箱安全机制

保证程序安全
保护Java原生的JDK代码

沙箱机制就是将Java代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问。
通过这样的措施来保证对代码的有限隔离,防止对本地系统造成破坏。

JDK9 双亲委派机制变化

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

相关文章:

【JVM】总结篇-类的加载篇之 类的加载器 和ClassLoader分析

文章目录 类的加载器ClassLoader自定义类加载器双亲委派机制概念源码分析优势劣势如何打破Tomcat 沙箱安全机制JDK9 双亲委派机制变化 类的加载器 获得当前类的ClassLoader clazz.getClassLoader() 获得当前线程上下文的ClassLoader Thread.currentThread().getContextClassLoa…...

怎样修改el-table主题样式

起因&#xff1a;el-table有主题样式&#xff0c;部分需要单独设置 环境&#xff1a;ideanodejs插件谷歌浏览器 第一步&#xff1a;找到scss文件&#xff1a; 谷歌浏览器打开表格页面&#xff0c;ctrlshifti打开开发者工具&#xff0c;点击后鼠标移动到表格单元格上单击一下…...

MySQL(二)MySQL DDL数据库定义语言

1. MySQL DDL数据库定义语言 1.1. MySQL定义语言 进入MySQL mysql -u root -p(回车后输入密码&#xff0c;即可进入mysq1)1.1.1. 数据库操作 &#xff08;1&#xff09;查看数据库 mysql>show databases;注:MySQL语句分隔符为“&#xff1b;”   mysql库很重要它里面有…...

Spring Boot 项目启动报 NoClassDefFoundError 异常的原因分析与解决方案 - jackson 版本不一致

目录 报错: 问题分析&#xff1a; 解决方案&#xff1a; 方案 1&#xff1a;对 Jackson 版本进行统一 方案 2&#xff1a;升级 Springfox 版本 方案 3&#xff1a;替换 Springfox 为 springdoc-openapi&#xff08;推荐&#xff09; 方案 4&#xff1a;排除冲突的 Jack…...

原型与原型链

什么是原型&#xff08;对象&#xff09; 在JavaScript中&#xff0c;每个对象都具有一个原型对象prototype&#xff0c;目的是&#xff1a;利用原型对象实现在同一原型链中的原型方法共享 在理解原型对象前&#xff0c;需要先了解什么是构造函数 构造函数 用来初始化对象的…...

【Linux】信号处理

一、Linux系统信号 1、常见的系统信号 常见的Linux系统信号 信号值描述1SIGHUP挂起&#xff08;hang up&#xff09;进程2SIGINT中断进&#xff08;interrupt&#xff09;程3SIGQUIT停止&#xff08;stop&#xff09;进程9SIGKILL无条件终止&#xff08;terminate&#xff09;…...

5个不同类型的mysql数据库安装

各种社区版本下载官方地址&#xff1a;MySQL :: MySQL Community Downloads 一、在线YUM仓库&#xff08;Linux&#xff09; 选择 MySQL Yum Repository 选择对应版本下载仓库安装包&#xff08;No thanks, just start my download.&#xff09; 下载方法1&#xff1a;下载到本…...

python学习笔记—12—布尔类型、if语句

1. 布尔类型 (1) 定义 (2) 比较运算符 (3) 代码演示 1. 手动定义 bool_1 True bool_2 False print(f"bool_1的内容是&#xff1a;{bool_1}, 类型是&#xff1a;{type(bool_1)}") print(f"bool_2的内容是&#xff1a;{bool_2}, 类型是&#xff1a;{type(bool…...

分数阶傅里叶变换代码 MATLAB实现

function Faf myfrft(f, a) %分数阶傅里叶变换函数 %输入参数&#xff1a; %f&#xff1a;原始信号 %a&#xff1a;阶数 %输出结果&#xff1a; %原始信号的a阶傅里叶变换N length(f);%总采样点数 shft rem((0:N-1)fix(N/2),N)1;%此项等同于fftshift(1:N)&#xff0c;起到翻…...

《数据结构》期末考试测试题【中】

《数据结构》期末考试测试题【中】 21.循环队列队空的判断条件为&#xff1f;22. 单链表的存储密度比1&#xff1f;23.单链表的那些操作的效率受链表长度的影响&#xff1f;24.顺序表中某元素的地址为&#xff1f;25.m叉树第K层的结点数为&#xff1f;26. 在双向循环链表某节点…...

openwrt 清缓存命令行

一、查看缓存 &#xff1a; free -m 二、清缓存&#xff1a;echo 3 > /proc/sys/vm/drop_caches  三、详解。 释放物理页缓存 echo 1 > /proc/sys/vm/drop_caches 释放可回收的slab对象&#xff0c;包含inode and dentry echo 2 > /proc/sys/vm/drop_caches 同时…...

RP2K:一个面向细粒度图像的大规模零售商品数据集

这是一种用于细粒度图像分类的新的大规模零售产品数据集。与以往专注于相对较少产品的数据集不同&#xff0c;我们收集了2000多种不同零售产品的35万张图像&#xff0c;这些图像直接在真实的零售商店的货架上拍摄。我们的数据集旨在推进零售对象识别的研究&#xff0c;该研究具…...

.NET Core FluentAPI

目录 约定配置 主要规则 两种配置方式 Data Annotation Fluent API Fluent API配置 Fluent API众多方法 选择 约定配置 主要规则 表名采用DbContext中的对应的DbSet的属性名。数据表列的名字采用实体类属性的名字&#xff0c;列的数据类型采用和实体类属性类型最兼容…...

【C++数据结构——查找】顺序查找(头歌实践教学平台习题)【合集】

目录&#x1f60b; 任务描述 相关知识 一、根据输入数据建立顺序表 二、顺序表的输出 三、顺序查找算法 测试说明 通关代码 测试结果 任务描述 本关任务&#xff1a;实现顺序查找的算法 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a; 根据输入数据建立…...

HTTP Scheme 通常指的是在 URL 中用于指定使用 HTTP 协议的方案(scheme)

HTTP Scheme 通常指的是在 URL 中用于指定使用 HTTP 协议的方案&#xff08;scheme&#xff09;。URL&#xff08;统一资源定位符&#xff09;中的 scheme 部分指明了访问资源所使用的协议。对于 HTTP&#xff0c;有两个主要的 scheme&#xff1a; - **http**&#xff1a;表示…...

基于Matlab的变压器仿真模型建模方法(13):单相升压自耦变压器的等效电路和仿真模型

1.单相升压自耦变压器的基本方程和等效电路 单相升压自耦变压器的接线原理图如图1所示。在建立自耦变压器的基本方程时,仍然把它看成是从双绕组变压器演变而来。在图1中,设节点a到节点b部分的绕组的匝数为,对应于双绕组变压器的原边绕组;节点c到节点a部分的绕组的绕组匝数为…...

【Vue.js】监听器功能(EventListener)的实际应用【合集】

目录 &#x1f914;在实际开发过程中&#xff0c;我遇到了一个颇为棘手的小问题 &#x1f60b;解决这个小问题 问题出现的原因剖析 解决方法阐述 问题成功解决&#xff01;​ &#x1f4d6;相关知识总结 基本概念 使用方法 实际应用场景 &#x1f914;在实际开发过程中…...

【Shell脚本】Docker构建Java项目,并自动停止原镜像容器,发布新版本

本文简述 经常使用docker部署SpringBoot 项目&#xff0c;因为自己的服务器小且项目简单&#xff0c;因此没有使用自动化部署。每次将jar包传到服务器后&#xff0c;需要手动构建&#xff0c;然后停止原有容器&#xff0c;并使用新的镜像启动&#xff0c;介于AI时代越来越懒的…...

【iOS Swift Moya 最新请求网络框架封装通用】

【iOS Swift Moya 最新请求网络框架封装通用】 前言框架结构1.API定义&#xff08;TargetType&#xff09;2. 配置MoyaProvider3. 网络管理器4. 使用示例注意事项进一步优化 前言 设计一个基于Moya的网络请求框架&#xff0c;可以提供灵活的网络请求管理&#xff0c;例如设置请…...

前端批量下载文件

背景 文件管理页面&#xff0c;后端只提供了一个根据 file_path 和 file_name 参数下载文件的API接口。产品需要支持用户多选之后的批量下载功能。 技术实现 基础代码 先调用下载接口&#xff0c;获取到二进制的文件流&#xff0c;然后通过 a 标签完成下载。 // return [r…...

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

AI语音助手的Python实现

引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...

Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么&#xff1f;它的作用是什么&#xff1f; Spring框架的核心容器是IoC&#xff08;控制反转&#xff09;容器。它的主要作用是管理对…...

在Zenodo下载文件 用到googlecolab googledrive

方法&#xff1a;Figshare/Zenodo上的数据/文件下载不下来&#xff1f;尝试利用Google Colab &#xff1a;https://zhuanlan.zhihu.com/p/1898503078782674027 参考&#xff1a; 通过Colab&谷歌云下载Figshare数据&#xff0c;超级实用&#xff01;&#xff01;&#xff0…...

【版本控制】GitHub Desktop 入门教程与开源协作全流程解析

目录 0 引言1 GitHub Desktop 入门教程1.1 安装与基础配置1.2 核心功能使用指南仓库管理日常开发流程分支管理 2 GitHub 开源协作流程详解2.1 Fork & Pull Request 模型2.2 完整协作流程步骤步骤 1: Fork&#xff08;创建个人副本&#xff09;步骤 2: Clone&#xff08;克隆…...

【PX4飞控】mavros gps相关话题分析,经纬度海拔获取方法,卫星数锁定状态获取方法

使用 ROS1-Noetic 和 mavros v1.20.1&#xff0c; 携带经纬度海拔的话题主要有三个&#xff1a; /mavros/global_position/raw/fix/mavros/gpsstatus/gps1/raw/mavros/global_position/global 查看 mavros 源码&#xff0c;来分析他们的发布过程。发现前两个话题都对应了同一…...