什么是SPI?

个人笔记

在java项目使用maven导完包之后,点进去发现有的包下面有resource下面有META-INF\service,有的包下面有META-INF\spring.factoris文件。这些是干什么的,我自己写项目的时候为什么都没写过这个。

 在我的好奇驱动下,我就查询各种资料。

一、首先为什么要写这些文件,有啥用?

1、介绍

META-INF/servicesJDK 提供的 SPI(Service Provider Interface)机制的一种实现方式。

在 JDK 的 SPI 机制中,可以通过在 JAR 文件的 `META-INF/services` 目录下创建一个以接口全限定名为命名的文件,文件中列出接口的实现类的全限定名,用于指定接口的具体实现类。当 JDK 加载该接口时,会自动读取这个文件,并根据文件中指定的实现类来创建实例。

而在Spring 的SPI机制的实现方式是通过 META-INF/spring.factories 文件来实现的。在 Spring 中,每个模块或者库都可以通过在 `META-INF/spring.factories` 文件中注册自己的实现类,从而扩展或者替换 Spring 框架的功能。当 Spring 启动时,会自动读取所有 `META-INF/spring.factories` 文件,并加载其中注册的实现类。

上面两个可知: META-INF/spring.factories 和META-INF/services 都是实现SPI的,只是通过不同的方式去实现的。

2、它们的作用:

虽然两种 SPI 实现方式在原理上是类似的,但是它们的应用场景和使用方式有所区别。JDK 的 SPI 适用于 Java 标准库的扩展和插件化,而 Spring 的 SPI 则更适用于扩展和定制 Spring 框架本身的功能。Spring 的 SPI 机制更加灵活,允许开发者以模块化的方式扩展 Spring 的各个功能,从而实现高度可定制的应用程序。

3、怎么用

例子:

假设我们有一个自动配置类 `com.example.MyAutoConfiguration`,它定义了一个名为 `myBean` 的 Bean,用于打印一条简单的欢迎消息。

package com.example;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyAutoConfiguration {

    @Bean
    public MyBean myBean() {
        return new MyBean();
    }

    public static class MyBean {
        public void sayHello() {
            System.out.println("Hello, this is MyAutoConfiguration!");
        }
    }
}

现在,我们希望将这个自动配置类作为一个插件,供其他应用程序使用。为此,我们可以在 `resources/META-INF/spring.factories` 文件中添加以下内容:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

com.example.MyAutoConfiguration

把项目打包,在另一个项目中引入。 

<!-- pom.xml -->
<dependency>
    <groupId>com.example</groupId>
    <artifactId>my-auto-configuration</artifactId>
    <version>1.0.0</version>
</dependency>

 在应用程序的代码中,我们可以直接使用 `MyBean` 类,因为它已经被自动配置了。

import com.example.MyAutoConfiguration.MyBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class MyApp {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(MyApp.class, args);
        MyBean myBean = context.getBean(MyBean.class);
        myBean.sayHello();
    }
}

当我们运行应用程序时,会看到输出:

Hello, this is MyAutoConfiguration!

 

这说明 `com.example.MyAutoConfiguration` 这个自动配置类已经被自动加载,并且其中定义的 `myBean` Bean 已经在 Spring 容器中创建了。

这个例子展示了如何使用 `spring.factories` 文件将自动配置类作为插件提供给其他应用程序使用。通过引入相应的依赖,其他应用程序可以自动获得 `MyAutoConfiguration` 中定义的 Bean,并获得相应的功能扩展。

二、深入了解

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

解决的根本问题:

为什么需要SPI机制,这才是问题的源头。SPI是一种跟API相对应的反向设计思想:API由实现方确定标准规范和功能,调用方无权做任何干预; 而SPI是由调用方确定标准规范,也就是接口,然后调用方依赖此接口,第三方实现此接口,这样做就可以方便的进行扩展,类似于插件机制,这是SPI出现的需求背景。然后问题来了:

怎么样才能加载这些SPI接口的实现类呢?

问题的关键不是/META-INF/services。真正的原因是须了解java的类加载机制!SPI接口属于java rt核心包,只能由启动类加载器BootStrap classLoader加载,而第三方jar包是用户classPath路径下,根据类加载器的可见性原则:启动类加载器无法加载这些jar包,也就是没法向下委托,所以spi必须打破这种传统的双亲委派机制,通过自定义的类加载器来加载第三方jar包下的spi接口实现类! 这才是问题的关键!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值