框架必备的SPI扩展机制
2019-05-09 本文已影响0人
十毛tenmao
很多框架中,为了实现更好的扩展机制,都是用了JDK支持的SPI机制,本文简单介绍了一下,并提供了快速实现的示例
SPI
Java SPI(Service Provider Interface)是一种JDK支持的扩展机制,可以为某个接口寻找服务实现的机制,有点类似于IOC的思想,将装配的控制权移到了程序之外。
Java SPI
SPI常用场景
- 数据库驱动加载
-
Servlet API中的
javax.servlet.ServletContainerInitializer
-
Dubbo中的类SPI机制,实现功能点的扩展(Dubbo可扩展机制实战)
接口定义方
类似于JDK中的java.sql.Driver
- 接口
public interface Scorer {
double score(String first, String second);
}
- 管理器
public class ScorerManager {
private static final Map<String, Scorer> SCORER_MAP = new HashMap<>(16);
static {
loadInitialScorers();
System.out.println("Scorer Manager initialized");
}
public static void registerScore(Scorer scorer) {
SCORER_MAP.putIfAbsent(scorer.getClass().getName(), scorer);
}
public static Scorer getScore(String scoreName) {
Scorer scorer = SCORER_MAP.get(scoreName);
if (scorer == null) {
throw new RuntimeException("cannot not find score with name:" + scoreName);
}
return scorer;
}
private static void loadInitialScorers() {
ServiceLoader<Scorer> serviceLoader = ServiceLoader.load(Scorer.class);
for (Scorer scorer : serviceLoader) {
//这里可以什么都不做,只是出发实现类的构造函数和静态模块
//其实也可以在这里完成注册,这样的话各个实现类会更加简单。不过在实现类中实现注册,会更加灵活,因为实现类在注册之前也许还需要做一些初始化的工作
}
}
}
接口实现方
类似于mysql中的com.mysq.Driver
和com.mysql.cj.jdbc.Drive
r, h2中的org.h2.Driver
- 实现类
public class BarScorer implements Scorer {
static {
ScorerManager.registerScore(new BarScorer());
System.out.println("BarScorer initialized");
}
@Override
public double score(String first, String second) {
return 0;
}
}
-
META-INF/services/com.tenmao.Scorer
文件
com.tenmao.BarScorer
接口使用方
- 用户应用程序
public static void main(String[] args) {
Scorer score = ScorerManager.getScore("com.tenmao.BarScorer");
double scoreOne = score.score("1", "1");
System.out.println(scoreOne);
}
注意事项
- 接口实现类必须包含无参构造函数(默认就有)
- 一般在XxxManager中不会注册实现类,而是交由实现类中静态模块完成。这样会存在一个问题,一个实现类会生成至少两个对象,其中一个对象被ServiceLoader生成后会被垃圾回收。