开发者社区> Java4ye> 正文

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

简介: 前言 主要介绍下 Java 中的 SPI 机制 。 Springboot 的 SPI 机制 咱们在下文 Springboot的自动装配中再说~ 😝 嘿嘿 至于 [[ dubbo 的 SPI 机制]],还没时间深入了解,简单知道了它的 SPI 的自适应扩展机制,以及下面这些扩展~(超级多扩展的~)🐷 冲冲冲! 什么是SPI呢? SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在 ClassPath 路径下的 META-INF/services 文件夹查找文件,自动加载文件里所定义的类(定义多少,加载多少) 它是 Java 提供的一套用
+关注继续查看

前言


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


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

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


image



image


冲冲冲


什么是SPI呢?


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

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


JAVA SPI 实践


步骤


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


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


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


项目结构图


image


spi文件内容


image


源码如下


接口


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();
        }
    }
}
复制代码


结果


image


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


JDBC实例


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


image


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


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


com.mysql.cj.jdbc.Driver
复制代码


Driver源码


image


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


那有什么作用呢?


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

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


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


image


JAVA SPI 原理探索


疑惑


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


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


详细请看下图👇


image


image


破坏双亲委派机制


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


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

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

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


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


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


image


ServiceLoader源码


image


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


ClassLoader cl = Thread.currentThread().getContextClassLoader();
复制代码


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


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


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


image


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


总结


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


原理图奉上~


image



版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
如何设置阿里云服务器安全组?阿里云安全组规则详细解说
阿里云安全组设置详细图文教程(收藏起来) 阿里云服务器安全组设置规则分享,阿里云服务器安全组如何放行端口设置教程。阿里云会要求客户设置安全组,如果不设置,阿里云会指定默认的安全组。那么,这个安全组是什么呢?顾名思义,就是为了服务器安全设置的。安全组其实就是一个虚拟的防火墙,可以让用户从端口、IP的维度来筛选对应服务器的访问者,从而形成一个云上的安全域。
18873 0
阿里云服务器ECS登录用户名是什么?系统不同默认账号也不同
阿里云服务器Windows系统默认用户名administrator,Linux镜像服务器用户名root
15552 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
28076 0
阿里云ECS云服务器初始化设置教程方法
阿里云ECS云服务器初始化是指将云服务器系统恢复到最初状态的过程,阿里云的服务器初始化是通过更换系统盘来实现的,是免费的,阿里云百科网分享服务器初始化教程: 服务器初始化教程方法 本文的服务器初始化是指将ECS云服务器系统恢复到最初状态,服务器中的数据也会被清空,所以初始化之前一定要先备份好。
16593 0
阿里云服务器安全组设置内网互通的方法
虽然0.0.0.0/0使用非常方便,但是发现很多同学使用它来做内网互通,这是有安全风险的,实例有可能会在经典网络被内网IP访问到。下面介绍一下四种安全的内网互联设置方法。 购买前请先:领取阿里云幸运券,有很多优惠,可到下文中领取。
22087 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,大概有三种登录方式:
13090 0
+关注
Java4ye
公众号Java4ye 作者🐖
62
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载