java spi
ddatsh
Service Provider Interface
“接口+策略模式+配置文件” 组合实现的动态加载机制
SPI 应用之一是可替换的插件机制
-
定义一组接口
-
写接口的一个或多个实现
-
src/main/resources/META-INF/services/接口名文件, 内容是要应用的实现类
-
用 ServiceLoader 加载配置文件中指定的实现
主要被框架的开发人员使用,如 java.sql.Driver 接口,不同厂商可以针对同一接口做出不同的实现
场景
-
jdbc
4.0 前要先加载驱动
Class.forName("com.mysql.jdbc.Driver")
4.0 利用 SPI 直接
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增强和优化
-
JDK SPI 一次性实例化扩展点所有实现,没用上的耗时/耗资源也实例化
-
扩展点加载失败,连扩展点的名称都拿不到
JDK ScriptEngine,getName(),如果依赖的 jruby.jar不存在,RubyScriptEngine实现类加载失败,失败原因被吃掉
用户执行ruby脚本时,会报不支持ruby,而不是真正失败的原因
-
扩展点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