首先引用API中的说明文档:
一个简单的服务提供者加载设施。
服务 是一个熟知的接口和类(通常为抽象类)集合。服务提供者 是服务的特定实现。提供者中的类通常实现接口,并子类化在服务本身中定义的子类。服务提供者可以以扩展的形式安装在 Java 平台的实现中,也就是将 jar 文件放入任意常用的扩展目录中。也可通过将提供者加入应用程序类路径,或者通过其他某些特定于平台的方式使其可用。
为了加载,服务由单个类型表示,也就是单个接口或抽象类。(可以使用具体类,但建议不要这样做。)一个给定服务的提供者包含一个或多个具体类,这些类扩展了此服务类型 ,具有特定于提供者的数据和代码。提供者类 通常不是整个提供者本身而是一个代理,它包含足够的信息来决定提供者是否能满足特定请求,还包含可以根据需要创建实际提供者的代码。提供者类的详细信息高度特定于服务;任何单个类或接口都不能统一它们,因此这里没有定义任何这种类型。此设施唯一强制要求的是,提供者类必须具有不带参数的构造方法,以便它们可以在加载中被实例化。
通过在资源目录 META-INF/services 中放置提供者配置文件 来标识服务提供者。文件名称是服务类型的完全限定 二进制名称 。该文件包含一个具体提供者类的完全限定二进制名称列表,每行一个。忽略各名称周围的空格、制表符和空行。注释字符为 '#' ('\u0023' , NUMBER SIGN );忽略每行第一个注释字符后面的所有字符。文件必须使用 UTF-8 编码。
....还有其他的一些就不拷贝过来了。
从说明中可以看出,这个机制允许服务的声明与实现的分离,具体使用哪个实现可以在运行时才决定,使用哪个实现取决于是否部署了该实现。下面是个简单的demo。
1、首先定义一个表示服务的接口:
package learn.serviceLoader.service; import java.util.ServiceLoader; public abstract class SimpleService { public abstract void doService(); private static boolean isInit = false; private static SimpleService service = null; public static final SimpleService getService() { if (!isInit) { ServiceLoader<SimpleService> serviceLoader = ServiceLoader .load(SimpleService.class); for (SimpleService s : serviceLoader) { service = s; break; } isInit = true; } return service; } }
此接口定义了服务的能力,同时提供了工厂方法来获取服务的实例。这个工厂方法就是通过此机制来查找服务的实现的。
2、实现服务提供者。
package learn.serviceLoader.serviceimpl; import learn.serviceLoader.service.SimpleService; public class StandarService extends SimpleService { @Override public void doService() { System.out .println("i am standar service, i implement SimpleService, so you get here ."); } }
就是实现服务接口的方法。
3、在服务提供者jar中下配置:
a、建立一个META-INF文件夹,在META-INF文件夹下建立一个services文件夹;
b、在services文件夹下建立服务配置文件,如:learn.serviceloader.service.SimpleService
里面的内容就是提供者的全限定类名:learn.serviceLoader.serviceimpl.StandarService # comment
4、服务的使用者:
package learn.serviceLoader.test; import learn.serviceLoader.service.SimpleService; public class TestServiceLoader { public static void main(String[] args) { SimpleService service = SimpleService.getService(); service.doService(); SimpleService service2 = SimpleService.getService(); service2.doService(); } }
服务的使用者通过服务接口的工厂方法来获取服务,使用者并不知道、也不需要知道使用的是哪个服务提供者。
对于复杂的使用,应该将服务的声明放在单独的api.jar包,不同的实现放在不同的jar,部署时只需要部署api.jar和特定的提供者jar包。此机制还支持 “reload ()
清除此加载器的服务者缓存,以重载所有服务者。”,应该可以预留方法,实现动态切换服务提供者。
这是个简单又强大的机制,可以方便地实现服务声明与实现的分离。