`

通过ServiceLoader实现链式处理

 
阅读更多

ServiceLoader与ClassLoader是Java中2个即相互区别又相互联系的加载器.JVM利用ClassLoader将类载入内存,这是一个类声明周期的第一步(一个java类的完整的生命周期会经历加载、连接、初始化、使用、和卸载五个阶段,当然也有在加载或者连接之后没有被初始化就直接被使用的情况)。详情请参阅:详解Java类的生命周期

ServiceLoader又是什么呢?ServiceLoader:一个简单的服务提供者加载设施。服务是一个熟知的接口和类(通常为抽象类)集合。服务提供者是服务的特定实现。提供者中的类通常实现接口,并子类化在服务本身中定义的子类。服务提供者可以以扩展的形式安装在 Java 平台的实现中,也就是将 jar 文件放入任意常用的扩展目录中。也可通过将提供者加入应用程序类路径,或者通过其他某些特定于平台的方式使其可用。……唯一强制要求的是,提供者类必须具有不带参数的构造方法,以便它们可以在加载中被实例化。

通过在资源目录META-INF/services中放置提供者配置文件来标识服务提供者。文件名称是服务类型的完全限定二进制名称。该文件包含一个具体提供者类的完全限定二进制名称列表,每行一个。忽略各名称周围的空格、制表符和空行。注释字符为'#'('\u0023',NUMBER SIGN);忽略每行第一个注释字符后面的所有字符。文件必须使用 UTF-8 编码。

以延迟方式查找和实例化提供者,也就是说根据需要进行。服务加载器维护到目前为止已经加载的提供者缓存。每次调用iterator方法返回一个迭代器,它首先按照实例化顺序生成缓存的所有元素,然后以延迟方式查找和实例化所有剩余的提供者,依次将每个提供者添加到缓存。可以通过reload方法清除缓存。

……

以上来源于Java API里的说明,也许说的很专业,让我们有点晕头转向,我们可以简单的认为:ServiceLoader也像ClassLoader一样,能装载类文件,但是使用时有区别,具体区别如下:(1)ServiceLoader装载的是一系列有某种共同特征的实现类,而ClassLoader是个万能加载器;(2)ServiceLoader装载时需要特殊的配置,使用时也与ClassLoader有所区别;(3)ServiceLoader还实现了Iterator接口。[如有错误或不到的地方敬请指出,互相学习:)]

下面是关于ServiceLoader的简单的例子,仅供参考

(1)基础服务:IService

?
1
2
3
4
5
package com.service;
public interface IService {
String sayHello();
String getScheme();
}


(2)具体服务实现1:HDFSService

?
1
2
3
4
5
6
7
8
9
10
11
12
package com.impl;
import com.service.IService;
public class HDFSService implements IService {
@Override
public String sayHello() {
return "Hello HDFSService";
}
@Override
public String getScheme() {
return "hdfs";
}
}


(3)具体服务实现2:LocalService

?
1
2
3
4
5
6
7
8
9
10
11
12
package com.impl;
import com.service.IService;
public class LocalService implements IService {
@Override
public String sayHello() {
return "Hello LocalService";
}
@Override
public String getScheme() {
return "local";
}
}
(4)配置:META-INF/services/com.service.IService


?
1
2
com.impl.HDFSService
com.impl.LocalService
(5)测试类
?
1
2
3
4
5
6
7
8
9
10
11
package com.test;
import java.util.ServiceLoader;
import com.service.IService;
public class Test {
public static void main(String[] args) {
ServiceLoader<IService> serviceLoader = ServiceLoader.load(IService.class);
for (IService service : serviceLoader) {
System.out.println(service.getScheme()+"="+service.sayHello());
}
}
}


结果:

hdfs=Hello HDFSService
local=Hello LocalService

可以看到ServiceLoader可以根据IService把定义的两个实现类找出来,返回一个ServiceLoader的实现,而ServiceLoader实现了Iterable接口,所以可以通过ServiceLoader来遍历所有在配置文件中定义的类的实例。


ServiceLoader的应用

(1)Hadoop FileSystem

Hadoop FileSystem就是通过这个机制来根据不同文件的scheme来返回不同的FileSystem。

?
1
2
3
4
5
6
7
8
9
10
11
private static void loadFileSystems() {
synchronized (FileSystem.class) {
if (!FILE_SYSTEMS_LOADED) {
ServiceLoader<FileSystem> serviceLoader = ServiceLoader.load(FileSystem.class);
for (FileSystem fs : serviceLoader) {
SERVICE_FILE_SYSTEMS.put(fs.getScheme(), fs.getClass());
}
FILE_SYSTEMS_LOADED = true;
}
}
}
对应的配置文件:

?
1
2
3
4
5
6
7
org.apache.hadoop.fs.LocalFileSystem
org.apache.hadoop.fs.viewfs.ViewFileSystem
org.apache.hadoop.fs.s3.S3FileSystem
org.apache.hadoop.fs.s3native.NativeS3FileSystem
org.apache.hadoop.fs.kfs.KosmosFileSystem
org.apache.hadoop.fs.ftp.FTPFileSystem
org.apache.hadoop.fs.HarFileSystem
通过之前的测试类输出对应的scheme和class如下:
file=class org.apache.hadoop.fs.LocalFileSystem
viewfs=class org.apache.hadoop.fs.viewfs.ViewFileSystem
s3=class org.apache.hadoop.fs.s3.S3FileSystem
s3n=class org.apache.hadoop.fs.s3native.NativeS3FileSystem
kfs=class org.apache.hadoop.fs.kfs.KosmosFileSystem
ftp=class org.apache.hadoop.fs.ftp.FTPFileSystem
har=class org.apache.hadoop.fs.HarFileSystem
hdfs=class org.apache.hadoop.hdfs.DistributedFileSystem
hftp=class org.apache.hadoop.hdfs.HftpFileSystem
hsftp=class org.apache.hadoop.hdfs.HsftpFileSystem
webhdfs=class org.apache.hadoop.hdfs.web.WebHdfsFileSystem

可以看到FileSystem会把所有的FileSystem的实现都以scheme和class来cache,之后就从这个cache中取相应的值。因此,以后可以通过ServiceLoader来实现一些类似的功能,而不用依赖像Spring这样的第三方框架。

(2)责任链模式

责任链模式的定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

责任连模式可以使用ServiceLoader实现具体服务对象的迭代加载并处理,为了确保此模式的灵活性,建议判断逻辑通过配置文件或数据库的方式,具体实现方式见参考链接(2)消灭成堆的……


参考资料:

(1)java.util.ServiceLoader使用

(2)消灭成堆的分支语句之类责任链模式

(3)转一篇很不错的介绍NetBeans的文章

附:测试用例文件的文件结构

转载自:通过ServiceLoader实现链式处理

分享到:
评论

相关推荐

    利用WinDriver实现链式DMA

    由于提供的【部分内容】并不包含有关“链式DMA”和“WinDriver”的明确技术描述,将不会在...通过WinDriver,可以大大简化链式DMA的实现过程,但仍然需要仔细规划和严谨的设计来确保数据传输的正确性和系统的稳定性。

    swift-YQChainTaskSwift链式调用50行不到代码实现的链式任务调用

    总之,`YQChainTask`是一个轻量级的Swift库,它通过简单的50行代码实现了链式任务调用,使得任务执行过程更加整洁和易于理解。这种设计思路可以启发我们如何在自己的项目中运用链式调用来提升代码的可读性和维护性。

    用链式栈实现简易计算器

    通过对链式栈的理解和运用,不仅可以实现基本的加减乘除,还能处理更复杂的数学运算,提供便捷的用户交互体验。通过阅读源代码和配套的PPT,我们可以深入学习链式栈的实现细节,以及如何在实际编程中运用数据结构...

    C语言实现链式栈的模板

    为了在C语言中实现链式栈的模板,我们通常会使用以下策略: 1. **typedef**:通过`typedef`关键字,我们可以为不同的数据类型定义新的类型别名,这样就可以创建一个通用的链式栈结构体,其中的元素类型被这个别名...

    顺序存储和链式存储的泛型队列_C语言项目

    总之,本项目通过C语言实现了顺序存储和链式存储的泛型队列,提供了一套完整的队列操作接口,适用于多种场景。理解和掌握这些知识点对于提升C语言编程能力和数据结构应用能力非常有帮助。通过阅读和分析项目中的代码...

    链式队列的实现

    该文件实现链式队列功能,包含队列的创建queucreat、入队add与出队output,并通过打印显示函数的执行效果。

    C语言实现链式队列

    链式队列

    链式队列的表示和实现源码

    链式队列在处理需要按照FIFO顺序执行任务的问题上非常有用,如操作系统调度、打印机任务管理等。此外,链式队列相比数组实现的队列具有更大的灵活性,因为它的大小不受固定数组大小的限制,可以动态地增加或减少。

    数据结构线性表的顺序和链式实现

    在实际编程中,线性表的两种常见实现方式是顺序表和链式表,这两种实现各有优缺点。 顺序表是一种静态的数据结构,它的所有元素在内存中是连续存放的,就像数组一样。这种实现方式的优点是访问速度快,因为元素的...

    线性表的链式表示和实现

    通过实验,掌握了单链表的基本操作在链式存储结构上的实现,了解了线性表的逻辑结构特征,并掌握了链式表示的编程方法。 知识点: 1. 线性表的链式表示。 2. 单链表的基本操作。 3. 线性表的逻辑结构特征。 4. ...

    链式线性表实现

    在C++中,链式线性表通常通过结构体或类来实现。这里我们将深入探讨如何用C++实现链表。 1. **定义链表节点结构**: 首先,我们需要定义一个结构体或类来表示链表的节点。节点通常包含两部分:数据成员(用于存储...

    C++使用模板类实现链式栈

    这些操作都是通过链式栈的实现类LinkedStack来实现的。 1. 入栈操作:入栈操作是将一个新的数据元素插入到链式栈的栈顶。这个操作可以通过LinkedStack类的Push函数来实现。 2. 出栈操作:出栈操作是删除链式栈的...

    线性表的顺序和链式实现方式

    线性表是由n(n&gt;=0)个相同类型元素构成的有限序列,可以按照两种主要的方式进行实现:顺序结构和链式结构。本篇将详细讨论这两种实现方式,并结合C语言进行函数封装的实践。 1. **顺序结构**: 顺序结构的线性表...

    队列的链式实现(java)

    本篇将详细探讨如何使用Java语言实现链式队列,这是一种非数组形式的队列实现,通过节点连接来存储数据。 链式队列的实现通常涉及两个主要部分:队头(front)和队尾(rear)。队头指向队列的第一个元素,而队尾...

    可实现链式栈

    链式栈,本程序系数据结构课程课本实例,亲测可用

    c语言 链式队列的实现

    在C语言中,链式队列的实现通常涉及结构体的定义、节点的创建与销毁、以及一系列针对队列操作的函数。下面我们将深入探讨链式队列的实现细节。 首先,我们需要定义一个结构体来表示队列中的节点。每个节点包含两个...

    串的链式存储

    在计算机科学中,字符串,或称串,是...理解和掌握链式串的结构及操作算法对于编程实践至关重要,特别是在处理大量文本数据和实现动态字符串功能时。在实际应用中,应根据具体需求和性能权衡来选择合适的串存储方式。

    二分查找链式实现

    在链式结构中实现二分查找,与在数组中实现有所不同,因为链表不支持随机访问,但依然可以通过适当的设计实现高效查找。 链式结构通常指的是链表,包括单链表、双向链表等。在链表中,每个节点包含数据以及指向下一...

    线性表的链式实现

    链式线性表与顺序存储相比,其优点在于它不需要预先分配连续的内存空间,而是通过节点间的指针链接来构造数据结构。这种实现方式使得插入和删除操作更为灵活,因为它们只需要改变相邻节点的指针即可,而不需要移动...

    用c语言实现的链式栈

    链式栈是一种基于链表数据结构实现的栈,与数组实现的顺序栈...通过这样的实现,我们可以方便地进行栈的动态扩展和收缩,适合处理数据量不确定或变化的情况。在实际编程中,链式栈常用于表达式求值、递归调用等场景。

Global site tag (gtag.js) - Google Analytics