java.util.ServiceLoader源码分析
发布日期:2021-06-30 17:51:28 浏览次数:2 分类:技术文章

本文共 5112 字,大约阅读时间需要 17 分钟。

java.util.ServiceLoader源码分析

回顾:

ServiceLoader类的使用(具体参考博客):

使用步骤基本为:

1、编写一个接口(标准)。

2、编写接口的实现类。

3、在/src/META-INF/services下新建一个文件,文件名为接口的包名+接口名。

4、把实现类的全名写在该文件中,一个实现类占一行。

我们还知道ServiceLoader的原理是Java的反射机制。配置文件的作用是告知实现类是什么,怎么才能找到,然后通过反射机制创建相关的实现类。

下面我们就通过这个思路来分析ServiceLoader的源码。

public class Main {
public static void main(String[] args) {
ServiceLoader
s = ServiceLoader.load(MyServer.class); Iterator
searches = s.iterator(); while (searches.hasNext()) {
Search curSearch = searches.next(); curSearch.search("test"); } }}

上面这个代码是本人使用ServiceLoader的时候的Main函数。首先我们需要根据接口名字来load我们的实现类。

下面是源码:我们发现load方法返回的是一个自己newServiceLoader对象。我们看一下newServiceLoader<>(service, loader);的构造方法。

public static  ServiceLoader load(Class service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl);}public static ServiceLoader load(Class service, ClassLoader loader){ return new ServiceLoader<>(service, loader);}

下面是构造方法:第一个参数是接口(上面的MyServer)的Class对象,第二个是类加载器。在这个构造函数中做了两件事,一是赋值,二是调用reload()方法。而reload方法的作用是根据接口的Class对象和类加载器来初始化lookupIterator对象。

private ServiceLoader(Class svc, ClassLoader cl) {
service = svc; loader = cl; reload();}public void reload() {
providers.clear(); lookupIterator = new LazyIterator(service, loader);}providers对象的定义是:private LinkedHashMap
providers = new LinkedHashMap<>();key是实现类的全名字,value是实现类的对象,并且该对象已经被强转成接口的类型。

lookupIteratorLazyIterator类的对象,这个类是ServerLoader的私有内部类。它实现了迭代器Iterator,所以它具有迭代器的功能。同时它还定义了这几个属性:

Class<S>service;//接口的Class对象

ClassLoader loader;//类加载器Enumeration
configs = null;//保存实现类的URLIterator
pending = null;//保存实现类的全名String nextName = null;//迭代器中下一个实现类的全名

调用reload方法之后,接着便是new了一个迭代器对象。这个是它的构造方法,作用只是初始化了两个属性。到了这个时候,还没有解析我们的配置文件,也还没有创建实现类的实例。这些是在什么时候是在那里触发的呢?在上面的main函数中,有searches.hasNext(),实际上调用的是LazyIterator类中的hasNext方法。

private LazyIterator(Class service, ClassLoader loader) {
this.service = service; this.loader = loader;}下面是hasNext方法的源码。我们注意到:String PREFIX = "META-INF/services/";service.getName()得到接口的全名(包名+接口名)。调用getResources(fullName)得到配置文件的URL集合(可能有多个配置文件)。通过这个方法parse(service, configs.nextElement());,参数是接口的Class对象和配置文件的URL来解析配置文件,返回值是配置文件里面的内容,也就是实现类的全名(包名+类名)。public boolean hasNext() {
if (nextName != null) {
return true; } if (configs == null) {
try {
String fullName = PREFIX + service.getName(); if (loader == null) configs = ClassLoader.getSystemResources(fullName); else configs = 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;}下面的源码是解析配置文件,注意:返回值是一个数组,里面就是names,实现类的全名。private Iterator
parse(Class service, URL u) throws ServiceConfigurationError{
InputStream in = null; BufferedReader r = null; ArrayList
names = new ArrayList<>(); try {
in = u.openStream(); r = new BufferedReader(new InputStreamReader(in, "utf-8")); int lc = 1; while ((lc = parseLine(service, u, r, lc, names)) >= 0); } catch (IOException x) {
fail(service, "Error reading configuration file", x); } finally {
try {
if (r != null) r.close(); if (in != null) in.close(); } catch (IOException y) {
fail(service, "Error closing configuration file", y); } } return names.iterator();}

到了这里,已经解析了配置文件,知道了实现类的全名,接下来怎么办?我们需要利用Java的反射机制,根据类的全名类创建对象。那么在那里触发什么时候触发呢?看我们上面的main函数,当我们执行searches.next();的时候,也就是执行下面的代码:

下面是迭代器LazyIterator类中的方法。这个c = Class.forName(cn, false, loader);,第一个参数是实现类的全名,第三个是类加载器,返回值是实现类的Class对象。再看下面的S p = service.cast(c.newInstance());作用是创建实例并强转成接口的类型,最后返回。结束!谢谢! public S next() {
if (!hasNext()) {
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 } public void remove() {
throw new UnsupportedOperationException(); }}

转载地址:https://liangyihuai.blog.csdn.net/article/details/50716369 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:nginx反向代理,负载均衡
下一篇:ServiceLoader跟DriverManager使用总结

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2024年04月15日 23时48分15秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章