spring的spi

spi是什么?

SPI(service provider interface)机制是JDK内置的一种服务发现机制,可以动态的发现服务,即服务提供商,它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。目前这种大部分都利用SPI的机制进行服务提供,比如:dubbo、spring、JDBC、等;

spi解决了什么问题?

由于classLoader加载类的时候采用是【双亲委托模式】,意思是:首先委托父类去加载器获取,若父类加载器存在则直接返回,若加载器无法完成此加载任务,自己才去加载。该加载存在的弊端就是上层的类加载永远无法加载下层的类加载器所加载的类,所以通过spi解决了该问题。

spi是一种将服务接口与服务实现分离以达到解耦、大大提升了程序可扩展性的机制。引入服务提供者就是引入了spi接口的实现者,通过本地的注册发现获取到具体的实现类,轻松可插拔spi实现了动态加载,插件化,

弊端:

资源浪费:由于 spi 是通过循环加载实现类,会导致所有的类全部一起加载!

有哪些SPI机制?

jcl-over-slf4j-1.7.5.jar

dubbo

mysql-connector-java-5.1.35.jar

等等...

原因

简单的说,类加载器中的双亲委派模型的工作原理是对于类的加载,先交给父类加载器完成,如果父类加载器无法完成加载,子类加载器才进行加载。JDK中的一些基础类接口,例如 JDBC,如果按照双亲委派模型,JDBC只能用启动类加载器完成驱动的加载,但是驱动的实现是在用户目录的 jar 包实现,所以启动类加载器就无法加载用户的目录的驱动类了(下面可以从代码实例了解一下)

ServiceLoader 的源码中的介绍如下

代码下载:https://gitee.com/hong99/spring/issues/I1N1DF

代码实现

jdk spi实现

位置图

/**
 *
 * 功能描述:接口
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/17 15:13
 */
public interface Robot {

    void sayHello();
}
package com.hong.spring.spi.service.impl;

import com.hong.spring.spi.service.Robot;
/**
 *
 * 功能描述: 大黄峰
 *
 * @param: 
 * @return: 
 * @auther: csh
 * @date: 2020/8/17 15:14
 */
public class Bumblebee implements Robot {

    @Override
    public void sayHello() {
        System.out.println("Hello, I am Bumblebee.");
    }
}
package com.hong.spring.spi.service.impl;

import com.hong.spring.spi.service.Robot;
/**
 *
 * 功能描述: 擎天柱
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/17 15:14
 */
public class OptimusPrime implements Robot {
    
    @Override
    public void sayHello() {
        System.out.println("Hello, I am Optimus Prime.");
    }
}

META-INF/services/com.hong.spring.spi.service.Robot

com.hong.spring.spi.service.impl.Bumblebee
com.hong.spring.spi.service.impl.OptimusPrime

spi_test.JdkSpiTest

package spi_test;

import com.hong.spring.spi.service.Robot;
import sun.misc.Service;

import java.util.Iterator;
import java.util.ServiceLoader;

/**
 * @Auther: csh
 * @Date: 2020/8/17 14:53
 * @Description:jdk的spi测试
 */
public class JdkSpiTest {
    /**
     *
     * 功能描述:测试spi
     *
     * @param:
     * @return:
     * @auther: csh
     * @date: 2020/8/17 15:16
     */
    public static void main(String[] args) {
        Iterator <Robot> providers = Service.providers(Robot.class);
        ServiceLoader <Robot> load = ServiceLoader.load(Robot.class);
        while (providers.hasNext()){
            Robot next = providers.next();
            next.sayHello();
        }
        System.out.println("==============================");
        Iterator <Robot> iterator = load.iterator();
        while (iterator.hasNext()){
            Robot next = iterator.next();
            next.sayHello();
        }

    }
}

结果

Hello, I am Bumblebee.
Hello, I am Optimus Prime.
==============================
Hello, I am Bumblebee.
Hello, I am Optimus Prime.

spring spi实现

spring的spi是通过SpringFactoriesLoader类loadFactories方法来加载的,包目录是:org.springframework.core.io.support

文件格式是:配置放在 META-INF/spring.factories中;

