一、使用场景

一般使用接口的实现类都是静态new一个实现类赋值给接口引用,如下:

HelloService service = new HelloImpl();

如果需要动态的获取一个接口的实现类呢?全局扫描全部的Class,然后判断是否实现了某个接口?代价太大,一般不会这么做。一种合适的方式就是使用配置文件,把实现类名配置在某个地方,然后读取这个配置文件,获取实现类名。JDK给我们提供的TestServiceLoader 就是这种方式。

二、使用方式

在实现类的jar包的META-INF下新建一个文件夹services,并在services下新建一个文件,以接口的全限定名为文件名,内容为实现类的全限定名。

通过以下的例子来分析实现原理.

1. 新建一个接口,2个实现类。

package com.test.loader;public interface HelloService {public void sayHello();
}
package com.test.loader;public class Dog implements HelloService {@Overridepublic void sayHello() {System.out.println("bark bark bark...");}
}
package com.test.loader;public class Sheep implements HelloService {@Overridepublic void sayHello() {System.out.println("bleat bleat bleat...");}
}

2. 分别把接口、2个实现类打成3个jar包,放在D盘下。

3. 在Dog.jar、Sheep.jar分别加上META-INF下新建一个文件夹services,并在services下新建一个文件,以接口的全限定名为文件名,内容为实现类的全限定名。如下:

4. 使用指定的ClassLoader不包含实现类

public static void notInTheClassLoader() throws MalformedURLException {ClassLoader serviceCL = new URLClassLoader(new URL[] { new URL("file:" + "D:/HelloService.jar") },TestServiceLoader.class.getClassLoader().getParent());/* 指定的ClassLoader没有实现类,所以扫描不到META-INF/services/com.test.loader.HelloService */ServiceLoader<HelloService> helloServices = ServiceLoader.load(HelloService.class, serviceCL);Iterator<HelloService> it = helloServices.iterator();while (it.hasNext()) {HelloService service = it.next();service.sayHello();}}

结果不会打印任何信息。

5. 指定的ClassLoader包含实现类

public static void inTheClassLoader() throws MalformedURLException {ClassLoader serviceCL = new URLClassLoader(new URL[] { new URL("file:" + "D:/Dog.jar"), new URL("file:" + "D:/Sheep.jar") },TestServiceLoader.class.getClassLoader().getParent());/* 实现类在指定的ClassLoader,所以可以扫描META-INF/services/com.test.loader.HelloService */ServiceLoader<HelloService> helloServices = ServiceLoader.load(HelloService.class, serviceCL);Iterator<HelloService> it = helloServices.iterator();while (it.hasNext()) {HelloService service = it.next();service.sayHello();}}

结果如下:

bark bark bark...bleat bleat bleat...

6. 使用指定的ClassLoader加载接口类,不指定ClassLoader加载实现类。

public static void defaultClassLoader() throws MalformedURLException, ClassNotFoundException {ClassLoader serviceCL = new URLClassLoader(new URL[] { new URL("file:" + "D:/HelloService.jar"),new URL("file:" + "D:/Dog.jar"), new URL("file:" + "D:/Sheep.jar") },TestServiceLoader.class.getClassLoader().getParent());/* 默认会使用 ClassLoader.getSystemClassLoader() */ServiceLoader<HelloService> helloServices = ServiceLoader.load(((Class<HelloService>) (serviceCL.loadClass("com.test.loader.HelloService"))));Iterator<HelloService> it = helloServices.iterator();while (it.hasNext()) {HelloService service = it.next();service.sayHello();}}

结果不打印任何信息。

7. 把2个实现jar加到工程的Build Path里面,不指定ClassLoader。

public static void notSpecifyClassLoader() {ServiceLoader<HelloService> helloServices = ServiceLoader.load(HelloService.class);Iterator<HelloService> it = helloServices.iterator();while (it.hasNext()) {HelloService service = it.next();service.sayHello();}}

结果如下:

bark bark bark...bleat bleat bleat...

三、简单解析ServiceLoader

1. 构造函数,如果不指定ClassLoader或者指定的为null,则使用ClassLoader.getSystemClassLoader() ,即AppClassLoader。

    private ServiceLoader(Class<S> svc, ClassLoader cl) {service = Objects.requireNonNull(svc, "Service interface cannot be null");loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;reload();}

2. 遍历有两个方法

hasNext会调用hasNextService,如果指定的ClassLoader为空(一般不会为空,构造函数会初始化),则调用ClassLoader.getSystemClassLoader().getResources,否则调用指定的ClassLoader的getResources方法,获取META-INF/services/接口的全限定名称,如META-INF/services/com.test.loader.HelloService,从这个文件中找出实现类全限定名称。

