SPI机制是如何规避双亲委派机制的?

这篇具有很好参考价值的文章主要介绍了SPI机制是如何规避双亲委派机制的?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

SPI是如何规避双亲委派机制的?

1、何为双亲委派机制?

双亲委派机制是什么?

双亲委派机制指的是Java中类加载机制的特性。

双亲委派机制是作用于什么地方?

双亲委派机制主要作用于类加载的时候。

类加载器

首先需要清晰的知道,双亲委派机制指的是类加载的特性。在了解其特性之前,我们需要先了解类加载器有哪些(不考虑自定义加载器的情况)。

加载器 解释
BootStrap加载器 最为顶层的加载器,负责加载System.getProperty("sun.boot.class.path")下的Jar包,主要是jre\lib目录下的内容。该类加载器为C实现,在Java中无法获取
Ext类加载器 扩展类加载器,负责加载System.getProperty("java.ext.dirs")下的Jar包,主要是jre\lib\ext下的内容。在Java中对应ExtClassLoader(注意此处以jdk8为例,jdk11中有所改变)。
App类加载器 应用类加载器,负责加载System.getProperty("java.class.path")下的Jar包,主要是自身程序加载的包。在Java中对应AppClassLoader(注意此处以jdk8为例,jdk11中有所改变)。

类加载器之间的结构如何:

SPI机制是如何规避双亲委派机制的?

可以看出来,App类加载器是最小的一层,也是我们开发用户接触最多的一层,越往上加载的类就越核心。

双亲委派机制是什么样的结构?

双亲委派机制其实就是描述类加载器加载类的顺序及其特点。

我们开发者需要去加载类的场景每天都在接触,例如在代码中new Car(我们自己的类),此时就是需要去加载这个类。在触发加载类的时候,开发者处于加载器的最低层。那么就可以看作成:App类加载器去加载Car这个类

而实际上的加载顺序是这样的:

SPI机制是如何规避双亲委派机制的?

App类加载器--通知-->Ext类加载器--通知-->BootStrap类加载器

BootStrap类加载器--发现找不到该类,则向下返回-->Ext类加载器--发现找不到该类,继续向下返回-->App类加载器(当前类加载器如果找不到该类则抛出异常,否则加载成功)

上述为双亲委派机制加载类时的顺序,其特点为先向上通知到最顶层,再由最顶层往下尝试,直到成功加载或到达发送加载类请求的加载器。

这种加载特点最大的作用如下:

安全性:由于Java核心类均有BootStrap加载器、Ext加载器去加载,再加上这种加载类的特性,可以有效防止Java核心类被篡改,正常的Java应用无法修改核心类实现。不仅可以应用在Java核心类中,当我们的应用是插件式时,此方式也可以防止插件中篡改主程序的代码。

2、SPI是什么?

上面我们讲述了双亲委派机制,现在要讲述SPI。

SPI是什么?

SPI(Service Provider Interface)是JDK内置的一种服务提供发现机制,可以用来启用框架扩展和替换组件,主要是被框架的开发人员使用。

例如数据库驱动中java.sql.Driver接口,其他不同厂商可以针对同一接口做出不同的实现,MySQL和PostgreSQL都有不同的实现提供给用户,而Java的SPI机制可以为某个接口寻找服务实现。Java中SPI机制主要思想是将功能实现剔除到程序之外,这针对与模块化解耦有很大的作用。

例如下图:

SPI机制是如何规避双亲委派机制的?

除数据库驱动以外,例如日志框架、Dubbo等也涉及到SPI机制。

在上图中,例如当我们需要具体Driver实现的时候,直接通过JDK的API:

ServiceLoader<java.sql.Driver> serviceLoader = ServiceLoader.load(java.sql.Driver.class);
for (java.sql.Driver driver : serviceLoader) {
     // mysql、pg、oracle、db2等
}

注意,SPI机制存在一些约定,这些约束如下:

  1. 三方接口需在META-INF/services/${interface_name}文件中列举实现类,每一个实现类为一行。例如数据库这,那么示例如下:

    META-INF/services/java.sql.Driver

