本文共 3040 字,大约阅读时间需要 10 分钟。
1.什么是SPI?
SPI:Service Provider Interface,是JDK提供的为某个接口寻找服务实现的机制。为了实现不对实现类进行硬编码,在程序里动态指明。
2.使用场景?
2.1 common-logging
apache最早提供的日志的门面接口。只有接口,没有实现。具体方案由各提供商实现, 发现日志提供商是通过扫描 META-INF/services/org.apache.commons.logging.LogFactory配置文件,通过读取该文件的内容找到日志提工商实现类。只要我们的日志实现里包含了这个文件,并在文件里制定 LogFactory工厂接口的实现类即可。
2.2 JDBC
META-INF.services包下有个java.sql.Driver文件:
com.mysql.jdbc.Driver com.mysql.fabric.jdbc.FabricMySQLDriver其中com.mysql.jdbc.Driver类是实现了java.sql.Driver接口,下面那com.mysql.fabric.jdbc.FabricMySQLDriver才是真正的实现类
3.例子:
3.1接口:
package com.test.spi;public interface HelloInterface { public void sayHello();}
3.2实现类:
public class ImageHello implements HelloInterface { @Override public void sayHello() { System.out.println("Image Hello"); }}
public class TextHello implements HelloInterface { @Override public void sayHello() { System.out.println("Text Hello."); }}
3.3 文件
在src路径下的META-INF/services/com.test.spi.HelloInterface
com.test.spi.impl.TextHellocom.test.spi.impl.ImageHello
3.4 Main方法
public class SPIMain { public static void main(String[] args) { ServiceLoaderloaders = ServiceLoader.load(HelloInterface.class); for (HelloInterface in : loaders) { in.sayHello(); } }}
3.5 运行结果:
Text Hello.
Image Hello4.原理分析
4.1ServiceLoader实现了Iterable这个接口,因此可以在遍历的时候,重新进行lazy加载。
public final class ServiceLoaderimplements Iterable{ private static final String PREFIX = "META-INF/services/"; // The class or interface representing the service being loaded private final Classservice; // The class loader used to locate, load, and instantiate providers private final ClassLoader loader; // The access control context taken when the ServiceLoader is created private final AccessControlContext acc; // Cached providers, in instantiation order private LinkedHashMapproviders = new LinkedHashMap<>(); // The current lazy-lookup iterator private LazyIterator lookupIterator;}
4.2
private class LazyIterator implements Iterator{ private S nextService() { if (!hasNextService()) throw new NoSuchElementException(); String cn = nextName; nextName = null; Class c = null; try { //延迟加载 c = Class.forName(cn, false, loader); } catch (ClassNotFoundException x) { fail(service, "Provider " + cn + " not found"); } if (!service.isAssignableFrom(c)) { fail(service, "Provider " + cn + " not a subtype"); } try { S p = service.cast(c.newInstance()); providers.put(cn, p); return p; } catch (Throwable x) { fail(service, "Provider " + cn + " could not be instantiated", x); } throw new Error(); // This cannot happen } }
参考:
1.
转载地址:http://xpqli.baihongyu.com/