热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

详谈ServiceLoader实现原理

下面小编就为大家带来一篇详谈ServiceLoader实现原理。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

在java中根据一个子类获取其父类或接口信息非常方便,但是根据一个接口获取该接口的所有实现类却没那么容易。

有一种比较笨的办法就是扫描classpath所有的class与jar包中的class,然后用ClassLoader加载进来,然后再判断是否是给定接口的子类。但是很显然,不会使用这种方法,代价太大。

java本身也提供了一种方式来获取一个接口的子类,那就是使用java.util.ServiceLoader#load(java.lang.Class) 方法,但是直接使用该方法也是不能获取到给定接口所有的子类的。

需要接口的子类以配置的方式主动注册到一个接口上,才能使用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 serviceLoader = ServiceLoader.load(Animal.class);
    Iterator 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  List loade(Class service) throws Exception{
    String mappingCOnfigFile= MAPPING_CONFIG_PREFIX + "/" + service.getName() ;
    //由于一个接口的实现类可能存在多个jar包中的META-INF目录下,所以下面使用getResources返回一个URL数组
    Enumeration cOnfigFileUrls= CustomServiceLoader.class.getClassLoader().getResources(mappingConfigFile);
    if(cOnfigFileUrls== null){
      return null ;
    }
    List services = new LinkedList();
    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 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实现原理就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。


推荐阅读
  • HBase客户端Table类中getRpcTimeout方法的应用与编程实例解析 ... [详细]
  • 在Hive中合理配置Map和Reduce任务的数量对于优化不同场景下的性能至关重要。本文探讨了如何控制Hive任务中的Map数量,分析了当输入数据超过128MB时是否会自动拆分,以及Map数量是否越多越好的问题。通过实际案例和实验数据,本文提供了具体的配置建议,帮助用户在不同场景下实现最佳性能。 ... [详细]
  • 本文深入探讨了IO复用技术的原理与实现,重点分析了其在解决C10K问题中的关键作用。IO复用技术允许单个进程同时管理多个IO对象,如文件、套接字和管道等,通过系统调用如`select`、`poll`和`epoll`,高效地处理大量并发连接。文章详细介绍了这些技术的工作机制,并结合实际案例,展示了它们在高并发场景下的应用效果。 ... [详细]
  • 如何正确配置与使用日志组件:Log4j、SLF4J及Logback的连接与整合方法
    在当前的软件开发实践中,无论是开源项目还是日常工作中,日志框架都是不可或缺的工具之一。本文详细探讨了如何正确配置与使用Log4j、SLF4J及Logback这三个流行的日志组件,并深入解析了它们之间的连接与整合方法,旨在帮助开发者高效地管理和优化日志记录流程。 ... [详细]
  • 在使用sbt构建项目时,遇到了“对象apache不是org软件包的成员”的错误。本文详细分析了该问题的原因,并提供了有效的解决方案,包括检查依赖配置、清理缓存和更新sbt插件等步骤,帮助开发者快速解决问题。 ... [详细]
  • 深入解析Wget CVE-2016-4971漏洞的利用方法与安全防范措施
    ### 摘要Wget 是一个广泛使用的命令行工具,用于从 Web 服务器下载文件。CVE-2016-4971 漏洞涉及 Wget 在处理特定 HTTP 响应头时的缺陷,可能导致远程代码执行。本文详细分析了该漏洞的成因、利用方法以及相应的安全防范措施,包括更新 Wget 版本、配置防火墙规则和使用安全的 HTTP 头。通过这些措施,可以有效防止潜在的安全威胁。 ... [详细]
  • ActiveMQ是由Apache开发的一款广受欢迎且功能强大的开源消息中间件。作为完全符合JMS 1.1和J2EE 1.4规范的JMS Provider实现,尽管JMS规范已问世多年,但ActiveMQ依然保持了其在消息队列领域的领先地位。本文将带你初步了解ActiveMQ的核心概念及其应用场景,帮助你快速入门这一重要的消息传递技术。 ... [详细]
  • 深入解析 javax.faces.view.ViewDeclarationLanguageWrapper.getWrapped() 方法及其应用实例 ... [详细]
  • NoSQL数据库,即非关系型数据库,有时也被称作Not Only SQL,是一种区别于传统关系型数据库的管理系统。这类数据库设计用于处理大规模、高并发的数据存储与查询需求,特别适用于需要快速读写大量非结构化或半结构化数据的应用场景。NoSQL数据库通过牺牲部分一致性来换取更高的可扩展性和性能,支持分布式部署,能够有效应对互联网时代的海量数据挑战。 ... [详细]
  • 构建顶级PHP博客系统:实践与洞见
    构建顶级PHP博客系统不仅需要扎实的技术基础,还需深入理解实际应用需求。本文以Zend Studio为开发环境,MySQL作为数据存储,Apache服务器为运行平台,结合jQuery脚本语言,详细阐述了从环境搭建到功能实现的全过程,分享了开发PHP博客管理系统的宝贵经验和实用技巧。 ... [详细]
  • Linux学习精华:程序管理、终端种类与命令帮助获取方法综述 ... [详细]
  • 随着越来越多的应用程序采用JSON格式作为响应数据,基于Spring Framework构建的服务端应用也广泛采用了这一实践。本文将详细介绍如何在Spring 4.x版本的MVC框架中配置和实现HTTP请求返回JSON数据流,涵盖相关配置、依赖管理和代码示例,帮助开发者高效地实现这一功能。 ... [详细]
  • 如何在Linux系统中实施网络流量监测与分析
    本文详细介绍了在Linux系统中实施网络流量监测与分析的方法。通过使用专业的工具和技术,读者可以有效地监控和分析网络流量,从而提高系统的安全性和性能。希望本文能为读者提供有价值的参考和实践指导。 ... [详细]
  • Android平台生活辅助应用的设计与开发实现
    随着移动互联网技术的迅猛发展,Android操作系统已成为移动设备中的主流平台。本文探讨了基于Android平台的生活辅助应用设计与开发,旨在通过创新的功能和用户友好的界面,提升用户的日常生活质量。研究不仅涵盖了应用的核心功能实现,还深入分析了用户体验优化的方法,为同类应用的开发提供了有价值的参考。 ... [详细]
  • 在 Tomcat 上部署 Jenkins 实现持续集成环境搭建
    本文介绍了如何在Tomcat服务器上部署Jenkins以构建持续集成环境。首先,需下载并解压Tomcat压缩包,例如 `apache-tomcat-8.5.37.tar.gz`。接着,为Tomcat创建一个管理用户,并对相关文件目录进行权限配置,确保Jenkins能够顺利运行于Tomcat之上。此外,还详细阐述了环境配置、服务启动及基本的故障排查技巧,为用户提供了一套完整的部署指南。 ... [详细]
author-avatar
ckx1989
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有