JDK/Dubbo/Spring 三種 SPI 機(jī)制,誰(shuí)更好?
作者:Corwien
來(lái)源:SegmentFault 思否社區(qū)
SPI 全稱(chēng)為 Service Provider Interface,是一種服務(wù)發(fā)現(xiàn)機(jī)制。SPI 的本質(zhì)是將接口實(shí)現(xiàn)類(lèi)的全限定名配置在文件中,并由服務(wù)加載器讀取配置文件,加載實(shí)現(xiàn)類(lèi)。這樣可以在運(yùn)行時(shí),動(dòng)態(tài)為接口替換實(shí)現(xiàn)類(lèi)。正因此特性,我們可以很容易的通過(guò) SPI 機(jī)制為我們的程序提供拓展功能。
package com.github.kongwu.spisamples;
public class LoggerFactory {
static {
SuperLoggerConfiguration configuration = new XMLConfiguration();
configuration.configure(configFile);
}
public static getLogger(Class clazz){
......
}
}
JDK SPI
META-INF/services/下創(chuàng)建一個(gè)com.github.kongwu.spisamples.SuperLoggerConfiguration文件(沒(méi)有后綴)。文件中只有一行代碼,那就是我們默認(rèn)的com.github.kongwu.spisamples.XMLConfiguration(注意,一個(gè)文件里也可以寫(xiě)多個(gè)實(shí)現(xiàn),回車(chē)分隔)META-INF/services/com.github.kongwu.spisamples.SuperLoggerConfiguration:
com.github.kongwu.spisamples.XMLConfiguration
ServiceLoader<SuperLoggerConfiguration> serviceLoader = ServiceLoader.load(SuperLoggerConfiguration.class);
Iterator<SuperLoggerConfiguration> iterator = serviceLoader.iterator();
SuperLoggerConfiguration configuration;
while(iterator.hasNext()) {
//加載并初始化實(shí)現(xiàn)類(lèi)
configuration = iterator.next();
}
//對(duì)最后一個(gè)configuration類(lèi)調(diào)用configure方法
configuration.configure(configFile);
package com.github.kongwu.spisamples;
public class LoggerFactory {
static {
ServiceLoader<SuperLoggerConfiguration> serviceLoader = ServiceLoader.load(SuperLoggerConfiguration.class);
Iterator<SuperLoggerConfiguration> iterator = serviceLoader.iterator();
SuperLoggerConfiguration configuration;
while(iterator.hasNext()) {
configuration = iterator.next();//加載并初始化實(shí)現(xiàn)類(lèi)
}
configuration.configure(configFile);
}
public static getLogger(Class clazz){
......
}
}
META-INF/services/com.github.kongwu.spisamples.SuperLoggerConfiguration:
com.github.kongwu.spisamples.ext.YAMLConfiguration
java -cp super-logger.jar:a.jar:b.jar:main.jar example.Main
super-logger.jar,擴(kuò)展的YAMLConfiguration SPI配置文件在main.jar,那么iterator獲取的最后一個(gè)元素一定為YAMLConfiguration。java -cp main.jar:super-logger.jar:a.jar:b.jar example.Main
Dubbo SPI
optimusPrime = org.apache.spi.OptimusPrime
bumblebee = org.apache.spi.Bumblebee
@SPI
public interface Robot {
void sayHello();
}
public class OptimusPrime implements Robot {
@Override
public void sayHello() {
System.out.println("Hello, I am Optimus Prime.");
}
}
public class Bumblebee implements Robot {
@Override
public void sayHello() {
System.out.println("Hello, I am Bumblebee.");
}
}
public class DubboSPITest {
@Test
public void sayHello() throws Exception {
ExtensionLoader<Robot> extensionLoader =
ExtensionLoader.getExtensionLoader(Robot.class);
Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
optimusPrime.sayHello();
Robot bumblebee = extensionLoader.getExtension("bumblebee");
bumblebee.sayHello();
}
}
**
來(lái)看看Dubbo中協(xié)議的接口:
@SPI("dubbo")
public interface Protocol {
......
}
com.alibaba.dubbo.rpc.Protocol文件如下:filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
com.alibaba.dubbo.rpc.protocol.webservice.WebServiceProtocol
thrift=com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol
memcached=com.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol
redis=com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol
rest=com.alibaba.dubbo.rpc.protocol.rest.RestProtocol
registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
qos=com.alibaba.dubbo.qos.protocol.QosProtocolWrapper
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getDefaultExtension();
//protocol: DubboProtocol
Spring SPI
META-INF/spring.factories,功能上和 JDK 的類(lèi)似,每個(gè)接口可以有多個(gè)擴(kuò)展實(shí)現(xiàn),使用起來(lái)非常簡(jiǎn)單://獲取所有factories文件中配置的LoggingSystemFactory
List<LoggingSystemFactory>> factories =
SpringFactoriesLoader.loadFactories(LoggingSystemFactory.class, classLoader);
# Logging Systems
org.springframework.boot.logging.LoggingSystemFactory=\
org.springframework.boot.logging.logback.LogbackLoggingSystem.Factory,\
org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.Factory,\
org.springframework.boot.logging.java.JavaLoggingSystem.Factory
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
# ConfigData Location Resolvers
org.springframework.boot.context.config.ConfigDataLocationResolver=\
org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,\
org.springframework.boot.context.config.StandardConfigDataLocationResolver
......
META-INF/spring.factories文件,只添加你要的那個(gè)配置,不要完整的復(fù)制一遍 Spring Boot 的 spring.factories 文件然后修改**
比如我只想添加一個(gè)新的 LoggingSystemFactory 實(shí)現(xiàn),那么我只需要新建一個(gè)
META-INF/spring.factories文件,而不是完整的復(fù)制+修改:org.springframework.boot.logging.LoggingSystemFactory=\
com.example.log4j2demo.Log4J2LoggingSystem.Factory
對(duì)
JDK SPI
DUBBO SPI
Spring SPI
三種 SPI 機(jī)制對(duì)比之下,JDK 內(nèi)置的機(jī)制是最弱雞的,但是由于是 JDK 內(nèi)置,所以還是有一定應(yīng)用場(chǎng)景,畢竟不用額外的依賴(lài);Dubbo 的功能最豐富,但機(jī)制有點(diǎn)復(fù)雜了,而且只能配合 Dubbo 使用,不能完全算是一個(gè)獨(dú)立的模塊;Spring 的功能和JDK的相差無(wú)幾,最大的區(qū)別是所有擴(kuò)展點(diǎn)寫(xiě)在一個(gè) spring.factories 文件中,也算是一個(gè)改進(jìn),并且 IDEA 完美支持語(yǔ)法提示。
各位看官們大佬們,你們覺(jué)得 JDK/Dubbo/Spring 三種 SPI 的機(jī)制,哪個(gè)更好呢?歡迎評(píng)論區(qū)留
參考
Introduction to the Service Provider Interfaces - Oracle Dubbo SPI - Apache Dubbo Creating Your Own Auto-configuration - Spring

評(píng)論
圖片
表情