com.mysql.cj.jdbc.Driver
org.postgresql.Driver
oracle.jdbc.OracleDriver
com.ibm.db2.jcc.DB2Driver

​ 2.定义的实现类必须实现对应接口

​ 3.实现类必须提供无参构造器

3、为什么说SPI规避了双亲委派机制?

​ 注意,我们前面说了双亲委派机制中,加载器会往上层加载器递交加载请求,我们已知java.util.ServiceLoader的类加载器为BootStrap加载器。此加载器已经是最顶层,无更加上层的加载器。而按照加载器职责的约定,ServiceLoader所属类加载器的职责是加载jdk核心类,其是无法加载到用户的类。例如下图:

SPI机制是如何规避双亲委派机制的?

​ 现在的问题是:既然ServiceLoader的类加载器是最顶层的,其加载职责不负责我们自己的类,那么它是如何加载到类似JDBC这种实现类的呢?

附:ServiceLoader的类加载器是BootStrap类加载器,在程序中是无法获取到该类的类加载器的。

4、SPI是如何规避双亲委派机制的?

​ 要搞清楚这个问题的原因,得先确认我们使用SPI的入口:

ServiceLoader<Xxxx> serviceLoader = ServiceLoader.load(Xxxx.class);

​ 进入该方法,寻找其实现的方式:

java.util.ServiceLoader#load(java.lang.Class)

SPI机制是如何规避双亲委派机制的?

​ 注意此处获取了当前线程的类加载器,而在线程中调用该类方法的是我们用户自己。那么这里就理解为获取到了用户的类加载器。

SPI机制是如何规避双亲委派机制的?

​ 再往该方法中查找,找到该段代码:

java.util.ServiceLoader#ServiceLoader

SPI机制是如何规避双亲委派机制的?

​ 注意该段代码中,cl为上一步获取到的类加载器,如果发现类加载器不存在,会再次获取系统默认加载器,这个系统默认加载器在常规情况下是用于加载启动类的加载器(jdk注释中解释),而启动类则是我们用户自己定义的类,这里毋庸置疑也会是应用类加载器。

​ 从上面的代码中我们总结出来,ServiceLoader获取了我们的应用类加载器,至此load方法入口基本上没有其他内容可以细看。

​ 为减轻文章阅读压力,直接跳转到该方法

java.util.ServiceLoader.LazyIterator#nextService

SPI机制是如何规避双亲委派机制的?

​ 注意这里的loader是我们前面获取到的应用类加载器,这个方法中是获取到了具体需要实例化的实现类,即将对其进行实例化, 在这之前需要先获取到Class,这里使用Class.forName(class, false, ClassLoader)方法,这个方法的含义是使用指定的类加载器去加载指定的类。既然这里的类加载器是应用类加载器,那么类加载顺序自然就又回到了应用类加载器-->扩展类加载器-->BootStrap类加载器-->扩展类加载器-->应用类加载器,能加载到我们想要的类也就不奇怪了。

​ 看到这里也就明白了为什么使用SPI仍然能正常加载类了。

​ SPI的加载机制看起来虽然方面,但仍然有缺点:

1. 无法实现动态加载、卸载的效果,只有最简单的加载三方类的实现。
1. 由于实现原因,实现类必须提供无参构造器,局限性和扩展性很低

​ 综合来说,SPI简单但局限性大,项目中能接受这些缺点就可以放心使用,如接受不了则可以模拟SPI机制自行实现一套加载机制,自己实现起来扩展性和局限性肯定是原生SPI不能比的。

​ 本次内容结束,如发现内容错误请留言,会尽快改正。文章来源地址https://www.toymoban.com/news/detail-680229.html

