SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。
SPI
JDK 中 提供了一个 SPI 的功能,核心类是 java.util.ServiceLoader。其作用就是,可以通过类名获取在”META-INF/services/“下的多个配置实现文件。
ServiceLoader
- META-INF/services下的文件(以Interface全路径命名)中添加具体实现类的全路径名。
- 使用程序使用ServiceLoader动态加载实现类(根据目录META-INF/services下的配置文件找到实现类的全限定名并调用classloader来加载实现类到JVM)
- SPI的实现类必须具有无参数的构造方法
演示代码
目录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| └── src ├── main │ ├── java │ │ └── cn │ │ └── z201 │ │ └── spi │ │ ├── SpeakChinese.java │ │ ├── SpeakEnglish.java │ │ ├── SpeakGerman.java │ │ ├── SpeakSPIService.java // 定义接口 │ │ └── package-info.java │ └── resources │ └── META-INF │ └── services │ └── cn.z201.spi.SpeakSPIService // 配置文件,文件名以接口包和类名称组成。
|
接口
1 2 3
| public interface SpeakSPIService { Object echo(); }
|
实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class SpeakGerman implements SpeakSPIService{
@Override public Object echo() { return "german : hallo"; } }
public class SpeakEnglish implements SpeakSPIService{ @Override public Object echo() { return "english : hello"; } }
public class SpeakChinese implements SpeakSPIService {
@Override public Object echo() { return "chinese : 你好"; } }
|
配置文件
1 2 3
| cn.z201.spi.SpeakChinese cn.z201.spi.SpeakGerman cn.z201.spi.SpeakEnglish
|
测试
1 2 3 4 5 6 7 8 9 10 11 12 13
| class SpeakSPIServiceTest {
@Test public void setUp(){ ServiceLoader<SpeakSPIService> serviceLoader = ServiceLoader.load(SpeakSPIService.class, Thread.currentThread().getContextClassLoader()); Iterator<SpeakSPIService> iterator = serviceLoader.iterator(); while (iterator.hasNext()) { SpeakSPIService speakSPIService = iterator.next(); System.out.println("speakSPIService " + speakSPIService.echo()); } }
}
|
1 2 3 4
| speakSPIService chinese : 你好 speakSPIService german : hallo speakSPIService english : hello
|
改进
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 41 42 43 44 45 46 47 48 49 50
| @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface LanguageType {
String language(); }
@LanguageType(language = "chinese") public class SpeakChinese implements SpeakSPIService {
@Override public Object echo() { return "chinese : 你好"; } } @LanguageType(language = "english") public class SpeakEnglish implements SpeakSPIService{ @Override public Object echo() { return "english : hello"; } } @LanguageType(language = "german") public class SpeakGerman implements SpeakSPIService{
@Override public Object echo() { return "german : hallo"; } }
public class LanguageTypeTool {
public static SpeakSPIService language(String language){ ServiceLoader<SpeakSPIService> serviceLoader = ServiceLoader.load(SpeakSPIService.class, Thread.currentThread().getContextClassLoader()); Iterator<SpeakSPIService> iterator = serviceLoader.iterator(); while (iterator.hasNext()) { SpeakSPIService speakSPIService = iterator.next(); Class clazz = speakSPIService.getClass(); LanguageType languageType = (LanguageType) clazz.getDeclaredAnnotation(LanguageType.class); if (null != languageType) { if (Objects.equals(language,languageType.language())) { return speakSPIService; } } } return null; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Test public void setUp() { ServiceLoader<SpeakSPIService> serviceLoader = ServiceLoader.load(SpeakSPIService.class, Thread.currentThread().getContextClassLoader()); Iterator<SpeakSPIService> iterator = serviceLoader.iterator(); while (iterator.hasNext()) { SpeakSPIService speakSPIService = iterator.next(); System.out.println(speakSPIService.echo()); } SpeakSPIService speakSPIService = LanguageTypeTool.language("chinese"); if (null != speakSPIService) { System.out.println(speakSPIService.echo()); } speakSPIService = LanguageTypeTool.language("english"); if (null != speakSPIService) { System.out.println(speakSPIService.echo()); } }
|
1 2 3 4 5
| chinese : 你好 german : hallo english : hello chinese : 你好 english : hello
|
ServiceLoaderFactoryBean
通过Java提供的SPI的方式来加载相关实现类并注册到Spring容器中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class ServiceLoaderFactoryBean extends AbstractServiceLoaderBasedFactoryBean implements BeanClassLoaderAware {
@Override protected Object getObjectToExpose(ServiceLoader<?> serviceLoader) { return serviceLoader; }
@Override public Class<?> getObjectType() { return ServiceLoader.class; }
}
|
演示代码
1 2 3 4 5
| <dependency> <groupId>cn.z201.spi</groupId> <artifactId>Jdk-SPI</artifactId> <version>1.0.0-SNAPSHOT</version> </dependency>
|
配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
@Configuration public class SpeakSPIServiceConfig {
@Bean("initBeanFactoryServiceLoaderFactoryBean") public ServiceLoaderFactoryBean serviceLoaderFactoryBean() { ServiceLoaderFactoryBean serviceLoaderFactoryBean = new ServiceLoaderFactoryBean(); serviceLoaderFactoryBean.setServiceType(SpeakSPIService.class); return serviceLoaderFactoryBean; }
}
|
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Test public void setSpringUp() { AnnotationConfigApplicationContext config = new AnnotationConfigApplicationContext(); config.register(SpeakSPIServiceConfig.class); config.refresh(); ServiceLoader<SpeakSPIService> serviceLoader = config.getBean("initBeanFactoryServiceLoaderFactoryBean", ServiceLoader.class); displayServiceLoader(serviceLoader); config.close(); }
private void displayServiceLoader(ServiceLoader<SpeakSPIService> serviceLoader){ Iterator<SpeakSPIService> iterator = serviceLoader.iterator(); while (iterator.hasNext()) { SpeakSPIService speakSPIService = iterator.next(); System.out.println("speakSPIService " + speakSPIService.echo()); } }
|
1 2 3 4
| speakSPIService chinese : 你好 speakSPIService german : hallo speakSPIService english : hello
|
改进
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 41 42
| @Test public void setSpringRegisterBeanUp() { AnnotationConfigApplicationContext config = new AnnotationConfigApplicationContext(); config.register(SpeakSPIServiceConfig.class); config.refresh(); registerBean(config); lookupCollectionType(config); config.close(); } private void registerBean(AnnotationConfigApplicationContext applicationContext){ ServiceLoader<SpeakSPIService> serviceLoader = applicationContext.getBean("initBeanFactoryServiceLoaderFactoryBean", ServiceLoader.class); Iterator<SpeakSPIService> iterator = serviceLoader.iterator(); while (iterator.hasNext()) { SpeakSPIService speakSPIService = iterator.next(); Class clazz = speakSPIService.getClass(); LanguageType languageType = (LanguageType) clazz.getDeclaredAnnotation(LanguageType.class); if (null != languageType) { applicationContext.registerBean(languageType.language(),clazz); } } }
private void lookupCollectionType(BeanFactory beanFactory) { if (beanFactory instanceof ListableBeanFactory) { ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory; Map<String, SpeakSPIService> beansMap = listableBeanFactory.getBeansOfType(SpeakSPIService.class); beansMap.forEach((key, value) -> { System.out.println("单一类型集合查找 " + key + " " + value.echo()); }); } }
|
1 2 3
| 单一类型集合查找 chinese chinese : 你好 单一类型集合查找 german german : hallo 单一类型集合查找 english english : hello
|
END