写点什么

服务发现机制 SPI 居然是破坏者!

用户头像
4ye
关注
发布于: 4 小时前

今天 4ye 来和小伙伴们分享下这个 SPI 机制啦~ ,SPI 的身影无处不在! 是热插拔的体现之一,更和双亲委派机制有点小关系,居然是个破坏者😜


前言

主要介绍下 Java 中的 SPI 机制 。


SpringbootSPI 机制 咱们在下文 Springboot自动装配中再说~ 😝 嘿嘿


至于 [[ dubboSPI 机制]],还没时间深入了解,简单知道了它的 SPI 的自适应扩展机制,以及下面这些扩展~(超级多扩展的~)🐷






冲冲冲

什么是 SPI 呢?

SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在 ClassPath 路径下的 META-INF/services 文件夹查找文件,自动加载文件里所定义的类(定义多少,加载多少)

它是 Java 提供的一套用来被第三方实现或者扩展的接口

JAVA SPI 实践

步骤


  1. 先定义一个接口和它的实现类

  2. 再在 resources 下添加 META-INF/services两个文件夹 在该文件夹中新建一个普通文本,名字为 接口的全名 ,并将 接口的实现类全名 添加到文本中,多个的话进行换行操作

  3. 最后在代码中调用 ServiceLoader.load 方法即可实现

项目结构图

spi 文件内容

源码如下

接口

public interface IJava4ye {
void say();
}
public class Java4yeImpl implements IJava4ye{
@Override public void say() { System.out.println(" 【java4ye】 杰伦 杰伦!"); }}
复制代码

测试类

public class SpiTest {
@Test public void spiTest(){ ServiceLoader<IJava4ye> load = ServiceLoader.load(IJava4ye.class);
for (IJava4ye iJava4ye : load) { iJava4ye.say(); } }
}
复制代码

结果


是不是非常的方便,还有 IOC 的感觉 哈哈😄

JDBC 实例

那么实践完,我们再来看看 JDBC 这个小案例😝



可以看到我们使用的 MySQLJDBC 包中就有这么一个例子~🐷


而这个 java.sql.Driver 文件中就定义了这么一个实现类~👇


com.mysql.cj.jdbc.Driver

Driver 源码


可以发现这里有一个静态代码块,随着类被加载,它会直接往 DriverManager 中注册这个 Driver 驱动器 。

那有什么作用呢?

简单看下这个 JDBC 的写法 ,可以发现,之前我们都是要手动去写这个


Class.forName 来加载这个驱动器,但是用了这个 SPI 后,那不就可以省略这行代码了吗~😄 哈哈🐷


以后我们都不用管厂商定义了啥,直接使用 DriverManager.getConnection(url, username, password); 就可以获取到这个连接了,代码中就不会出现这种硬编码~ 灵活多了🐷 (当然不开发框架或者自己多折腾好像也用不上呀😝 哈哈哈)


JAVA SPI 原理探索

疑惑

不知道小伙伴们实践完会不会有点小疑惑,这个 SPI 一定要用 ServiceLoader.load 方法去加载的吗?


4ye 也不知道是不是 哈哈,但是 DriverManager 中就是这么使用的😝


详细请看下图👇



破坏双亲委派机制

我们都知道在 Java 中有这个双亲委派机制 ,像上面 DriverManager 这种做法,其实是破坏了这个 双亲委派机制


因为这个实现类是属于第三方类库 mysql


而这个 Driver 接口是在jdk 自身的 lib --》 rt.jar 中的


它是由 根类加载器 BootstrapClassloader 加载的, 而它的实现类却是在第三方的包中,这样子 BootstrapClassloader 是无法加载的。

那么怎样才能加载第三方包中的类呢?

看看 ServiceLoader 这个类我们就清楚啦~ 😝


ServiceLoader 源码


可以发现这里有很显眼的一句话


ClassLoader cl = Thread.currentThread().getContextClassLoader();


通过 当前线程的上下文类加载器 去加载的!


那么这个 当前线程的上下文类加载器 中又是用了哪个 classLoader 呢?


在源码中一番搜索之后,我们可以看到在启动器 Launcher 中有这么一段代码,将 AppClassLoader 赋值给 ClassLoader 变量,并将其设置到当前线程中。😋



那么结论也很明显啦,SPI 是通过应用程序类加载器 AppClassLoader 去加载第三方包中的类的。

总结

java 提供了这个服务发现机制 SPI ,可以方便我们对某个接口进行扩展,实现模块的热插拔,而缺点就是,灵活度不高,配置文件中由多少实现类,都会被加载到内存中,不管有没有使用到~🐷


原理图奉上~


最后

欢迎小伙伴们来一起探讨问题~


如果你觉得本篇文章还不错的话,那拜托再点点赞支持一下呀😝

让我们开始这一场意外的相遇吧!~

欢迎留言!谢谢支持!ヾ(≧▽≦*)o 冲冲冲!!

我是 4ye 咱们下期应该……很快再见!! 😆

如果文章对您有所帮助,欢迎关注公众号 J a v a 4 y e 😆

发布于: 4 小时前阅读数: 7
用户头像

4ye

关注

公众号:J a v a 4 y e 2021.07.19 加入

还未添加个人简介

评论

发布
暂无评论
服务发现机制SPI居然是破坏者!