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

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!
今天 4ye 来和小伙伴们分享下这个 SPI 机制啦~ ,SPI 的身影无处不在!是热插拔的体现之一,更和双亲委派机制有点小关系,居然是个破坏者?


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


前言

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


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


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


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




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


冲冲冲


什么是SPI呢?


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


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



JAVA SPI 实践

步骤

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

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

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


项目结构图

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


spi文件内容

服务发现机制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();
      }
  }

}


结果

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


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


JDBC实例

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


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


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


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


com.mysql.cj.jdbc.Driver


Driver源码

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


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


那有什么作用呢?


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

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


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


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



JAVA SPI 原理探索


疑惑

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


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


详细请看下图?

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


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


破坏双亲委派机制

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

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

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

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


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


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


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



ServiceLoader源码

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


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

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


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


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


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


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


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


总结

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


原理图奉上~

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


最后

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

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

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

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

我是4ye 咱们下期很快再见!!




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

电子书网站和整理好的一份电子书单


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

站了5个小时换来的书单


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

一份Vue笔记,一个少年故事


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

万字长文,带你快速上手这些池化技术!


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

MySQL各大版本新特性一览





原文始发于微信公众号(Java4ye):服务发现机制SPI居然是破坏者?!