目录
背景
近来看Hasor、Dubbo等框架都说到微内核(Microkernel),想想jdk、srping、dubbo、hasor等都谈SPI,决定随笔记录下各个框架的spi,文章主要谈jdk、spring、dubbo的spi机制,出于目前的水平看来,spi就是一种偷懒方式,框架内都是针对接口编程,而实现自己可做可不做,谁做的好可以插进来就用就完了。
1、jdk的spi
jdk的spi先说说java.util.ServiceLoader这个类吧,先看看咋用的尝试下spi,
//定义服务提供接口
public interface JdkProvider {
int calculate(int x,int y);
}
//第一个服务
public class JdkFirstProvider implements JdkProvider {
public int calculate(int x, int y) {
System.out.println("JdkFirstProvider给出 2(x+y)");
return 2*(x+y);
}
}
//第二个服务
public class JdkSecondProvider implements JdkProvider {
public int calculate(int x, int y) {
System.out.println("JdkSecondProvider给出 x+y");
return x + y;
}
}
//消费者
public class JdkFirstCustomer {
public static void main(String[] args) {
ServiceLoader<JdkProvider> jdkProviders = ServiceLoader.load(JdkProvider.class);
jdkProviders.forEach(service-> System.out.println(service.calculate(3,8)));
}
}

附上一个时序图

jdk的spi大致就是委托ClassLoader去按META-INF/services/(接口全类名)加载各个实现类,之后反射实例化实现类存到map中,并将实例返回客户端。
2、Spring的spi
尝试下spring的spi
//bean接口
public interface SpringProvider {
String sayHello();
}
//接口实现1
public class SpringChineseProvider implements SpringProvider {
@Override
public String sayHello() {
return "SpringChineseProvider 大家好!";
}
}
//接口实现2
public class SpringUSProvider implements SpringProvider {
@Override
public String sayHello() {
return "SpringUSProvider hello everyone";
}
}
//客户端
public class SpringCustomer {
public static void main(String[] args) {
List<SpringProvider> factories = SpringFactoriesLoader.loadFactories(SpringProvider.class, null);
factories.forEach(factory->{
System.out.println(factory.sayHello());
});
}
}
详细说了jdk的spi这个就不细说了,也是通过ClassLoader去META-INF/spring.factories加载class,然后反射实例化返回,说说应用吧,像SpringBoot用这种方式去加载一些自动配置类,即引入xx-starter就能够自动向spring容器中注入许多配置好的组件。
2、Dubbo的spi
//注册工厂
public class DubboMultiRegistryFactory extends AbstractRegistryFactory {
@Override
protected Registry createRegistry(URL url) {
return new DubboMultiRegistry(url);
}
}
//注册
public class DubboMultiRegistry extends MulticastRegistry {
public DubboMultiRegistry(URL url) {
super(url);
System.out.println("自定义注册中心");
}
}
//客户端
public class TestExtensionLoader {
public static void main(String[] args) {
ExtensionLoader<RegistryFactory> loader = ExtensionLoader.getExtensionLoader(RegistryFactory.class);
RegistryFactory remote = loader.getExtension("multicast1");
Registry multicast = remote.getRegistry(new URL("multicast1", "224.5.6.7", 1234));
System.out.println(multicast);
}
}
//文件全名 META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
//文件内容 multicast1=com.zkr.learning.dubbo.ext.register.DubboMultiRegistryFactory
可以看到dubbo的spi主要通过ExtensionLoader实现,整个过程大致也是ClassLoader加载META-INF/dubbo下的文件得到class,取得class后反射实例化,不过在这里实例化后多个一步依赖注入的过程,而依赖注入交给了ExtensionFactory,ExtensionFactory做了几种实现其中就包括SpringExtensionFactory,那总结下来也就是1、根据不同Class类型,到指定位置获取class实例化;2、执行注入交给ExtensionFactory。
思考
好的框架都做了方便的扩展机制,spi只是其中一种方式,其它的还有诸如:BeanPostProcessor让开发者干预到bean的生命周期,ServletContainerInitializer、ServletContextInitializer、WebApplicationInitializer将ServletContext暴露出来,让开发者可以在web应用中对servlet做些额外操作,ConfigurableEnvironment关联MutablePropertySources让开发者实现环境的可动态配置,ChannelHandler、ChannelHandlerContext、ChannelPipeline让开发者对数据的读写能进行自定义的操作,当然还有一些其它的像Flowable的各种引擎配置器,这些都是一些接口,也是一种方式,针对接口编程,当然spi也是接口编程,只是它是找固定位置的文件,其它的可能是做一个循环链式处理,当然在这里也看到了jdk思想的强大,不管啥框架吧,都会借鉴jdk的思想,然后做一些额外的优化,比如上文的3种扩展机制,因此接下来还要好好看看javase、javaee的其它思想。
本文探讨了SPI(Service Provider Interface)扩展机制,详细介绍了JDK、Spring和Dubbo中的实现方式。JDK的SPI通过`ServiceLoader`加载实现类,Spring类似但用于自动配置,Dubbo的SPI增加了依赖注入步骤。SPI作为一种框架扩展手段,允许开发者方便地插入自己的实现。
2629





