Java之ServiceLoader

  转载请注明源出处:http://www.cnblogs.com/lighten/p/6946683.html

1.简介

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

2.例子

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

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

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<TestService> loader = ServiceLoader.load(TestService.class);
		for(TestService service : loader) {
			System.out.println(service.sayHello());
		}
	}

}

  运行一下结果如下:

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

3.源码解读

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

  1.先看属性:

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

  2.再看loader方法:

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

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

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

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

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

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

  5.parse解析文件步骤:

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

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

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

 

posted @ 2017-06-05 19:31  dark_saber  阅读(1618)  评论(0编辑  收藏  举报