SPI机制总结
Servie Provider Inteface, 为扩展提供了可能;
JDK SPI机制
-
定义接口
接口WelldoneLog
public interface WelldoneLog { boolean support(String type); void info(); void error(); }
需要扩展的实现类
// WelldoneLog实现类1 Log4jLogImpl public class Log4jLogImpl implements WelldoneLog { @Override public boolean support(String type) { return "log4j".equalsIgnoreCase(type); } @Override public void info() { System.out.println("i'm log4j info"); } @Override public void error() { System.out.println("i'm log4j error"); } }
// WelldoneLog实现类2 LogbackLogImpl public class LogbackLogImpl implements WelldoneLog { @Override public boolean support(String type) { return "logback".equalsIgnoreCase(type); } @Override public void info() { System.out.println("i'm logback info"); } @Override public void error() { System.out.println("i'm logback error"); } }
-
在resources/META-INF/services目录创建文件,文件名必须跟接口的全限定类名一致,内容为实现类的全限定类名
如:文件resources/META-INF/services/com.welldone.spi.WelldoneLog
com.welldone.spi.Log4jLogImpl com.welldone.spi.LogbackLogImpl
-
使用ServiceLoader动态加载扩展的实现类
public class SpiUseDemo { public static void main(String[] args) { // 通过ServiceLoader获取SPI该接口的所有实现实例 ServiceLoader<WelldoneLog> allImpl = ServiceLoader.load(WelldoneLog.class); Iterator<WelldoneLog> iterator = allImpl.iterator(); Scanner scanner = new Scanner(System.in); String intputName = scanner.nextLine(); // 可以通过外部输入名字来决定调哪个实现 while (iterator.hasNext()) { WelldoneLog next = iterator.next(); if (next.support(intputName)) { // 调匹配输入inputName那个 next.info(); } } } }
Spring中的SPI
-
定义接口
public interface WelldoneLog { boolean support(String type); void info(); void error(); }
-
接口的扩展实现类
// WelldoneLog实现类1 Log4jLogImpl public class Log4jLogImpl implements WelldoneLog { @Override public boolean support(String type) { return "log4j".equalsIgnoreCase(type); } @Override public void info() { System.out.println("i'm log4j info"); } @Override public void error() { System.out.println("i'm log4j error"); } }
// WelldoneLog实现类2 LogbackLogImpl public class LogbackLogImpl implements WelldoneLog { @Override public boolean support(String type) { return "logback".equalsIgnoreCase(type); } @Override public void info() { System.out.println("i'm logback info"); } @Override public void error() { System.out.println("i'm logback error"); } }
-
resource/META-INF/spring.factories
内容如下:接口全限定类名=实现类全限定类名[,实现类全限定类名]… (多个用逗号分隔,反斜杠是换行的意思)
com.welldone.spi.WelldoneLog=\ com.welldone.spi.Log4jLogImpl,\ com.welldone.spi.LogbackLogImpl
-
API获取实现了接口的实现类实例
@RunWith(SpringJUnit4ClassRunner.class) public class TestSpringSPI { @Test public void test() { // 通过SpringFactoriesLoader获取到spring.factories中的所有实现类的实例 List<WelldoneLog> impls = SpringFactoriesLoader.loadFactories(WelldoneLog.class, ClassUtils.getDefaultClassLoader()); for (WelldoneLog impl : impls) { impl.info(); } } }
Dubbo中的SPI
- dubbo内部SPI目录resource/META-INF/dubbo/internal
- 用户使用SPI机制对dubbo做扩展时,目录放在resource/META-INF/dubbo
- 文件以接口的全限定类名命名,如com.welldone.spi.WelldoneLog
- 文件内容以key=value格式编写(key为自定义简写名字如log4j, value为对应实现类的全限定类名如com.welldone.spi.Log4jLogImpl, 多个则写成多个key=value)
- 使用ExtensionLoader API进行实现类实例的加载