到了这里,关于SPI机制是如何规避双亲委派机制的?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 类加载 - 双亲委派机制详解

    启动类加载器(Bootstrap Class Loader):它是Java虚拟机的一部分,负责加载Java核心类库,如java.lang包中的类。它是最顶层的类加载器,由C++实现,不是Java类。 扩展类加载器(Extension Class Loader):它负责加载Java的扩展类库,位于JRE的lib/ext目录下。它是由Java编写的类加载器,是

    2024年02月12日
    浏览(39)
  • JVM类加载和双亲委派机制

    当我们用java命令运行某个类的main函数启动程序时,首先需要通过类加载器把类加载到JVM,本文主要说明类加载机制和其具体实现双亲委派模式。 类加载过程 : 类加载的过程是将类的字节码加载到内存中的过程,主要包括:加载--链接--初始化,其中链接还包括验证、准备、

    2024年02月09日
    浏览(46)
  • JVM:双亲委派机制&类加载器

    Java运行时环境有一个 java.lang 包,里面有一个 ClassLoader 类 我们自定义一个 String 类在 java.lang 包下,下面的main方法报错。原因是: 根据双亲委派机制,会向上找先是找到了 应用程序加载器(appClassLoader) ,然后向上找 扩展类加载器(ExtClassLoader) ,最后找 根类加载器(Boot Strap

    2024年02月01日
    浏览(37)
  • 【Java】深入了解双亲委派机制(常说的类加载机制)

    ava虚拟机(JVM)的类加载机制是Java应用中不可或缺的一部分。本文将详细介绍JVM的双亲委派机制,并阐述各关键点。 双亲委派机制(Parent-Delegate Model)是Java类加载器中采用的一种类加载策略。该机制的核心思想是:如果一个类加载器收到了类加载请求,默认先将该请求委托

    2024年02月04日
    浏览(36)
  • 【JAVA基础】JVM之类加载--双亲委派机制

    1. 类加载的过程 描述: 我们写的 .java 文件通过编译成字节码文件 .class 文件,然后再通过我们的类加载器:Class Loader,反射以后,类模板存在方法区,把实例化的对象存在堆里; 看图: 对象的hashcode值 解释: 从图中我们可以看出,从同一个类模板new出来三个对象(实例化

    2024年01月23日
    浏览(45)
  • JVM(类的加载与ClassLoader、双亲委派机制)

    类在内存中完整的生命周期: 加载--使用--卸载 。其中加载过程又分为: 装载、链接、初始化 三个阶段。 当程序主动使用某个类时,如果该类还未被加载到内存中,系统会通过加载、链接、初始化三个步骤来对该类进行初始化。如果没有意外,JVM将会连续完成这三个步骤,

    2024年02月03日
    浏览(48)
  • 解析Java类加载的运行机制和双亲委派模型

    类加载形如把.class文件,加载到内存中,得到类对象这样的过程 类加载一共分为五个步骤: 加载 验证 准备 解析 初始化 加载 类加载的第一步是将编译好的 Java 类的字节码文件加载到 Java 虚拟机(JVM)中。类加载器会根据类的名称找到对应的字节码文件,并将其读取到内存

    2024年02月15日
    浏览(42)
  • 从类加载到双亲委派:深入解析类加载机制与 ClassLoader

    目录 在 Java 编程中,类加载是一个关键的技术点,它负责将类引入 Java 虚拟机(JVM)使得程序能够正确地加载、链接、初始化类;类加载的过程是 Java 程序执行的基础,它涉及从磁盘或网络上加载类的字节码,解析类的符号引用,最终将类加载到内存中供程序使用 类加载的

    2024年02月11日
    浏览(39)
  • 【JVM从入门到实战】(六)类加载器的双亲委派机制

    在Java中如何使用代码的方式去主动加载一个类呢? 方式1:使用Class.forName方法,使用当前类的类加载器去加载指定的类。 方式2:获取到类加载器,通过类加载器的loadClass方法指定某个类加载器加载。 双亲委派机制: 自底向上查找是否加载过,再由顶向下进行加载 面试题:

    2024年02月04日
    浏览(49)
  • 深入理解Java类加载机制中的双亲委派模型--根据源码探讨

    前言: 今天和大家探讨一道Java中经典的面试题,这道面试题经常出现在各个公司的面试中,本篇文章主要讲解 ava类加载机制中的双亲委派模型 的知识。该专栏比较适合刚入坑Java的小白以及准备秋招的大佬阅读。 如果文章有什么需要改进的地方欢迎大佬提出,对大佬有帮助

    2024年02月14日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包