java八股文面试[JVM]——如何打破双亲委派模型

  1. 双亲委派模型的第一次“被破坏”是重写自定义加载器的loadClass(),jdk不推荐。一般都只是重写findClass(),这样可以保持双亲委派机制.而loadClass方法加载规则由自己定义,就可以随心所欲的加载类,典型的打破双亲委派模型的框架和中间件tomcatosgi

  2. 双亲委派模型的第二次“被破坏”是ServiceLoader和Thread.setContextClassLoader()。即线程上下文类加载器(contextClassLoader)。双亲委派模型很好地解决了各个类加载器的基础类统一问题(越基础的类由越上层的加载器进行加载),基础类之所以被称为“基础”,是因为它们总是作为被调用代码调用的API。但是,如果基础类又要调用用户的代码,那该怎么办呢?线程上下文类加载器就出现了。

    1. SPI。这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个;如果在应用程序的全局范围内都没有设置过,那么这个类加载器默认就是应用程序类加载器。了有线程上下文类加载器,JNDI服务使用这个线程上下文类加载器去加载所需要的SPI代码,也就是父类加载器请求子类加载器去完成类加载动作,这种行为实际上就是打通了双亲委派模型的层次结构来逆向使用类加载器,已经违背了双亲委派模型,但这也是无可奈何的事情。Java中所有涉及SPI的加载动作基本上都采用这种方式,例如JNDI,JDBC,JCE,JAXB和JBI等。

    2. 线程上下文类加载器默认情况下就是AppClassLoader,那为什么不直接通过getSystemClassLoader()获取类加载器来加载classpath路径下的类的呢?其实是可行的,但这种直接使用getSystemClassLoader()方法获取AppClassLoader加载类有一个缺点,那就是代码部署到不同服务时会出现问题,如把代码部署到Java Web应用服务或者EJB之类的服务将会出问题,因为这些服务使用的线程上下文类加载器并非AppClassLoader,而是Java Web应用服自家的类加载器,类加载器不同。,所以我们应用该少用getSystemClassLoader()。总之不同的服务使用的可能默认ClassLoader是不同的,但使用线程上下文类加载器总能获取到与当前程序执行相同的ClassLoader,从而避免不必要的问题

  3. 双亲委派模型的第三次“被破坏”是由于用户对程序动态性的追求导致的,这里所说的“动态性”指的是当前一些非常“热门”的名词:代码热替换、模块热部署等,简答的说就是机器不用重启,只要部署上就能用

前言
比较两个类是否“相等”,前提是这两个类由同一个类加载器加载,
否则,即使这两个类来源于同一个Class 文件,被同一个虚拟机加载,
只要加载它们的类加载器不同,那么这两个类就必定不相等。
打破双亲委派

 如下是一个自定义的类加载器TestClassLoader,并重写了findClass和loadClass:

