SPI和类加载器

主要目的

SPI的本质是java的一种服务发现能力。
如果同时面临下面两个问题,就可以使用SPI
**0)AB模块使用不同的类加载器,且A模块的加载器无法加载B模块
1)A模块依赖B模块。
2)A模块又要先于B模块被类加载,或者不能确定两个模块的类加载顺序。

大部分场景其实根本不需要SPI。合理的类依赖可以避免这个问题。
SPI的本质思路很简单,就是使用线程上下文加载器去加载用到的类。SPI不是JVM提供的能力,而是JDK中提供的tricky但必要的能力。

一个简单的demo

具体的例子见
https://blog.csdn.net/qq_27292113/article/details/100324127

核心代码如下

  • 在核心jar中定义接口,并使用接口代码
  • 在外部jar中定义services
    在这里插入图片描述
  • 使用是是会选择第一个实现
    在这里插入图片描述
    通过ServiceLoader完成加载

类加载机制

实际应用

JAVA日志

启动时,我们可以看见,slf4j找到了多个SPI的实现。

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/gdl/.m2/repository/org/slf4j/slf4j-log4j12/1.6.1/slf4j-log4j12-1.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/gdl/.m2/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.10.0/log4j-slf4j-impl-2.10.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
SpringBoot
JDBC

java.sql.DriverManager类会在其static代码段执行

                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();

                /* Load these drivers, so that they can be instantiated.
                 * It may be the case that the driver class may not be there
                 * i.e. there may be a packaged driver with the service class
                 * as implementation of java.sql.Driver but the actual class
                 * may be missing. In that case a java.util.ServiceConfigurationError
                 * will be thrown at runtime by the VM trying to locate
                 * and load the service.
                 *
                 * Adding a try catch block to catch those runtime errors
                 * if driver not available in classpath but it's
                 * packaged as service and that service is there in classpath.
                 */
                try{
                    while(driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                } catch(Throwable t) {
                // Do nothing
                }
                return null;

注释上写的很清楚
DriverManager类会被Bootstrap类加载器加载。但是这个时候,他是在核心库中找不到具体实现模块的。所以,他必须先去寻找自己需要加载的模块,再完成本类的加载。

driver接口定义了如下方法
在这里插入图片描述
可以看到有诸多的实现。
在这里插入图片描述
简单而言,就是自动加载:DriverManager的静态代码块执行的时刻,使用该时刻当前线程类加载器加载java.sql.Driver文件,并用当前线程类加载器加载及、实例化、registerDriver

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
类加载器ServiceLoader是Java中的一个工具类,用于加载和实例化服务提供者接口的实现类。它的作用是在运行时动态地加载实现了某个接口的类,并创建其实例。ServiceLoader使用了线程上下文类加载器加载类,这样可以解决父类加载器无法加载类加载器加载的类的问题。\[1\]\[3\] 具体来说,ServiceLoader会使用当前类的类加载器加载其他类,如果一个类引用了另一个类,那么它的类加载器就会去加载被引用的类(前提是被引用的类尚未加载)\[2\]。而在某些情况下,高层提供了统一的接口让低层去实现,并且需要在高层加载或实例化底层的类时,就需要通过线程上下文类加载器来帮助高层的类加载器找到并加载该类\[3\]。 虽然我们可以直接使用getClassLoader获取系统类加载器加载对应的类,但是使用上下文类加载器更加方便,因为它可以在任何代码执行的线程中随时取出来使用\[3\]。所以在使用ServiceLoader时,我们可以使用线程上下文类加载器加载服务提供者接口的实现类。 #### 引用[.reference_title] - *1* [打破双亲委派之SPI、线程上下文类加载器、ServiceLoader](https://blog.csdn.net/weixin_45902285/article/details/121757550)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [【JVM类加载】线程上下文的类加载器及使用模式ServiceLoader(服务加载器)在SPI中的重要作用分析...](https://blog.csdn.net/qq_42261668/article/details/102607997)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值