SPI (Service Provider Interface)机制

本文最后更新于:2023-06-02 03:05 星期五

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;

/**
*
* @author <a href="mailto:wangdekunemail@gmail.com">WangDeKun</a>
* @since
*
* <p>
*/
@SPI //dubbo 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;

/**
*
* @author <a href="mailto:wangdekunemail@gmail.com">WangDeKun</a>
* @since
*
* <p>
*/
@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;

/**
*
* @author <a href="mailto:wangdekunemail@gmail.com">WangDeKun</a>
* @since
*
* <p>
*/
@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;

/**
*
* @author <a href="mailto:wangdekunemail@gmail.com">WangDeKun</a>
* @since
*
* <p>
*/
@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;

/**
*
* @author <a href="mailto:wangdekunemail@gmail.com">WangDeKun</a>
* @since
*
* <p>
*/
@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;

/**
* @author <a href="mailto:wangdekunemail@gmail.com">WangDeKun</a>
*
* <p>
* @since
*/
@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;

/**
*
* @author <a href="mailto:wangdekunemail@gmail.com">WangDeKun</a>
* @since
*
* <p>
*/
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;

/**
* @author <a href="mailto:wangdekunemail@gmail.com">WangDeKun</a>
* @since <p>
*/
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
--------------------------

Dubbo SPI

配置

文件名:接口全路径,内容 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;

/**
* @author <a href="mailto:wangdekunemail@gmail.com">WangDeKun</a>
* <p>
* <p>
* 扫描以下路径
* <p>
* META-INF/dubbo/internal
* <p>
* META-INF/dubbo
* <p>
* META-INF/services
* <p>
* @since
*/

public class TestDubboSpi {
public static void main(String[] args) {
ExtensionLoader<Command> extensionLoader = ExtensionLoader.getExtensionLoader(Command.class);
//META-INF/dubbo/internal
extensionLoader.getExtension("CommandWhichGit").execute();
//META-INF/dubbo
extensionLoader.getExtension("CommandEnv").execute();
// META-INF/services
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
--------------------------

SPI (Service Provider Interface)机制
https://blog.dekun.wang/blog/e2c17ed0.html
作者
Wang Dekun
发布于
2023-06-01 21:57 星期四
更新于
2023-06-02 03:05 星期五
许可协议