java.util.ServiceLoader源码分析
java.util.ServiceLoader源码分析
回顾:
ServiceLoader类的使用(具体参考博客http://blog.csdn.net/liangyihuai/article/details/50716035):
使用步骤基本为:
1、编写一个接口(标准)。
2、编写接口的实现类。
3、在/src/META-INF/services下新建一个文件,文件名为接口的包名+接口名。
4、把实现类的全名写在该文件中,一个实现类占一行。
我们还知道ServiceLoader的原理是Java的反射机制。配置文件的作用是告知实现类是什么,怎么才能找到,然后通过反射机制创建相关的实现类。
下面我们就通过这个思路来分析ServiceLoader的源码。
public class Main { public static void main(String[] args) { ServiceLoader<Search> s = ServiceLoader.load(MyServer.class); Iterator<Search> searches = s.iterator(); while (searches.hasNext()) { Search curSearch = searches.next(); curSearch.search("test"); } } }
上面这个代码是本人使用ServiceLoader的时候的Main函数。首先我们需要根据接口名字来load我们的实现类。
下面是源码:我们发现load方法返回的是一个自己new的ServiceLoader对象。我们看一下newServiceLoader<>(service, loader);的构造方法。
public static <S> ServiceLoader<S> load(Class<S> service) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); } public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader){ return new ServiceLoader<>(service, loader); }
下面是构造方法:第一个参数是接口(上面的MyServer)的Class对象,第二个是类加载器。在这个构造函数中做了两件事,一是赋值,二是调用reload()方法。而reload方法的作用是根据接口的Class对象和类加载器来初始化lookupIterator对象。
private ServiceLoader(Class<S> svc, ClassLoader cl) { service = svc; loader = cl; reload(); } public void reload() { providers.clear(); lookupIterator = new LazyIterator(service, loader); }providers对象的定义是:private LinkedHashMap<String,S> providers = new LinkedHashMap<>();key是实现类的全名字,value是实现类的对象,并且该对象已经被强转成接口的类型。
lookupIterator是LazyIterator类的对象,这个类是ServerLoader的私有内部类。它实现了迭代器Iterator,所以它具有迭代器的功能。同时它还定义了这几个属性:
Class<S>service;//接口的Class对象
ClassLoader loader;//类加载器 Enumeration<URL> configs = null;//保存实现类的URL Iterator<String> pending = null;//保存实现类的全名 String nextName = null;//迭代器中下一个实现类的全名
调用reload方法之后,接着便是new了一个迭代器对象。这个是它的构造方法,作用只是初始化了两个属性。到了这个时候,还没有解析我们的配置文件,也还没有创建实现类的实例。这些是在什么时候是在那里触发的呢?在上面的main函数中,有searches.hasNext(),实际上调用的是LazyIterator类中的hasNext方法。
private LazyIterator(Class<S> 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<String> parse(Class service, URL u) throws ServiceConfigurationError { InputStream in = null; BufferedReader r = null; ArrayList<String> 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(); } }
java.util.ServiceLoader源码分析相关推荐
- 并发编程5:Java 阻塞队列源码分析(下)
上一篇 并发编程4:Java 阻塞队列源码分析(上) 我们了解了 ArrayBlockingQueue, LinkedBlockingQueue 和 PriorityBlockingQueue,这篇文 ...
- Java并发-ReentrantReadWriteLock源码分析
ReentrantLock实现了标准的互斥重入锁,任一时刻只有一个线程能获得锁.考虑这样一个场景:大部分时间都是读操作,写操作很少发生:我们知道,读操作是不会修改共享数据的,如果实现互斥锁,那么即使都 ...
- Java集合类框架源码分析 之 LinkedList源码解析 【4】
上一篇介绍了ArrayList的源码分析[点击看文章],既然ArrayList都已经做了介绍,那么作为他同胞兄弟的LinkedList,当然必须也配拥有姓名! Talk is cheap,show m ...
- 【Java】equals源码分析
源码分析 This class consists of static utility methods for operating on objects. These utilities include ...
- JAVA集合专题+源码分析
文章目录 Java集合专题 集合和数组的区别 数组 集合 区别 集合体系结构介绍 单列集合 [Collection ] Collection接口 迭代器 迭代器原理 增强for循环 List接口 对集 ...
- java activerecord.db_JFinal 源码分析 [DB+ActiveRecord]
我记得以前有人跟我说,"面试的时候要看spring的源码,要看ioc.aop的源码"那为什么要看这些开源框架的源码呢,其实很多人都是"应急式"的去读,就像读一篇 ...
- Java中ArrayList源码分析
一.简介 ArrayList是一个数组队列,相当于动态数组.每个ArrayList实例都有自己的容量,该容量至少和所存储数据的个数一样大小,在每次添加数据时,它会使用ensureCapacity()保 ...
- Java容器 | 基于源码分析List集合体系
一.容器之List集合 List集合体系应该是日常开发中最常用的API,而且通常是作为面试压轴问题(JVM.集合.并发),集合这块代码的整体设计也是融合很多编程思想,对于程序员来说具有很高的参考和借鉴 ...
- java并发:join源码分析
join join join是Thread方法,它的作用是A线程中子线程B在运行之后调用了B.join(),A线程会阻塞直至B线程执行结束 join源码(只有继承Thread类才能使用) 基于open ...
最新文章
- Windows和Linux下apache-artemis-2.10.0安装配置
- (转)Linux下的输入/输出重定向
- 进程管理工具(Supervisor)笔记
- (转)Spring Boot 2 (七):Spring Boot 如何解决项目启动时初始化资源
- 特斯拉在美国召回947辆电动汽车 因倒车影像显示延迟
- 多线程 CreateThread与_beginthreadex本质区别
- 分享一个TEXT文档加密/解密编辑器
- [ERP/鼎捷E10][生产制造]取代料/替代料
- mysql jemalloc_jemalloc优化MySQL、Nginx内存管理
- 矩阵代数(四)- 分块矩阵
- win10 net framework 3.5提示错误代码0x800f081f
- 基于STM32的0.96寸OLED显示屏显示数据
- iframe标签使用
- 011235813用java写出来_C 编程练习题大全(带答案)
- JScript 06 根据成绩平均分划分等级
- 顺序查找与二分查找时间复杂度的比较
- gif图片体积过大怎么办?手把手教你快速压缩gif动图
- 查看 Windows 10 快捷键 占用 VSCode 多行编辑 ctrl alt uparrow
- 使用OpenSSL实现CA证书的搭建过程
- 数学计算机软件课程,《数学软件》课程教学大纲.doc
热门文章
- php多进程共享数据库,PHP多进程环境下通过共享内存与信号量实现资源共享
- java 捕获数据包,缓存从pcap捕获的数据包
- mysql windows 管道连接,科技常识:Windows Server 2016 MySQL数据库安装配置详细安装教程...
- c++ 高通、低通、带通滤波器_射频/微波滤波器
- mysql索引能重复吗_mysql重复索引与冗余索引实例分析
- python 修改字符串中的某个单词_python Pandas替换字符串中的单词
- solidworks模板_SolidWorks文件属性分类和创建方法,图纸自动属性的基础
- Android跨平台编译 —— BOOST
- Hazelcast集群服务(2)
- 转:软件设计漫谈之三:30分钟掌握面向对象类的设计原则