public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
   Assert.notNull(factoryClass, "'factoryClass' must not be null");
   ClassLoader classLoaderToUse = classLoader;
   if (classLoaderToUse == null) {
      classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
   }
   List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
   if (logger.isTraceEnabled()) {
      logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
   }
   List<T> result = new ArrayList<T>(factoryNames.size());
   for (String factoryName : factoryNames) {
      result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
   }
   AnnotationAwareOrderComparator.sort(result);
   return result;
}

代码下载:https://gitee.com/hong99/spring/issues/I1N1DF

最后:

不管是jdk 的spi或者 spring spi都很好遵循了开闭原则,即对修改关闭,对拓展开放,其实看了很多开源的框架后都发现,很多框架都是遵循原则,并且在里面应用了大量的设计模式。而spi 的思想应用广泛就是应用了策略模式,不管是jdk/jdbc/spring/dubbo等框架都有用到,只是所标注的名字不同而且,实现原理大致相同。spi主要是解決是为了解决因为类上层加载器无法去加载下层加载器这个问题,当然也存在一个弊端就是扫描了所有类实现...,

参考文章:

https://www.cnblogs.com/helloz/p/10961026.html

jdK spi:https://blog.csdn.net/rzpy_qifengxiaoyue/article/details/107977448

双亲委托:https://www.cnblogs.com/hiyujie/p/wo-xueJava1ClassLoader-yu-shuang-qin-wei-tuo-mo-sh.html

https://www.cnblogs.com/hiyujie/p/wo-xueJava1ClassLoader-yu-shuang-qin-wei-tuo-mo-sh.html

https://learnku.com/articles/45733

dubbo官方:http://dubbo.apache.org/zh-cn/docs/source_code_guide/dubbo-spi.html

本文分享自微信公众号 - 技术趋势(jishuqs),作者:逍遥壮士

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-08-22

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • spring使用内存数据库(h2)

    内存数据库,就是不需要去安装类似于mysql、oracle等这些数据库,而是通过程序运行的时候将sql执行将数据读取到内存中,程序结束后从内存中移除,减少因为安...

    逍遥壮士
  • 设计模式-迭代器模式

    每次想起学生时代,经常性点名,第头来,第尾来,乱来,呵呵,老师的点名方式五花8门...而点这种就类似到遍历,我们设计模式中的迭代器一样的逻辑,从头到尾或按照想要...

    逍遥壮士
  • Spring BeanFactory 容器

    BeanFactory简称bean工厂,是整个spring的核心父类,也是IOC容器或对象的工厂,类是:org.springframework.beans.fa...

    逍遥壮士
  • 云+社区技术沙龙第26期回顾-大数据技术实践与应用(文末附PPT)

    2019年8月24日,由云+社区主办的第26期云+社区技术沙龙——大数据技术实践与应用,在北京快手总部举行,本期沙龙云+社区联合快手邀请技术大咖为大家深度解读大...

    云加社区技术沙龙
  • Storm客户端提交任务失败原因分析

    囚兔
  • [Jenkins]5分钟系列之六Jenkins cli介绍

    一、准备工作 1.1、环境准备 软件版本功能jenkins2.95提供平台1.2、推荐阅读 分分钟部署安装jenkins 二、Jenkins cli 2.1、J...

    追马
  • RecyclerView 缓存策略

    ppjun
  • 鱼和熊掌我都要之 Render-as-You-Fetch 模式

    感谢支持ayqy个人订阅号,每周义务推送1篇(only unique one)原创精品博文,话题包括但不限于前端、Node、Android、数学...

    ayqy贾杰
  • 前端基础-HTML(meta标签)

    这种情况就是乱码,是因为我们输入的中文,往计算机中保存的时候,最终都要转成2进制的数据形式,也就是说有一个编码的过程,在保存文件的时候默认使用的是ANSI编码格...

    cwl_java
  • Python——图片透明化处理

    量化分析的篇章,前一篇已经做了完结。如果要细究一下,整体的流程框架都有了,要深入详细的搭建程序代码,可能还要再写个十多篇。倘若交易策略多种组合,就要更多篇幅了。

    Ed_Frey

扫码关注云+社区

领取腾讯云代金券