前言
查看mysql-connector-java-5.1.47.jar包,可以看到
1.png
可以看到有一个java.sql.Driver这样的文件,这个是jdk给数据库厂商定的协议,第三方厂商可以做相应的实现来对接自己的服务。
定义
API (Application Programming Interface)在大多数情况下,都是实现方制定接口并完成对接口的实现,调用方仅仅依赖接口调用,且无权选择不同实现。 从使用人员上来说,API 直接被应用开发人员使用。
SPI (Service Provider Interface)是调用方来制定接口规范,提供给外部来实现,调用方在调用时则选择自己需要的外部实现。 从使用人员上来说,SPI 被框架扩展人员使用。
说人话:
API:自己定义自己实现
SPI:自己定义别人实现,调用的时候动态加载。
试用
1,首先定义SPI标准接口,你可以理解为类似于jdk的java.sql.Driver
3.png
代码:
public interface ILiveCdn {
void upload(String path);
void play(String video);
}
2,实现标准接口,你可以理解为类似于mysql-connector-java-xxx.jar
4.png
public class AliyunLiveCdnImpl implements ILiveCdn {
protected static final Logger LOGGER = LoggerFactory.getLogger(AliyunLiveCdnImpl.class);
@Override
public void upload(String path) {
LOGGER.info("AliyunLiveCdn upload:{}", path);
}
@Override
public void play(String video) {
LOGGER.info("AliyunLiveCdn play:{}", video);
}
}
public class ChinanetLiveCdnImpl implements ILiveCdn {
protected static final Logger LOGGER = LoggerFactory.getLogger(ChinanetLiveCdnImpl.class);
@Override
public void upload(String path) {
LOGGER.info("ChinanetLiveCdn upload:{}", path);
}
@Override
public void play(String video) {
LOGGER.info("ChinanetLiveCdn play:{}", video);
}
}
3,调用方,可以理解为调用jdk的jdbc服务的开发者
5.png
代码:
public class SpiClientTest {
public static void main(String[] args) {
test1();
}
private static void test1() {
ServiceLoader<ILiveCdn> liveCdns = ServiceLoader.load(ILiveCdn.class);
for (ILiveCdn u : liveCdns) {
u.upload("http://www.baidu.com");
u.play("play movie");
}
}
}
可以看到,调用成功~。
总结
优点:
不需要改动源码就可以实现扩展,解耦。
实现扩展对原来的代码几乎没有侵入性。
只需要添加配置就可以实现扩展,符合开闭原则。
缺点:
只能遍历所有的实现,并全部实例化。
配置文件中只是简单的列出了所有的扩展实现,而没有给他们命名。导致在程序中很难去准确的引用它们。
扩展如果依赖其他的扩展,做不到自动注入和装配。
扩展很难和其他的框架集成,比如扩展里面依赖了一个Spring bean,原生的Java SPI不支持。
多个并发多线程使用 ServiceLoader 类的实例是不安全的。
网友评论