SPI (Service Provider Interface)机制
什么是SPI机制
JDK内置的一种 服务提供发现机制,可以用来启用框架扩展和替换组件。
暴露一个公共接口,共开发人员实现,动态加载实现类。
示例
Code
1 2
| # dubbo spi 依赖包 implementation 'org.apache.dubbo:dubbo-common:3.2.2'
|
接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package dekun.wang.spi.interfaces;
import org.apache.dubbo.common.extension.SPI;
@SPI public interface Command { void execute(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package dekun.wang.spi.interfaces.dubbo;
import org.apache.dubbo.common.extension.SPI;
@SPI public interface CommandDubbo { void execute(); }
|
实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package dekun.wang.spi.interfaces.impl;
import dekun.wang.spi.interfaces.Command; import org.apache.dubbo.common.extension.SPI;
import java.util.Map;
@SPI("CommandEnv") public class CommandEnv implements Command { @Override public void execute() { System.out.println("=========================="); ProcessBuilder processBuilder = new ProcessBuilder(); Map<String, String> environment = processBuilder.environment(); System.out.println("Brew=" + environment.get("Brew")); System.out.println("--------------------------"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package dekun.wang.spi.interfaces.impl;
import dekun.wang.spi.interfaces.Command; import org.apache.dubbo.common.extension.SPI;
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;
@SPI("CommandWhichGit") public class CommandWhichGit implements Command { @Override public void execute() { System.out.println("=========================="); try { Process pr = Runtime.getRuntime().exec(new String[]{"/usr/bin/which", "git"}); BufferedReader br = new BufferedReader(new InputStreamReader(pr.getInputStream())); String result; while ((result = br.readLine()) != null) { System.out.println(result); } br.close(); } catch (IOException e) { throw new RuntimeException(e); } System.out.println("--------------------------"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| package dekun.wang.spi.interfaces.impl;
import dekun.wang.spi.interfaces.Command; import org.apache.dubbo.common.extension.SPI;
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets;
@SPI("CommandWitchLs") public class CommandWitchLs implements Command { @Override public void execute() { System.out.println("=========================="); try { ProcessBuilder processBuilder = new ProcessBuilder(); processBuilder.command("which", "ls"); Process process = processBuilder.start(); try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8));) { String line = null; while ((line = bufferedReader.readLine()) != null) { System.out.println(line); } } } catch (IOException e) { throw new RuntimeException(e); } System.out.println("--------------------------");
} }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package dekun.wang.spi.interfaces.dubbo;
import org.apache.dubbo.common.extension.SPI;
@SPI("CommandDubboImpl") public class CommandDubboImpl implements CommandDubbo { @Override public void execute() { System.out.println("============CommandDubboImpl================"); } }
|
资源目录配置
1 2 3 4 5 6 7 8 9 10 11 12
| $ tree -NL 4 resources resources ├── META-INF │ ├── dubbo │ │ ├── dekun.wang.spi.interfaces.Command # 文件名 │ │ └── internal │ │ └── dekun.wang.spi.interfaces.Command # 文件名 │ ├── services │ │ ├── dekun.wang.spi.interfaces.Command # 文件名 │ │ └── dekun.wang.spi.interfaces.dubbo.CommandDubbo # 文件名 │ └── spring.factories └── application.properties
|
JDK SPI
配置
resources 配置
创建META-INF/services
目录,新建以接口全路径命名的文件,文件内没行及实现类全路径类。
META-INF/services/dekun.wang.spi.interfaces.Command
行排序及迭代顺序
1 2 3
| dekun.wang.spi.interfaces.impl.CommandWhichGit dekun.wang.spi.interfaces.impl.CommandEnv dekun.wang.spi.interfaces.impl.CommandWitchLs
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package dekun.wang.spi;
import dekun.wang.spi.interfaces.Command;
import java.util.ServiceLoader;
public class TestJdkSpi { public static void main(String[] args) { ServiceLoader<Command> command = ServiceLoader.load(Command.class); for (Command cmd : command) { cmd.execute(); } } }
|
1 2 3 4 5 6 7 8 9
| ========================== /usr/local/bin/git -------------------------- ========================== Brew=/usr/local/bin:/usr/local/sbin -------------------------- ========================== /bin/ls --------------------------
|
Spring SPI
配置
resources 配置
目录创建 META-INF
目录,并创建文件spring.factories
META-INF/spring.factories
spring.factories
文件内容格式,加载顺序,及迭代顺序。
1 2 3 4
| dekun.wang.spi.interfaces.Command=\ dekun.wang.spi.interfaces.impl.CommandWhichGit,\ dekun.wang.spi.interfaces.impl.CommandEnv,\ dekun.wang.spi.interfaces.impl.CommandWitchLs
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package dekun.wang.spi;
import dekun.wang.spi.interfaces.Command; import org.springframework.core.io.support.SpringFactoriesLoader;
public class TestSpringSpi { public static void main(String[] args) { SpringFactoriesLoader springFactoriesLoader = SpringFactoriesLoader.forDefaultResourceLocation(Command.class.getClassLoader()); for (Command command : springFactoriesLoader.load(Command.class)) { command.execute(); } } }
|
1 2 3 4 5 6 7 8 9
| ========================== /usr/local/bin/git -------------------------- ========================== Brew=/usr/local/bin:/usr/local/sbin -------------------------- ========================== /bin/ls --------------------------
|
配置
文件名:接口全路径,内容 key(注解配置key)=实现类全路径
META-INF/dubbo/internal/dekun.wang.spi.interfaces.Command
内容:CommandWhichGit=dekun.wang.spi.interfaces.impl.CommandWhichGit
META-INF/dubbo/dekun.wang.spi.interfaces.Command
内容:
CommandEnv=dekun.wang.spi.interfaces.impl.CommandEnv
CommandWitchLs=dekun.wang.spi.interfaces.impl.CommandWitchLs
META-INF/services/dekun.wang.spi.interfaces.dubbo.CommandDubbo
内容: CommandDubboImpl=dekun.wang.spi.interfaces.dubbo.CommandDubboImpl
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| package dekun.wang.spi;
import dekun.wang.spi.interfaces.Command; import dekun.wang.spi.interfaces.dubbo.CommandDubbo; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.rpc.model.ApplicationModel;
public class TestDubboSpi { public static void main(String[] args) { ExtensionLoader<Command> extensionLoader = ExtensionLoader.getExtensionLoader(Command.class); extensionLoader.getExtension("CommandWhichGit").execute(); extensionLoader.getExtension("CommandEnv").execute(); ExtensionLoader.getExtensionLoader(CommandDubbo.class).getExtension("CommandDubboImpl").execute(); ApplicationModel applicationModel = ApplicationModel.defaultModel(); ExtensionLoader<Command> extensionLoader1 = applicationModel.getExtensionDirector().getExtensionLoader(Command.class); extensionLoader1.getExtension("CommandWitchLs").execute(); } }
|
1 2 3 4 5 6 7 8 9 10
| ========================== /usr/local/bin/git -------------------------- ========================== Brew=/usr/local/bin:/usr/local/sbin -------------------------- ============CommandDubboImpl================ ========================== /bin/ls --------------------------
|