详谈ServiceLoader实现原理
发布时间 - 2026-01-10 23:14:20 点击率:次在java中根据一个子类获取其父类或接口信息非常方便,但是根据一个接口获取该接口的所有实现类却没那么容易。

有一种比较笨的办法就是扫描classpath所有的class与jar包中的class,然后用ClassLoader加载进来,然后再判断是否是给定接口的子类。但是很显然,不会使用这种方法,代价太大。
java本身也提供了一种方式来获取一个接口的子类,那就是使用java.util.ServiceLoader#load(java.lang.Class<S>) 方法,但是直接使用该方法也是不能获取到给定接口所有的子类的。
需要接口的子类以配置的方式主动注册到一个接口上,才能使用ServiceLoader进行加载到子类,并且子类需要有一个无参构造方法,用于被ServiceLoader进行实例化
下面介绍使用ServiceLoader的步骤
1、 编写Service
package com.mogujie.uni.sl;
/**
* Created by laibao
*/
public interface Animal {
void eat();
}
2、编写实现类(注意:实现类不一定要与接口在同一个工程中,可以存在于其他的jar包中)
package com.mogujie.uni.sl;
/**
* Created by laibao
*/
public class Pig implements Animal {
@Override
public void eat() {
System.out.println("Pig eating...");
}
}
package com.mogujie.uni.sl;
/**
* Created by laibao
*/
public class Dog implements Animal {
@Override
public void eat() {
System.out.println("Dog eating...");
}
}
3、 在实现类所在的工程的classpath下面的建立META-INF/services目录,该目录是固定的,一定要按照规定的名称去创建,该目录用于配置接口与实现类的映射关系
然后根据接口全名 在该目录创建一个文件,例如上面例子中接口全名是com.mogujie.uni.sl.Animal,那么就需要在实现类的工程中建立META-INF/services/com.mogujie.uni.sl.Animal这样一个文件,然后在该文件中配置该接口的实现类,如果该接口有多个实现类,一行写一个(以换行符分割),例如:
com.mogujie.uni.sl.Pig com.mogujie.uni.sl.Dog
4、接下来就能使用ServiceLoader的方法获取com.mogujie.uni.sl.Animal接口的所有子类了。
测试类如下:
package com.mogujie.uni;
import com.mogujie.uni.sl.Animal;
import java.util.Iterator;
import java.util.ServiceLoader;
/**
* Created by laibao
*/
public class TestServiceLoader {
public static void main(String[] args) {
ServiceLoader<Animal> serviceLoader = ServiceLoader.load(Animal.class);
Iterator<Animal> animalIterator = serviceLoader.iterator();
while(animalIterator.hasNext()){
Animal animal = animalIterator.next();
animal.eat();
}
}
}
输出如下:
Pig eating... Dog eating...
ServiceLoader的原理其实很简单,就是根据给定的参数(接口)就能定位到该接口与实现类的映射配置文件的路径了,然后读取该配置文件,就能获取到该接口的子类
下面自己实现一个CustomServiceLoader与系统的ServiceLoader具有同样的功能
package com.mogujie.uni;
import org.apache.commons.io.IOUtils;
import java.net.URL;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
/**
* Created by laibao
*/
public class CustomServiceLoader {
public static final String MAPPING_CONFIG_PREFIX = "META-INF/services";
public static <S> List<S> loade(Class<S> service) throws Exception{
String mappingConfigFile = MAPPING_CONFIG_PREFIX + "/" + service.getName() ;
//由于一个接口的实现类可能存在多个jar包中的META-INF目录下,所以下面使用getResources返回一个URL数组
Enumeration<URL> configFileUrls = CustomServiceLoader.class.getClassLoader().getResources(mappingConfigFile);
if(configFileUrls == null){
return null ;
}
List<S> services = new LinkedList<S>();
while(configFileUrls.hasMoreElements()){
URL configFileUrl = configFileUrls.nextElement();
String configContent = IOUtils.toString(configFileUrl.openStream());
String[] serviceNames = configContent.split("\n");
for(String serviceName : serviceNames){
Class serviceClass = CustomServiceLoader.class.getClassLoader().loadClass(serviceName);
Object serviceInstance = serviceClass.newInstance();
services.add((S)serviceInstance);
}
}
return services ;
}
}
测试类如下:
package com.mogujie.uni;
import com.mogujie.uni.sl.Animal;
import java.util.List;
/**
* Created by laibao
*/
public class CustomServiceLoaderTest {
public static void main(String[] args) throws Exception {
List<Animal> animals = CustomServiceLoader.loade(Animal.class);
for (Animal animal : animals){
animal.eat();
}
}
}
输出:
Pig eating... Dog eating...
java系统定义的ServiceLoader与我们自定义的CustomServiceLoader的loade方法,它们的返回值类型是不一样的,ServiceLoader的loade方法返回的是ServiceLoader对象,ServiceLoader对象实现了Iterable接口,通过ServiceLoader的成员方法iterator();就能遍历所有的服务实例,而我们自定义的CustomServiceLoader的load方法返回的是一个List对象,直接将所有的服务实例封装在一个集合里面返回了。
系统的ServiceLoader通过返回一个Iterator对象能够做到对服务实例的懒加载 只有当调用iterator.next()方法时才会实例化下一个服务实例,只有需要使用的时候才进行实例化,具体实现读者可以去阅读源码进行研究,这也是其设计的亮点之一。
以上这篇详谈ServiceLoader实现原理就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
# java
# serviceloader
# 子类
# 就能
# 的是
# 多个
# 包中
# 给大家
# 加载
# 自定义
# 配置文件
# 到该
# 有一种
# 遍历
# 太大
# 希望能
# 这样一个
# 然后再
# 这篇
# 时才
# 于其
# 却没
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
HTML 中如何正确使用模板变量为元素的 name 属性赋值
如何制作一个表白网站视频,关于勇敢表白的小标题?
node.js报错:Cannot find module 'ejs'的解决办法
如何基于PHP生成高效IDC网络公司建站源码?
Python函数文档自动校验_规范解析【教程】
敲碗10年!Mac系列传将迎来「触控与联网」双革新
香港服务器网站生成指南:免费资源整合与高速稳定配置方案
北京专业网站制作设计师招聘,北京白云观官方网站?
打造顶配客厅影院,这份100寸电视推荐名单请查收
如何在万网开始建站?分步指南解析
bing浏览器学术搜索入口_bing学术文献检索地址
Laravel如何配置和使用队列处理异步任务_Laravel队列驱动与任务分发实例
儿童网站界面设计图片,中国少年儿童教育网站-怎么去注册?
Laravel事件监听器怎么写_Laravel Event和Listener使用教程
网站页面设计需要考虑到这些问题
如何在阿里云完成域名注册与建站?
Python面向对象测试方法_mock解析【教程】
Laravel如何实现模型的全局作用域?(Global Scope示例)
如何在阿里云香港服务器快速搭建网站?
Laravel怎么处理异常_Laravel自定义异常处理与错误页面教程
Laravel如何实现多对多模型关联?(Eloquent教程)
Laravel如何实现数据库事务?(DB Facade示例)
香港服务器网站搭建教程-电商部署、配置优化与安全稳定指南
javascript和jQuery中的AJAX技术详解【包含AJAX各种跨域技术】
Laravel如何使用软删除(Soft Deletes)功能_Eloquent软删除与数据恢复方法
Android Socket接口实现即时通讯实例代码
图片制作网站免费软件,有没有免费的网站或软件可以将图片批量转为A4大小的pdf?
详解Android图表 MPAndroidChart折线图
如何用AWS免费套餐快速搭建高效网站?
Windows10电脑怎么查看硬盘通电时间_Win10使用工具检测磁盘健康
LinuxShell函数封装方法_脚本复用设计思路【教程】
HTML5建模怎么导出为FBX格式_FBX格式兼容性及导出步骤【指南】
javascript中闭包概念与用法深入理解
百度浏览器如何管理插件 百度浏览器插件管理方法
惠州网站建设制作推广,惠州市华视达文化传媒有限公司怎么样?
Laravel请求验证怎么写_Laravel Validator自定义表单验证规则教程
Laravel distinct去重查询_Laravel Eloquent去重方法
详解免费开源的DotNet二维码操作组件ThoughtWorks.QRCode(.NET组件介绍之四)
LinuxCD持续部署教程_自动发布与回滚机制
iOS中将个别页面强制横屏其他页面竖屏
Laravel项目结构怎么组织_大型Laravel应用的最佳目录结构实践
今日头条微视频如何找选题 今日头条微视频找选题技巧【指南】
品牌网站制作公司有哪些,买正品品牌一般去哪个网站买?
Android自定义listview布局实现上拉加载下拉刷新功能
Laravel怎么导出Excel文件_Laravel Excel插件使用教程
制作网站软件推荐手机版,如何制作属于自己的手机网站app应用?
Laravel如何处理和验证JSON类型的数据库字段
大同网页,大同瑞慈医院官网?
网站制作软件有哪些,制图软件有哪些?
香港代理服务器配置指南:高匿IP选择、跨境加速与SEO优化技巧

