serviceloader java_Java之ServiceLoader

JDK1.6之后,java.util包下多了一个类ServiceLoader,其实现了Iterable接口(可以直接进行for-each loop)。这个类的主要作用是提供了一种服务发现机制,并没有什么深奥的内容。实现过程也十分简单,下面通过一个例子来详细讲解一下如何使用和其实现过程。本文基于jdk1.8。

2.例子

ServiceLoader的使用是要在根目录有一个文件夹META-INF/services/,其主要是对这个目录进行扫描,文件名是你需要提供服务的类(接口)全称(即包名.类名)。类中的内容,一行就是改接口的一个具体的实现类。包结构如下:

5b518ea5f278e4502853902cec536baf.png

服务的定义和实现具体如下:

public interface TestService {

public String sayHello();

}

public class TestServiceImpl1 implements TestService {

@Override

public String sayHello() {

return "hello, test1";

}

}

public class TestServiceImpl2 implements TestService {

@Override

public String sayHello() {

return "hello, test2";

}

}

配置文件就是实现类的类全称:

com.java.util.test.TestServiceImpl1

com.java.util.test.TestServiceImpl2

配置完成后就是最主要的使用方法了:

public class ServiceLoaderTest {

public static void main(String[] args) {

ServiceLoader loader = ServiceLoader.load(TestService.class);

for(TestService service : loader) {

System.out.println(service.sayHello());

}

}

}

运行一下结果如下:

6f3ad775c0571be02bc6f1d88c0ce1c2.png

使用起来也很简单,就是通过静态方法load进行加载,然后通过for-each循环遍历,使用这个实现类。

3.源码解读

ServiceLoader解析服务并不是加载就立刻解析的,其采取的是懒加载的方式,也就是第一次使用这个loader对象的时候才进行解析。

1.先看属性:

d6f7ce2b7d6f1b733c6b65bc2460b790.png

这里就定义了读取的文件名,service是需要加载的类,loader是类加载器,默认使用当前加载器,providers是服务提供者,lookupIterator就是懒加载的具体实现了。

2.再看loader方法:

9f2fb8d47f76f48f0f988271634f2768.png

loader方法就是初始化了一些属性,清空了providers。

3.关键的iterator()方法,这个是Iterable接口需要实现的一个方法:

d1ea5d26ea419975d681b183fb896c17.png

其遍历,是先通过providers来遍历,因为解析完成后这里应该是有值的,如果没值,就通过lookupIterator去遍历,这里也就看出并不是loader就开始读取然后解析,而是在遍历的时候,没找到解析的值,再通过设置的懒加载遍历器,去解析遍历。

4.核心的LazyIterator,其实现了Iterator接口,next和hasNext实际上调用的是其另两个方法,nextService与hasNextService

5104ad73f1e03c37ccc0ddaa336ca85a.png

上面代码很简单,就是读取META-INF/services文件夹下,所加载类的类全称名的文件,通过parse方法解析。

09498b15b2b66ed8c438bc49f1cf1658.png

nextService方法也很简单,就是通过实现类的类全称,通过Class.forName进行加载。然后判断该实现类是不是加载类的子类,service.isAssignableFrom,再通过service.cast()方法进行转换成所加载的类。

5.parse解析文件步骤:

9e01588bb153f64d796aa83953204da1.png

用utf-8的格式读取,然后调用parseLine方法,一行行读取到name这个迭代器中。

a3823d5913e2dcf6b2a7e576e460ea05.png

读取一行,截取#注释前面的内容,通过trim方法去掉两端空格。若还有值,则判断是否有空格或制表符,有即不符合规则,查看是不是Java标识的开头。都满足规则,如果providers中没有且未解析过相同的,就放入迭代器中。

至此,这个解析过程就明了了,就是读取指定文件夹的指定文件,文件中存的类全称,通过Class.forName,拿到字节码,再通过class.newInstance方法获得实例,将其cast成所要加载的类。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值