java spi

Service Provider Interface

“接口+策略模式+配置文件” 组合实现的动态加载机制

SPI 应用之一是可替换的插件机制

  1. 定义一组接口

  2. 写接口的一个或多个实现

  3. src/main/resources/META-INF/services/接口名文件, 内容是要应用的实现类

  4. 用 ServiceLoader 加载配置文件中指定的实现


主要被框架的开发人员使用,如 java.sql.Driver 接口,不同厂商可以针对同一接口做出不同的实现

场景

  • jdbc

    4.0 前要先加载驱动

1
  Class.forName("com.mysql.jdbc.Driver") 

4.0 利用 SPI 直接

1
2
    String url = "jdbc:xxxx://xxxx:xxxx/xxxx";
    Connection conn = DriverManager.getConnection(url,username,password);
  • common-logging

    META-INF/services/org.apache.commons.logging.LogFactory(是个抽象类)

  • Dubbo(重新实现,扩展了java SPI)

JDK SPI缺点/dubbo增强和优化

  1. JDK SPI 一次性实例化扩展点所有实现,没用上的耗时/耗资源也实例化

  2. 扩展点加载失败,连扩展点的名称都拿不到

    JDK ScriptEngine,getName(),如果依赖的 jruby.jar不存在,RubyScriptEngine实现类加载失败,失败原因被吃掉

    用户执行ruby脚本时,会报不支持ruby,而不是真正失败的原因

  3. 扩展点IoC和AOP支持,一个扩展点可setter注入其它扩展点

DUBBO SPI

META-INF/dubbo/接口

内容为:配置名=扩展实现类,多个实现类换行符分隔

配置文件是放在自己的jar包内,不是dubbo本身的jar包内,Dubbo会全ClassPath扫描


  • 代码(1)获取当前 SPI 接口对应的 ExtensionLoader
  • 代码(2)获取适配器实例,内部首先获取该 spi 对应的所有实现类的 Class 对象,然后创建适配器实例,最后注入该适配器依赖的其他扩展点
  • 代码(8)根据名称获取具体的 spi 实现类,内部是创建一个实现类的实例,并使用 warp 类进行包装后返回

SPI 本地化扩展

与ServiceLoader 的 SPI 稍有不同,要打包成 jar 包后,放 jre/lib/ext

CalendarData/CalendarName/CurrencyName/LocaleName/LocaleService/ResourceBundleControl/TimeZoneName Provider