Java SPI机制
什么是SPI
SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。
SPI有什么用
调用者根据实际使用需要,启用、扩展、或者替换框架的实现策略
如:
数据库加载驱动 java.sql.DriverManager
AccessController.doPrivileged(new PrivilegedAction() {
public void run() {
//此处使用JDK的SPI加载机制,加载所有的驱动,即java.sql.Driver接口的实现类
ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);
Iterator driversIterator = loadedDrivers.iterator();
/* Load these drivers, so that they can be instantiated.
* It may be the case that the driver class may not be there
* i.e. there may be a packaged driver with the service class
* as implementation of java.sql.Driver but the actual class
* may be missing. In that case a java.util.ServiceConfigurationError
* will be thrown at runtime by the VM trying to locate
* and load the service.
*
* Adding a try catch block to catch those runtime errors
* if driver not available in classpath but it's
* packaged as service and that service is there in classpath.
*/
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});
println("DriverManager.initialize: jdbc.drivers = " + drivers);
Dubbo
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SPI {
/**
* default extension name
*/
String value() default "";
}
SPI工作机制
代码片段 java.util.ServiceLoader
/**
* Creates a new service loader for the given service type, using the
* current thread's {@linkplain java.lang.Thread#getContextClassLoader
* context class loader}.
*
*
An invocation of this convenience method of the form
*
*
* ServiceLoader.load(service)
*
* is equivalent to
*
*
* ServiceLoader.load(service,
* Thread.currentThread().getContextClassLoader())
*
* @param the class of the service type
*
* @param service
* The interface or abstract class representing the service
*
* @return A new service loader
*/
public static ServiceLoader load(Class service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
通过调用load方法来实现对象的创建
最终使用Class.forName("class name").newInstance()来创建接口实现
SPI使用
当服务提供者提供了接口的一种具体实现后,在jar包的META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名;
接口实现类所在的jar包放在主程序的classpath中;
主程序通过ServiceLoader.load()动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM;
SPI的实现类必须携带一个不带参数的构造方法;
参考: