Java Service Provider实现

Java提供的Service Provider机制其实就是一种DI,在实现时只考虑接口(也就是规范),由其他第三方去实现具体的功能。这个跟Spring的依赖注入概念上差不多,Spring是框架级别的依赖注入,SPI则是语言自身提供的,不依赖于任何框架。

 

具体使用方法非常简单,假设系统中实现一个接口,例如com.test.DemoInterface,由第三方提供具体实现,有以下步骤:

1. 第三方提供DemoInterface的实现类,例如com.test.thirdparty.DemoInterfaceImpl

2. 在生成的jar包里,在META-INF/services目录下,创建一个UTF-8编码的文件,名称为com.test.DemoInterface,然后内容只需要一行,就是实现类的全路径 (com.test.thirdparty.DemoInterfaceImpl)

3. 在系统中使用以下代码即可调用

ServiceLoader di = ServiceLoader.load(DemoInterface.class);

 需要注意的是,di拿到的结果里,providers列表默认为空,也就是ServiceLoader只是去加载类,但并没有实例化,只有当第一次使用后,列表里才会有记录。

Iterator ite = di.iterator();
while (ite.hasNext()) {
     System.out.println(ite.next());
}

 调用完以上代码后,再去di里查看,providers列表有值了。

 

这里涉及一个问题就是,如果有多个第三方jar都提供了实现,并且都有META-INF/services的文件,系统加载到的列表的优先顺序如何呢?

默认情况下,系统会按照jar包加载的顺序来排序,也就是先发现的排在最前面(这里大致提一下jar包的加载顺序,默认情况下是: 系统/应用本身的类文件以及classpath --> 系统/应用本身引用的jar包 --> 应用服务器的jar包),如果有2个jar包,01.jar和02.jar,默认情况下01.jar会优先于02.jar的加载(其实也就是默认按照文件管理器里的排序顺序来加载的)。

如果希望优先加载02.jar,只要在启动应用时,手动指定classpath里jar的顺序就可以了,或者把02.jar改名为00.jar。也就是说可以通过修改jar包的名称来改变加载顺序。

 

然后我们在实现系统时,大多数情况下,只需要取得第一个实现就可以了。

 

Jaxb里在实例化XmlOutputFactory时,就使用这种方式,使用抽象工厂的方式加载对应的Xml工厂类,只是在查找方式上更加多样化一些,实际项目实施时可以考虑。

那里面有一个FactoryFinder,在寻找具体实现的工厂类时,按照以下顺序查找:

1. 查找SystemProperty里有没有指定,有则返回

2. 查找指定目录下的某个配置文件,如果有指定,则返回

3. 使用Java Service Provider机制查找

 

附件有这2个类的参考代码,就是从Jaxb里搬来的。

你可能感兴趣的:(Java)