Java spi 机制 (Service Provider Interface)
简述
Service Provider Interface(SPI) 是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。
在开发过程中,将问题抽象成API,可以为API提供各种实现。如果现在需要对API提供一种新的实现,我们可以不用修改原来的代码,直接生成新的Jar包,在包里提供API的新实现。通过Java的SPI机制,可以实现了框架的动态扩展,让第三方的实现能像插件一样嵌入到系统中。
Java的SPI类似于springIOC的功能,将装配的控制权移到了程序之外,实现在模块装配的时候不用在程序中动态指明。所以SPI的核心思想就是解耦,这在模块化设计中尤其重要。
简单点就是:spi是Java 内置的服务发现机制,可以被第三方扩展或实现的API, 它可以用来实现框架扩展和可替换的模块。
服务调用方通过 ServiceLoader.load 加载服务接口的实现类实例
服务提供方实现服务接口后, 在自己Jar包的META-INF/services目录下新建一个接口名全名的文件, 并将具体实现类全名写入。
Java spi 示例
项目结构
其中 SPI-ServiceImpl-B 和 SPI-ServiceImpl-A 是一样的。
创建统一的 api 接口
public interface SpiInterface {
void seyHello(String name);
}
创建接口实现
public class SpiServiceImplA implements SpiInterface {
public void seyHello(String name) {
System.out.println("AAAAAA"+name);
}
}
创建 META-INF 中的文件 注意名称路径,META-INF.services 中的文件是以,接口的全路径命名的。如上图工程中的文件一样。
文件中存放实现类的全路径: com.service.SpiServiceImplA
编写测试类:
import com.spi.SpiInterface;
import com.sun.tools.javac.util.ServiceLoader;
import java.util.Iterator;
public class application {
public static void main(String[] args) {
// 把api接口class文件传入方法中,就会自动加载所有实现
ServiceLoader<SpiInterface> load = ServiceLoader.load(SpiInterface.class);
Iterator<SpiInterface> iterator = load.iterator();
while (iterator.hasNext()){
SpiInterface spiInterface = iterator.next();
spiInterface.seyHello("hello");
}
}
}
Spring SPI 机制
对于Spring的SPI机制主要体现在SpringBoot中。体现在springboot四大核心之一的SpringBoot自动装配
spring 中是使用 SpringFactoriesLoader类加载的:SpringFactoriesLoader.loadFactoryNames
springboot spi 机制示例:
创建扩展工程
pom 文件引入 jar 包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.1.6.RELEASE</version>
<optional>true</optional>
</dependency>
编写具体实现类
package com.springautobean;
/**
* @Author: DaoZhuang
* @Date: 2020/7/15
*/
public class ReidsClient {
public void seyHello(){
System.out.println("顶顶顶顶");
}
}
编写 configuration 类
package com.springautobean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author: DaoZhuang
* @Date: 2020/7/15
*/
@Configuration
public class AutoBeanConfiguration {
@Bean
public ReidsClient reidsClient(){
return new ReidsClient();
}
}
在 resource 下创建 META-INF/spring.factories 文件
# key 对应springboot定义好的 org.springframework.boot.autoconfigure.EnableAutoConfiguration
# val 对应的是自己编写的 Configuration 配置类
# val 可以是多个,多个最后要加 \ 符号
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.springautobean.AutoBeanConfiguration
新建springboot 项目测试工程,在新建的项目中引入上面编写的项目 在项目中使用容器获取,实例类
@SpringBootApplication
public class SpringautobeandemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringautobeandemoApplication.class, args);
// 获取bean
ReidsClient reidsClient = applicationContext.getBean(ReidsClient.class);
// 打印 bean对象
System.out.println(reidsClient);
// 调用bean 方法
reidsClient.seyHello();
}
}
整个工程的代码截图:
学习spi机制可以对比,springboot自动装配,dubbo spi一起学习dubbo spi