next会调用nextService,根据hasNextService获取的实现类信息,使用指定的ClassLoader进行加载和实例化。

     private boolean hasNextService() {if (nextName != null) {return true;}if (configs == null) {try {String fullName = PREFIX + service.getName();if (loader == null)configs = ClassLoader.getSystemResources(fullName);elseconfigs = loader.getResources(fullName);} catch (IOException x) {fail(service, "Error locating configuration files", x);}}while ((pending == null) || !pending.hasNext()) {if (!configs.hasMoreElements()) {return false;}pending = parse(service, configs.nextElement());}nextName = pending.next();return true;}private S nextService() {if (!hasNextService())throw new NoSuchElementException();String cn = nextName;nextName = null;Class<?> c = null;try {c = Class.forName(cn, false, loader);} catch (ClassNotFoundException x) {fail(service,"Provider " + cn + " not found");}if (!service.isAssignableFrom(c)) {fail(service,"Provider " + cn  + " not a subtype");}try {S p = service.cast(c.newInstance());providers.put(cn, p);return p;} catch (Throwable x) {fail(service,"Provider " + cn + " could not be instantiated",x);}throw new Error();          // This cannot happen}

注:以上代码基于JDK1.8.0_144。

Java ServiceLoader使用和解析相关推荐

  1. Exception in thread main java.lang.Error: 无法解析的编译问题: 方法 main 不能声明为 static;只能在静态类型或顶级类型中才能声明静态方法

    Exception in thread "main" java.lang.Error: 无法解析的编译问题: 方法 main 不能声明为 static:只能在静态类型或顶级类型中才 ...

  2. 四种Java线程池用法解析

    四种Java线程池用法解析 本文为大家分析四种Java线程池用法,供大家参考,具体内容如下 http://www.jb51.net/article/81843.htm 1.new Thread的弊端 ...

  3. 你所需要的java基础篇深入解析大汇总

    java基础篇深入解析大总结 java基础(一) 深入解析基本类型 java基础(二) 自增自减与贪心规则 java基础(三) 加强型for循环与Iterator java基础(四) java运算顺序 ...

  4. java解析xml实例_在java中使用dom解析xml的示例分析

    本篇文章介绍了,在java中使用dom解析xml的示例分析.需要的朋友参考下 dom是个功能强大的解析工具,适用于小文档 为什么这么说呢?因为它会把整篇xml文档装载进内存中,形成一颗文档对象树 总之 ...

  5. JAVA中利用DOM解析XML文档

    JAVA中利用DOM解析XML文档 package org.sws.utils; import java.io.File;import java.io.IOException; import java ...

  6. java 解析gson_使用Java和Google GSON解析ESPN API

    java 解析gson 在我的第一篇文章中,我将解释如何解析ESPN API. 可以在http://developer.espn.com/docs上找到API文档. 首先,您需要请求一个API密钥,然 ...

  7. java docx文档解析_带有docx4j的Java Word(.docx)文档

    java docx文档解析 几个月前,我需要创建一个包含许多表和段落的动态Word文档. 过去,我曾使用POI来实现此目的,但是我发现它很难使用,并且在创建更复杂的文档时对我来说效果不佳. 因此,对于 ...

  8. 使用Java和Google GSON解析ESPN API

    在我的第一篇文章中,我将解释如何解析ESPN API. 可以在http://developer.espn.com/docs上找到API文档. 首先,您需要请求一个API密钥,然后可以开始查询REST ...

  9. java解析shell命令_Android中执行java命令的方法及java代码执行并解析shell命令

    这篇文章给大家介绍Android中执行java命令的方法及java代码执行并解析shell命令,需要的朋友一起学习 android中执行java命令的方法大家都晓得吗,下面一段内容给大家带来了具体解析 ...

  10. java微信开发API解析(二)-获取消息和回复消息

    java微信开发API解析(二)-获取消息和回复消息 说明 * 本演示样例依据微信开发文档:http://mp.weixin.qq.com/wiki/home/index.html最新版(4/3/20 ...

最新文章

  1. Windows 8开机时间
  2. 网站推广专员浅析不做大幅修改如何调整网站推广内容?
  3. 新建angular-cli项目
  4. Module not found: Error: Can't resolve 'less-loader' in 'E:\NodeDemo\vue_assistant_lsp'
  5. 权限管理-整合SpringSecurity
  6. VVC编码进展:码率降低,速度仍需提升
  7. 第七季1:MP4文件格式解析
  8. user_tab_columns是什么
  9. Java中什么时候throws_何时在Java方法声明中使用throws?
  10. 【联合仿真】Adams六关节机械臂与Matlab/Simulink的联合仿真(上)
  11. RTSP HTTP流媒体播放器demo
  12. 遗传算法和禁忌搜索解TSP
  13. AutoRunner学习——下载安装
  14. Google Map Web服务API
  15. 干货!详解服务器端和移动端性能测试指标
  16. java文档中心_Java文档 - SendCloud 文档中心 - SendCloud 文档中心
  17. 二级分销定制技术开发系统
  18. SpringBoot 集成接口文档,老鸟们也被打脸了!
  19. 剑指 Offer 05. 替换空格 三种方法
  20. 瑞星升级包 下载网站

热门文章

  1. matlab 仿真短路故障设置,基于MatlabSimulink的电力系统故障仿真与
  2. Linux命令任务管理器,如何在:Linux下面启动任务管理器
  3. windows7蓝牙怎么打开_英特尔升级Wi-Fi 和蓝牙驱动,Win10 更新5月版稳了
  4. 宁盾堡垒机双因素认证方案
  5. jsjq面试笔记(上)
  6. Linked Data_百度百科
  7. 2016年腾讯实习生校园招聘-电面2
  8. 【Python游戏】实现一个恶搞游戏,粉丝大战xxx小游戏(狗头保命) | 附带源码
  9. 积分大比拼,哪家银行信用卡积分最值钱?
  10. 《架构即未来》笔记1——概述全书内容