Analyse approfondie du mécanisme SPI de Spring Boot

Travailler ensemble pour créer et grandir ensemble ! C'est le 21e jour de ma participation au "Nuggets Daily New Plan · August Update Challenge", cliquez pour voir les détails de l'événement

Introduction

SPI (Service Provider Interface) est un mécanisme de découverte de fournisseur de services intégré au JDK, qui peut être utilisé pour activer des extensions de framework et remplacer des composants. Différentes implémentations sont utilisées pour la même interface à fournir à différents utilisateurs, améliorant ainsi l'extensibilité du framework.

Implémentation Java SPI

Le SPI intégré de Java utilise la classe java.util.ServiceLoader pour analyser le classPath et le fichier nommé avec le nom complet de l'interface dans le répertoire META-INF/services/ du package jar, et charge la classe d'implémentation d'interface spécifiée dans le fichier pour terminer l'appel.

Exemple de description

Créer une interface dynamique

public interface VedioSPI
{
    void call();
}

复制代码

Classe d'implémentation 1

public class Mp3Vedio implements VedioSPI
{
    @Override
    public void call()
    {
        System.out.println("this is mp3 call");
    }

}
复制代码

Classe d'implémentation 2

public class Mp4Vedio implements VedioSPI
{
    @Override
    public void call()
    {
       System.out.println("this is mp4 call");
    }

}
复制代码

Créez un nouveau répertoire META-INF/services/ dans le répertoire source du projet et créez le fichier com.skywares.fw.juc.spi.VedioSPI.

image.png

Essais associés

public class VedioSPITest
{
    public static void main(String[] args)
    {
        ServiceLoader<VedioSPI> serviceLoader =ServiceLoader.load(VedioSPI.class);
        
        serviceLoader.forEach(t->{
            t.call();
        });
    }
}
复制代码

Description : L'implémentation Java de spi est une classe d'outils fournie par ServiceLoader pour rechercher des services.

résultat de l'opération :

image.png

Analyse du code source

Ce qui précède n'est qu'un exemple simple pour réaliser la fonction SPI intégrée de java. Le principe d'implémentation est que ServiceLoader est une classe d'outils intégrée à Java utilisée pour trouver des interfaces fournissant des services. Il recherche des interfaces fournissant des services en appelant la méthode load(), et finalement traverse pour accéder aux classes d'implémentation de fournisseurs de services. interfaces une par une.

image.png

À partir du code source, vous pouvez trouver :

  • La classe ServiceLoader elle-même implémente l'interface Iterable et implémente la méthode iterator. L'implémentation de la méthode iterator appelle la méthode dans la classe interne LazyIterator. Après avoir analysé le fichier d'interface fourni par le service, le résultat final est renvoyé dans Iterator, qui ne services de support Fournit un accès direct aux classes d'implémentation d'interface.

  • Tous les fichiers correspondants de l'interface du fournisseur de services sont placés dans le répertoire META-INF/services/, et le type final détermine que le répertoire PREFIX ne peut pas être modifié.

虽然java提供的SPI机制的思想非常好,但是也存在相应的弊端。具体如下:

  • Java内置的方法方式只能通过遍历来获取
  • 服务提供接口必须放到META-INF/services/目录下。

针对java的spi存在的问题,Spring的SPI机制沿用的SPI的思想,但对其进行扩展和优化。

Spring SPI

Spring SPI沿用了Java SPI的设计思想,Spring采用的是spring.factories方式实现SPI机制,可以在不修改Spring源码的前提下,提供Spring框架的扩展性。

Spring 示例

定义接口

public interface DataBaseSPI
{
   void getConnection();
}

复制代码

相关实现

#DB2实现
public class DB2DataBase implements DataBaseSPI
{
    @Override
    public void getConnection()
    {
        System.out.println("this database is db2");
    }

}

#Mysql实现
public class MysqlDataBase implements DataBaseSPI
{
    @Override
    public void getConnection()
    {
       System.out.println("this is mysql database");
    }

}
复制代码

1.在项目的META-INF目录下,新增spring.factories文件

image.png

2.填写相关的接口信息,内容如下:

com.skywares.fw.juc.springspi.DataBaseSPI = com.skywares.fw.juc.springspi.DB2DataBase, com.skywares.fw.juc.springspi.MysqlDataBase
复制代码

说明多个实现采用逗号分隔。

相关测试类

public class SpringSPITest
{
    public static void main(String[] args)
    {
         List<DataBaseSPI> dataBaseSPIs =SpringFactoriesLoader.loadFactories(DataBaseSPI.class, 
                 Thread.currentThread().getContextClassLoader());
         
         for(DataBaseSPI datBaseSPI:dataBaseSPIs){
            datBaseSPI.getConnection();
         }
    }
}
复制代码

输出结果

image.png

从示例中我们看出,Spring 采用spring.factories实现SPI与java实现SPI非常相似,但是spring的spi方式针对java的spi进行的相关优化具体内容如下:

  • Java SPI是一个服务提供接口对应一个配置文件,配置文件中存放当前接口的所有实现类,多个服务提供接口对应多个配置文件,所有配置都在services目录下;
  • Spring factories SPI是一个spring.factories配置文件存放多个接口及对应的实现类,以接口全限定名作为key,实现类作为value来配置,多个实现类用逗号隔开,仅spring.factories一个配置文件。

那么spring是如何通过加载spring.factories来实现SpI的呢?我们可以通过源码来进一步分析。

源码分析

image.png

说明:loadFactoryNames解析spring.factories文件中指定接口的实现类的全限定名,具体实现如下:

image.pngDescription : obtenez les chemins d'accès aux fichiers de META-INF/spring.factories dans tous les packages jar et renvoyez-les sous forme de valeurs d'énumération. Parcourez le chemin du fichier spring.factories, chargez et analysez un par un, intégrez le nom de la classe d'implémentation du type factoryClass, obtenez le nom complet de la classe d'implémentation, puis effectuez l'opération d'instance de la classe. le code source pertinent est le suivant :

image.png

Description : L'instanciation consiste à réaliser l'initialisation correspondante par réflexion.

Résumer

Cet article explique en détail le mécanisme SPI de Java et Spring. La technologie SPI sépare l'interface de service de l'implémentation du service pour réaliser le découplage, améliorant ainsi l'évolutivité du programme. Si vous avez des questions, n'hésitez pas à donner votre avis.

Guess you like

Origin juejin.im/post/7132742686099898398