SPI其实就是一种约定好规范,规定好在classpath下的META-INF/services/路径下,放置指定接口的指定实现类,然后java提供了ServiceLoader类,这个类会去读取这个文件,然后通过文件中指定的接口实现类,然后class.forName()加载这个类。
总是:就是javase约定好规范,然后就能通过serviceLoader,来找到具体的实现类。
这样,可以,直接用接口进行调用,然后谁想实现他们的功能,谁就去实现这个接口,然后把这个接口实现类放到META-INF/services/路径就好了。
这里有个网上的例子:
public interface IShout {
void shout();
}
public class Dog implements IShout {
@Override
public void shout() {
System.out.println("wang wang");
}
}
public class Cat implements IShout {
@Override
public void shout() {
System.out.println("miao miao");
}
}
META-INF/services/org.foo.demo.IShout文件内容
org.foo.demo.Dog
org.foo.demo.Cat
测试
public class SPIMain {
public static void main(String[] args) {
ServiceLoader<IShout> shouts = ServiceLoader.load(IShout.class);
for (IShout s : shouts) {
s.shout();
}
}
}
既然是一个约定好的规范,那么,自然,你也可以约定好一个规范,然后自己做一个类似serviceLoader的加载器,然后去加载实现类就可以了,只要,让别人遵守这个规范就好了。
比如,dubbo:
对于Protocal的接口,提供实现类:
/META_INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol
然后通过自己的加载器,加载就完了。
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
dubbo为什么要自己实现spi呢?
因为,dubbo需要一个接口提供多个实现类,然后通过注解的方式,指定使用哪个实现类。
比如,它的/META_INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件内容是:
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
http=com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
以properties的方式,对实现类提供了一个标识符,然后通过@SPI("dubbo")这样的方式,实现加载指定的实现类,而这个方式,是java自带的spi做不到的。
@SPI("dubbo")
public interface Protocol {
int getDefaultPort();
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
void destroy();
}
转载于:https://www.jianshu.com/p/1a301ae3c650