Análisis en profundidad del mecanismo SPI de Spring Boot

¡Trabajar juntos para crear y crecer juntos! Este es el día 21 de mi participación en el "Nuggets Daily New Plan · August Update Challenge", haz clic para ver los detalles del evento

Introducción

SPI (Interfaz de proveedor de servicios) es un mecanismo de descubrimiento de proveedores de servicios integrado en JDK, que se puede usar para habilitar extensiones de marco y reemplazar componentes. Se usan diferentes implementaciones para que la misma interfaz se proporcione a diferentes usuarios, mejorando así la capacidad de expansión del marco.

Implementación Java SPI

El SPI incorporado de Java usa la clase java.util.ServiceLoader para analizar classPath y el archivo nombrado con el nombre completo de la interfaz en el directorio META-INF/services/ del paquete jar, y carga la clase de implementación de la interfaz especificada en el archivo para completar la llamada.

Ejemplo de descripción

Crear una interfaz dinámica

public interface VedioSPI
{
    void call();
}

复制代码

Clase de implementación 1

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

}
复制代码

Clase de implementación 2

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

}
复制代码

Cree un nuevo directorio META-INF/services/ en el directorio de origen del proyecto y cree el archivo com.skywares.fw.juc.spi.VedioSPI.

foto.png

Pruebas relacionadas

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

Descripción: la implementación Java de spi es una clase de herramienta proporcionada por ServiceLoader para encontrar servicios.

resultado de la operación:

foto.png

Análisis de código fuente

Lo anterior es solo un ejemplo simple para realizar la función SPI integrada de java. El principio de implementación es que ServiceLoader es una clase de herramienta incorporada en Java que se usa para encontrar interfaces que brindan servicios. Busca interfaces que brindan servicios llamando al método load() y finalmente atraviesa para acceder a las clases de implementación interfaces uno por uno.

foto.png

Desde el código fuente se puede encontrar:

  • La propia clase ServiceLoader implementa la interfaz iterable e implementa el método iterador. La implementación del método iterador llama al método en la clase interna LazyIterator. Después de analizar el archivo de interfaz proporcionado por el servicio, el resultado final se devuelve en el iterador, que no servicios de soporte Proporciona acceso directo a las clases de implementación de la interfaz.

  • Todos los archivos correspondientes de la interfaz del proveedor de servicios se colocan en el directorio META-INF/services/, y el tipo final determina que el directorio PREFIX no se puede cambiar.

虽然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文件

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

输出结果

foto.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的呢?我们可以通过源码来进一步分析。

源码分析

foto.png

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

foto.pngDescripción: obtenga las rutas de archivo de META-INF/spring.factories en todos los paquetes jar y devuélvalos como valores de enumeración. Recorra la ruta del archivo spring.factories, cargue y analice uno por uno, integre el nombre de la clase de implementación del tipo factoryClass, obtenga el nombre de clase completo de la clase de implementación y luego realice la operación de instancia de la clase. código fuente relevante es el siguiente:

foto.png

Descripción: La instanciación consiste en lograr la inicialización correspondiente a través de la reflexión.

Resumir

Este artículo explica en detalle el mecanismo SPI de Java y Spring.La tecnología SPI separa la interfaz del servicio de la implementación del servicio para lograr el desacoplamiento, mejorando así la escalabilidad del programa. Si tiene alguna pregunta, no dude en enviar sus comentarios.

Supongo que te gusta

Origin juejin.im/post/7132742686099898398
Recomendado
Clasificación