public class TestClassLoader extends ClassLoader {public TestClassLoader(ClassLoader parent) {super(parent);}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {// 1、获取class文件二进制字节数组byte[] data = null;try {System.out.println(name);String namePath = name.replaceAll("\\.", "\\\\");String classFile = "C:\\study\\myStudy\\ZooKeeperLearning\\zkops\\target\\classes\\" + namePath + ".class";ByteArrayOutputStream baos = new ByteArrayOutputStream();FileInputStream fis = new FileInputStream(new File(classFile));byte[] bytes = new byte[1024];int len = 0;while ((len = fis.read(bytes)) != -1) {baos.write(bytes, 0, len);}data = baos.toByteArray();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}// 2、字节码加载到 JVM 的方法区,// 并在 JVM 的堆区建立一个java.lang.Class对象的实例// 用来封装 Java 类相关的数据和方法return this.defineClass(name, data, 0, data.length);}@Overridepublic Class<?> loadClass(String name) throws ClassNotFoundException{Class<?> clazz = null;// 直接自己加载clazz = this.findClass(name);if (clazz != null) {return clazz;}// 自己加载不了,再调用父类loadClass,保持双亲委托模式return super.loadClass(name);}
}

测试:初始化自定义的类加载器,需要传入一个parent,指定其父类加载器,那就先指定为加载TestClassLoader的类加载器为TestClassLoader的父类加载器吧:

public static void main(String[] args) throws Exception {// 初始化TestClassLoader,被将加载TestClassLoader类的类加载器设置为TestClassLoader的parentTestClassLoader testClassLoader = new TestClassLoader(TestClassLoader.class.getClassLoader());System.out.println("TestClassLoader的父类加载器:" + testClassLoader.getParent());// 加载 DemoClass clazz = testClassLoader.loadClass("study.stefan.classLoader.Demo");System.out.println("Demo的类加载器:" + clazz.getClassLoader());
}

运行如下测试代码,发现报错了:
找不到java\lang\Object.class,我加载study.stefan.classLoader.Demo类和Object有什么关系呢?

转瞬想到java中所有的类都隐含继承了超类Object,加载study.stefan.classLoader.Demo,也会加载父类Object。Object和study.stefan.classLoader.Demo并不在同个目录,那就找到Object.class的目录(将jre/lib/rt.jar解压),修改TestClassLoader#findClass如下:
遇到前缀为java.的就去找官方的class文件。

运行测试代码:
还是报错了!!! 报错信息为:Prohibited package name: java.lang

看意思是java禁止用户用自定义的类加载器加载java.开头的官方类,也就是说只有启动类加载器BootstrapClassLoader才能加载java.开头的官方类。

得出结论,因为java中所有类都继承了Object,而加载自定义类study.stefan.classLoader.Demo,之后还会加载其父类,而最顶级的父类Object是java官方的类,只能由BootstrapClassLoader加载

跳过AppClassLoaderExtClassLoader
既然如此,先将study.stefan.classLoader.Demo交由BootstrapClassLoader加载即可
由于java中无法直接引用BootstrapClassLoader,所以在初始化TestClassLoader时,传入parent为null,也就是TestClassLoader的父类加载器设置为BootstrapClassLoader:

package com.stefan.DailyTest.classLoader;public class Test {public static void main(String[] args) throws Exception {// 初始化TestClassLoader,并将加载TestClassLoader类的类加载器// 设置为TestClassLoader的parentTestClassLoader testClassLoader = new TestClassLoader(null);System.out.println("TestClassLoader的父类加载器:" + testClassLoader.getParent());// 加载 DemoClass clazz = testClassLoader.loadClass("com.stefan.DailyTest.classLoader.Demo");System.out.println("Demo的类加载器:" + clazz.getClassLoader());}
}

双亲委派的逻辑在 loadClass,由于现在的类加载器的关系为TestClassLoader —>BootstrapClassLoader,所以TestClassLoader中无需重写loadClass。
运行测试代码:

成功了,Demo类由自定义的类加载器TestClassLoader加载的,双亲委派模型被破坏了。

如果不破坏双亲委派,那么Demo类处于classpath下,就应该是AppClassLoader加载的,所以真正破坏的是AppClassLoader这一层的双亲委派

一个比较完整的自定义类加载器

一般情况下,自定义类加载器都是继承URLClassLoader,具有如下类关系图:

tomcat是如何打破双亲委派的

Tomcat中可以部署多个web项目,为了保证每个web项目互相独立,所以不能都由AppClassLoader加载,所以自定义了类加载器WebappClassLoader,WebappClassLoader继承自URLClassLoader,重写了findClass和loadClass,并且WebappClassLoader的父类加载器设置为AppClassLoader。
WebappClassLoader.loadClass中会先在缓存中查看类是否加载过,没有加载,就交给ExtClassLoader,ExtClassLoader再交给BootstrapClassLoader加载;都加载不了,才自己加载;自己也加载不了,就遵循原始的双亲委派,交由AppClassLoader递归加载。

Web应用默认的类加载顺序是(打破了双亲委派规则):

先从JVM的BootStrapClassLoader中加载。
加载Web应用下/WEB-INF/classes中的类。
加载Web应用下/WEB-INF/lib/*.jap中的jar包中的类。
加载上面定义的System路径下面的类。
加载上面定义的Common路径下面的类。


如果在配置文件中配置了``,那么就是遵循双亲委派规则,加载顺序如下:

先从JVM的BootStrapClassLoader中加载。
加载上面定义的System路径下面的类。
加载上面定义的Common路径下面的类。
加载Web应用下/WEB-INF/classes中的类。
加载Web应用下/WEB-INF/lib/*.jap中的jar包中的类。


1 Tomcat对用户类库与类加载器的规划
在其目录结构下有三组目录(“/common/”、“/server/”、“/shared/”)可以存放Java类库,另外还可以加上Web应用程序本身的目录“/WEB-INF/”,一共4组,把Java类库放置在这些目录中的含义分别如下:

放置在/commom目录中:类库可被Tomcat和所有的Web应用程序共同使用
放置在/server目录中:类库可被Tomcat使用,对所有的Web应用程序都不可见
放置在/shared目录中:类库可被所有的Web应用程序所共同使用,但对Tomcat自己不可见
放置在/WebApp/WEB-INF目录中:类库仅仅可以被此Web应用程序使用,对Tomcat和其他Web应用程序都不可见
为了支持这套目录结构,并对目录里面的类库进行加载和隔离,Tomcat自定义了多个类加载器,这些类加载器按照经典的双亲委派模型来实现,所下图:

最上面的三个类加载器是JDK默认提供的类加载器,这三个加载器的的作用之前也说过,这里不再赘述了,而CommonClassLoader、CatalinaClassLoader、SharedClassLoader和WebAppClassLoader则是Tomcat自己定义的类加载器,他们分别加载/common/、/server/、/shared/和/WebApp/WEB-INF/中的Java类库。其中WebApp类加载器和jsp类加载器通常会存在多个实例每一个Web应用程序对应一个WebApp类加载器,每一个jsp文件对应一个Jsp类加载器

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

tomcat对于不同应用需要有不同的隔离环境
tomcat给每个应用都创建了一个WebApp ClassLoader类加载器
重写了load方法:不再向上查找,而是在本类查找不到后再向上。对于其他的需要共享的例如Redis,可以在上层Share ClassLoader中共享。

OSGI是如何打破双亲委派的

既然说到OSGI,就要来解释一下OSGi是什么,以及它的作用

OSGi(Open Service Gateway Initiative):是OSGi联盟指定的一个基于Java语言的动态模块化规范,这个规范最初是由Sun、IBM、爱立信等公司联合发起,目的是使服务提供商通过住宅网管为各种家用智能设备提供各种服务,后来这个规范在Java的其他技术领域也有不错的发展,现在已经成为Java世界中的“事实上”的模块化标准,并且已经有了Equinox、Felix等成熟的实现。OSGi在Java程序员中最著名的应用案例就是Eclipse IDE

OSGi中的每一个模块(称为Bundle)与普通的Java类库区别并不大,两者一般都以JAR格式进行封装,并且内部存储的都是Java Package和Class。但是一个Bundle可以声明它所依赖的Java Package(通过Import-Package描述),也可以声明他允许导出发布的Java Package(通过Export-Package描述)。在OSGi里面,Bundle之间的依赖关系从传统的上层模块依赖底层模块转变为平级模块之间的依赖(至少外观上如此),而且类库的可见性能得到精确的控制,一个模块里只有被Export过的Package才可能由外界访问,其他的Package和Class将会隐藏起来。除了更精确的模块划分和可见性控制外,引入OSGi的另外一个重要理由是,基于OSGi的程序很可能可以实现模块级的热插拔功能,当程序升级更新或调试除错时,可以只停用、重新安装然后启动程序的其中一部分,这对企业级程序开发来说是一个非常有诱惑性的特性

OSGi之所以能有上述“诱人”的特点,要归功于它灵活的类加载器架构。OSGi的Bundle类加载器之间只有规则,没有固定的委派关系。例如,某个Bundle声明了一个它依赖的Package,如果有其他的Bundle声明发布了这个Package,那么所有对这个Package的类加载动作都会为派给发布他的Bundle类加载器去完成。不涉及某个具体的Package时,各个Bundle加载器是平级关系,只有具体使用某个Package和Class的时候,才会根据Package导入导出定义来构造Bundle间的委派和依赖

另外,一个Bundle类加载器为其他Bundle提供服务时,会根据Export-Package列表严格控制访问范围。如果一个类存在于Bundle的类库中但是没有被Export,那么这个Bundle的类加载器能找到这个类,但不会提供给其他Bundle使用,而且OSGi平台也不会把其他Bundle的类加载请求分配给这个Bundle来处理

一个例子:假设存在BundleA、BundleB、BundleC三个模块,并且这三个Bundle定义的依赖关系如下:

BundleA:声明发布了packageA,依赖了java.*的包
BundleB:声明依赖了packageA和packageC,同时也依赖了Java.*的包
BundleC:声明发布了packageC,依赖了packageA
那么,这三个Bundle之间的类加载器及父类加载器之间的关系如下图:

由于没有涉及到具体的OSGi实现,所以上图中的类加载器没有指明具体的加载器实现,只是一个体现了加载器之间关系的概念模型,并且只是体现了OSGi中最简单的加载器委派关系。一般来说,在OSGi中,加载一个类可能发生的查找行为委派关系会比上图中显示的复杂,类加载时的查找规则如下:

以java.*开头的类,委派给父类加载器加载
否则,委派列表名单内的类,委派给父类加载器加载
否则,Import列表中的类,委派给Export这个类的Bundle的类加载器加载
否则,查找当前Bundle的ClassPath,使用自己的类加载器加载
否则,查找是否在自己的Fragment Bundle中,如果是,则委派给Fragment bundle的类加载器加载
否则,查找Dynamic Import列表的Bundle,委派给对应Bundle的类加载器加载
否则,查找失败
从之前的图可以看出,在OSGi里面,加载器的关系不再是双亲委派模型的树形架构,而是已经进一步发展成了一种更复杂的、运行时才能确定的网状结构

相关面试题:一个类的静态块是否可能被执行两次

一个自于网易面试官的一个问题,一个类的静态块是否可能被执行两次。

答案:如果一个类,被两个 osgi的bundle加载, 然后又有实例被初始化,其静态块会被执行两次

什么是SPI 机制

Spi 机制加载第三方扩展的jar包类初始化。
mysql, dubbo rpc


SPi机制的原理:
java SPI全称Service Provider Interface 。是java 提供的一套用来被第三方实现的API,他可以用来启用框架扩展和替换组件。实际上是基于接口编程+策略模式+配置文件 组合实现的动态加载机制

JDBC

原本的JDBC: Class.forName(“DriverName”) 是通过调用Driver中静态代码块中的将Driver注册

public class Driver extends NonRegisteringDriver implements java.sql.Driver {public Driver() throws SQLException {}static {try {DriverManager.registerDriver(new Driver());} catch (SQLException var1) {throw new RuntimeException("Can't register driver!");}}
}

使用SPI的JDBC:
在mysql的jar包中的META-INF/services/java.sql.Driver 文件中指明当前使用的Driver,然后可以直接调用

Connection conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb?characterEncoding=GBK", "root", "");

问题是 :一个类的加载器和调用他的加载器相同
这里调用的是 bootstrap类加载器,无法加载到子类厂商中的类


方法:使用线程上下文加载器

public class DriverManager {static {loadInitialDrivers();println("JDBC DriverManager initialized");}private static void loadInitialDrivers() {//省略代码//这里就是查找各个sql厂商在自己的jar包中通过spi注册的驱动ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);Iterator<Driver> driversIterator = loadedDrivers.iterator();try{while(driversIterator.hasNext()) {driversIterator.next();}} catch(Throwable t) {// Do nothing}//省略代码}
}

使用Thread类的 getContextClassLoader

    public static <S> ServiceLoader<S> load(Class<S> service) {ClassLoader cl = Thread.currentThread().getContextClassLoader();return ServiceLoader.load(service, cl);}public static <S> ServiceLoader<S> load(Class<S> service,ClassLoader loader){return new ServiceLoader<>(service, loader);}

整个mysql的驱动加载过程:

第一,获取线程上下文类加载器,从而也就获得了应用程序类加载器(也可能是自定义的类加载器)
第二,从META-INF/services/java.sql.Driver文件中获取具体的实现类名“com.mysql.jdbc.Driver”
第三,通过线程上下文类加载器去加载这个Driver类,从而避开了双亲委派模型的弊端

 SPI参考:39 如何破坏双亲委派机制原则 - 简书

知识来源:

JVM问题(一) -- 如何打破双亲委派模型_如何打破双亲委派机制_leo_messi94的博客-CSDN博客

打破双亲委派的几种办法_破坏双亲委派_hhpub的博客-CSDN博客

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

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

相关文章

数学建模--一维插值法的多种插值方式的Python实现

目录 1.算法流程步骤 2.算法核心代码 3.算法效果展示 1.算法流程步骤 #算法的核心就是利用scipy中的interpolate来完成工作 #一共是5种一维插值算法形式: #插值方法&#xff1a;1.阶梯插值 2.线性插值 3.2阶样条插值 4.3阶样条插值 #"nearest"阶梯插值 #"zero&…

lv3 嵌入式开发-7 linux shell脚本编程(分支语句、循环语句)

目录 1 分支语句 2 多路分支语句 3 for的用法 4 while的用法 5 循环控制语句 6 练习 1 分支语句 语法结构: if 表达式then 命令表fi 如果表达式为真, 则执行命令表中的命令; 否则退出if语句, 即执行fi后面的语句。 if和fi是条件语句的语句括号, 必须成对使用; …

LinkedList(4):多线程LinkedList 不安全情况

多线程不安全演示&#xff0c;线程越多&#xff0c;现象越明显&#xff0c;这边只启了四个线程。 package com.example.demo;import java.util.LinkedList; import java.util.UUID;public class LInkedListThread {public static void main(String[] args) {final LinkedList&…

Notepad++下载安装

自己在 找Notepad发现网上的网址参差不齐&#xff0c;自己找到的一个不错下载链接见文末&#xff01; Notepad 是一个免费的代码编辑器&#xff0c;专为在微软 Windows 环境下使用。它是一个开源项目&#xff0c;采用 GPL 许可证&#xff0c;并使用 C 编程语言结合 Win32 API 和…

ZLMeidaKit在Windows上启动时:计算机中丢失MSVCR110.dll,以及rtmp推流后无法转换为flv视频流解决

场景 ZLMediaKit在Windows上实现Rtmp流媒体服务器以及模拟rtmp推流和http-flv拉流播放&#xff1a; ZLMediaKit在Windows上实现Rtmp流媒体服务器以及模拟rtmp推流和http-flv拉流播放_zlm流媒体服务器_霸道流氓气质的博客-CSDN博客 按照以上教程启动MediaServer.exe时提示&am…

深入浅出AXI协议(4)——猝发传输

一、前言 在之前的文章中&#xff0c;我们着重介绍了关于AXI4的握手协议它可以使得传输的双方都可以自如地控制传输的速率&#xff0c;我们主要介绍了握手协议出现的3种可能情况。然后对于AXI4交易通信的握手信号的关系做出了介绍&#xff1a;&#xff08;1&#xff09;在AXI4互…

【webrtc】接收/发送的rtp包、编解码的VCM包、CopyOnWriteBuffer

收到的rtp包RtpPacketReceived 经过RtpDepacketizer 解析后变为ParsedPayloadRtpPacketReceived 分配内存,执行memcpy拷贝:然后把 RtpPacketReceived 给到OnRtpPacket 传递:uint8_t* media_payload = media_packet.AllocatePayload(rtx_payload.size());RTC

ExpressLRS开源代码之框架结构

ExpressLRS开源代码之框架结构 1. 源由2. Arduino应用框架3. ExpressLRS应用框架4. 硬件设计框架4.1 单天线4.2 双天线单PA4.3 双天线双PA 5. 应用软件设计6. 参考资料 1. 源由 最近为了理解《ExpressLRS开源之基本调试数据含义》&#xff0c;做了一些源代码的研读。 概念、文…

二进制搭建kubernetes(K8S)

二进制搭建kubernetes&#xff08;K8S&#xff09; 一、常见的K8S部署方式1.Minikube2.Kubeadmin3.二进制安装部署 二、二进制搭建K8S(单台master)1.部署架构规划2.系统初始化配置3.部署 docker引擎4.部署 etcd 集群4.部署 Master 组件5.部署 Worker Node 组件6.部署网络组件 三…

数据可视化工具中的显眼包:奥威BI自带方案上阵

根据经验来看&#xff0c;BI数据可视化分析项目是由BI数据可视化工具和数据分析方案两大部分共同组成&#xff0c;且大多数时候方案都需从零开始&#xff0c;反复调整&#xff0c;会耗费大量时间精力成本。而奥威BI数据可视化工具别具匠心&#xff0c;将17年经验凝聚成标准化、…

【juc】ReentrantReadWriteLock之缓存(仅当学习)

目录 一、说明二、代码示例2.1 pom依赖2.2 示例代码2.3 实体类 三、示例截图 一、说明 1.针对于读多写少的情况 2.先查缓存&#xff0c;没有再去查库 二、代码示例 2.1 pom依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"h…

文心一言api接入如何在你的项目里使用文心一言

文心一言api接入在项目里接入文心一言 一、百度文心一言API二、使用步骤1、接口2、请求参数3、请求参数示例4、接口 返回示例 三、 如何获取appKey和uid1、申请appKey:2、获取appKey和uid 四、重要说明 一、百度文心一言API 基于百度文心一言语言大模型的智能文本对话AI机器人…

Linux代码初试__进度条

前言 在我们的日常生活中&#xff0c;进度条是十分常见的&#xff0c;比如在软件下载中&#xff0c;应用加载中等等~~~那么进度条有什么特点&#xff1f;他又如何实现。 下面我们将结合下面的图展开讲解 一、前置理论知识 1.1回车和换行的区别 在我们的日常生活中&#x…

Android Studio新版本New UI及相关设置丨遥遥领先版

1、前言 俗话说工欲善其事必先利其器嘛&#xff0c;工具用不好怎么行呢&#xff0c;借着Android Studio的更新&#xff0c;介绍一下新版本中的更新内容&#xff0c;以及日常开发中那些好用的设置。 2、关于新版本 2.1、最新正式版本 Android Studio Giraffe | 2022.3.1 Pat…

FreeRTOS中断与任务之间同步(Error:..\..\FreeRTOS\portable\RVDS\ARM_CM4F\port.c,422 )

前言&#xff1a; FreeRTOS中&#xff0c;中断需要注意几点&#xff1a; 何时使用中断&#xff1b;中断服务函数&#xff08;ISR&#xff09;要处理的数据量有多大&#xff0c;通常我们希望中断的切换越快越好&#xff0c;也就是说&#xff0c;ISR尽量采用耗时较少的处理方式…

YOLO目标检测——赛马数据集下载分享

目标检测赛马数据集在马匹竞赛、马匹健康监测、马匹行为研究、马匹安全监控和马匹图像检索等应用场景中具有广泛的应用潜力&#xff0c;可以为马匹产业的发展和管理提供有力支持 数据集点击下载&#xff1a;YOLO赛马数据集640图片标框.rar 更多数据集下载和效果展示&#x…

论文研读|生成式跨模态隐写发展综述

前言&#xff1a;本文介绍近5年来生成式跨模态隐写领域的相关工作。 相关阅读&#xff1a;生成式文本隐写发展综述 不同于文本隐写&#xff0c;跨模态隐写需要考虑不同模态间的相关性&#xff0c;常见的跨模态场景有&#xff1a;Image-to-Text&#xff08;如图像描述&#xff…

安全编程:初始化那些你忽略掉的东西

对于黑客来说&#xff0c;特权提升漏洞是令他感到非常兴奋的事情&#xff0c;而有时候这种漏洞的来源仅仅是因为开发者忘记将内存缓冲区中的垃圾数据进行初始化。此话怎讲&#xff1f; 我想&#xff0c;现在每个人都应该熟悉 SecureZeroMemory 函数的使用&#xff0c;它用来擦…

【TypeScript】一直提示 :无法重新声明块范围变量

【TypeScript】一直提示 &#xff1a;无法重新声明块范围变量 问题描述&#xff1a;在VSCode中编写ts代码时&#xff0c;编写保存完之后&#xff0c;通过tsc 文件名.ts编译就会看到变量名下面出现了红色的波浪线&#xff0c;提示的内容是无法重新声明块范围变量。 解决方法&am…

基于RabbitMQ的模拟消息队列之五——虚拟主机设计

文章目录 一、创建VirtualHost类二、初始化三、API1.创建交换机2.删除交换机3.创建队列4.删除队列5.创建绑定6.删除绑定7.发送消息转发规则 8.订阅消息1.消费者管理2.推送消息给消费者 3.添加一个消费者管理ConsumerManager9.确认消息 创建VirtualHost类。 1.串起内存和硬盘的数…