Java SPI机制

Java SPI机制

什么是SPI

SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。

SPI有什么用

调用者根据实际使用需要,启用、扩展、或者替换框架的实现策略

如:

  • 数据库加载驱动 java.sql.DriverManager

    AccessController.doPrivileged(new PrivilegedAction<Void>() {
      public void run() {
    
        //此处使用JDK的SPI加载机制,加载所有的驱动,即java.sql.Driver接口的实现类
        ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
        Iterator<Driver> 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}.
 *
 * <p> An invocation of this convenience method of the form
 *
 * <blockquote><pre>
 * ServiceLoader.load(<i>service</i>)</pre></blockquote>
 *
 * is equivalent to
 *
 * <blockquote><pre>
 * ServiceLoader.load(<i>service</i>,
 *                    Thread.currentThread().getContextClassLoader())</pre></blockquote>
 *
 * @param  <S> the class of the service type
 *
 * @param  service
 *         The interface or abstract class representing the service
 *
 * @return A new service loader
 */
public static <S> ServiceLoader<S> load(Class<S> 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的实现类必须携带一个不带参数的构造方法;

参考:

posted @ 2019-11-18 16:35  江左笑笑生  阅读(103)  评论(0)    收藏  举报