JDK1.6以后包含了java.util.ServiceLoader类,但对于这个工具类的作用可能很多Java工程师未必了解。本文就希望通过一个如何用ServiceLoader实现模块加载机制的案例,来试图讲解ServiceLoader的应用。
ServiceLoader介绍
我们通常所指的服务就是指一些接口和类的集合,而一个服务提供者就是指一个服务的具体实现,Java平台一般都是通过特定扩展机制来加载服务实现的。ServiceLoader就是这样一种扩展机制的实现。
ServiceLoader的机制要求服务提供方的jar必须包含一个META-INF/services目录,目录里面需要存在以服务类型名称(包含包名和类名)命名的文本文件,比如
META-INF/services/com.example.Service1
文件内容要求很简单,每一行放一个这个接口的一个实现类的类型名称(包含包名和类名),比如:
com.example.Service1Impl1
com.example.Service1Impl2
准备好这些后,我们就可以调用ServiceLoader获取这些服务实例,代码如下:
ServiceLoader<Service1> services = ServiceLoader.get(Service1.class);
这时候ServiceLoader会帮我们去扫描classpath下所有的META-INF/services/目录,如果包含com.example.Service1文件,他初始化配置文件中的服务提供方实例,每一条记录都会对应一个服务实例。
好了,ServiceLoader的机制大概讲完了,简单吧。接下来我们就拿ServiceLoader来实现一个简单的模块加载系统。
模块系统定义
一个相对复杂的应用往往需要划分成多个模块来降低整体复杂度。每个模块各司其职,模块之间都是弱耦合关系。每个模块都有它的生命周期,一般都需要初始化,启动,关闭这几个步骤,所以我们可以定义一个模块的抽象接口,代码如下:
public interface TeslaModule {
/**
* 模块的启动级别,决定了模块启动关闭的顺序
*
* @return 模块的启动级别
*/
int getStartLevel();
/**
* 模块的初始化方法
*
* @param context
* @throws Exception
*/
void init(FrameworkContext context) throws Exception;
/**
* 模块的启动入口
*
* @param context
* @throws Exception
*/
void start(FrameworkContext context) throws Exception;
/**
* 模块的关闭入口
*
* @param context
* @throws Exception
*/
void stop(FrameworkContext context) throws Exception;
}
大家看到这个TeslaModule接口的命名就应该知道了,这个模块系统的实现已经应用到了tesla-framework 1.1.5中。
同时这里还会有一个int getStartLevel();方法,用于返回模块的启动级别。模块之间往往会有先后的依赖关系,startLevel就是用于指定模块的启动和关闭的依赖顺序。
模块的启动和关闭
模块配置文件
通过上面的ServiceLoader机制介绍我们知道,我们需要在每模块jar包中的META-INF/services目录下面包含一个com.mogujie.tesla.framework.TeslaModule的文件,内容需要包含特定的模块实现,如下:
com.mogujie.tesla.server.TeslaExposeModule
模块加载
这些准备好了,我们就可以ServiceLoader来加载模块实例,代码如下:
List<TeslaModule> allServices = new ArrayList<>();
for(TeslaModule module : ServiceLoader.load(TeslaModule.class)) {
allServices.add(module);
}
模块启动
获取模块实例后,我们才开始正式的启动模块,代码如下:
// 1
Collections.sort(allServices, new Comparator<TeslaModule>() {
@Override
public int compare(TeslaModule o1, TeslaModule o2) {
return o1.getStartLevel() - o2.getStartLevel();
}
});
// 2
for (TeslaModule module : allServices) {
module.init(frameworkContext);
}
this.logger.info("tesla framework inited!");
// 3
for (TeslaModule module : allServices) {
module.start(frameworkContext);
}
this.logger.info("tesla framework started!");
- 根据startLevel由小到大排序,startLevel值最小的最先启动
- 调用每个模块的init方法用于初始化模块
- 调用每个模块的start方法用于启动模块
模块关闭
在应用关闭的时候,我需要去主动关闭模块,因为有些模块内部需要在关闭时释放线程池等系统资源,代码如下:
// 1
Collections.sort(allServices, new Comparator<TeslaModule>() {
@Override
public int compare(TeslaModule o1, TeslaModule o2) {
return o2.getStartLevel() - o1.getStartLevel();
}
});
// 2
for (TeslaModule module : allServices) {
module.stop(toClose);
}
- 根据startLevel由大到小排序,startLevel值最大的最先关闭
- 调用每个模块的stop方法依次关闭模块
总结
我们通过一个简单模块系统的实现讲解了ServiceLoader的应用场景。当然ServiceLoader不只局限于模块的加载,因为ServiceLoader本身是一个简单可靠的工具,JDK中很多SPI(Service Provider Interface)的实现都是通过ServiceLoader方式加载的。所以说在Java中需要用到接口扩展的时候,第一选择就是ServiceLoader了,因为它是JDK自带的,不需要引入第三方的库。
相关推荐
标题中的“软件架构:SPI的Demo”提示我们讨论的主题是关于Software Programming Interface(SPI)的实践示例。SPI是一种在不同模块或组件之间定义服务发现和服务使用的机制,它允许第三方开发者扩展应用程序的功能...
Java软件插件开发模式是一种灵活的软件架构设计,它允许我们构建可扩展的应用程序,其中核心功能可以通过安装或卸载插件来增加或减少。在Java世界中,Maven是广泛应用的项目管理和集成工具,它提供了强大的依赖管理...
3. **插件架构**:很多插件框架如Eclipse、IDEA都基于SPI实现插件系统的动态加载。 四、示例 以下是一个简单的SPI使用示例: ```java public interface MyService { void execute(); } public class ...
2. **模块路径(Module Path)**:与类路径(ClassPath)类似,模块路径用于定位模块。Java 9及以后版本可以同时使用模块路径和类路径。 3. **模块化的好处**:模块化提升了代码的封装性,使得非公开API不易被误用...
- 列表服务(ListService)、内核引擎(KernelEngine)、游戏服务(GameService)、中心服务器(CenterServer)、登录服务器(LogonServer)、服务装载器(ServiceLoader)等。 - **编译顺序**: 1. **公共服务**...
openTCS 提供了多种方式来获取服务对象,例如使用 ServiceLoader、Inject 等。开发人员可以根据需要选择合适的方式来获取服务对象。 9. 运输订单的处理 openTCS 提供了丰富的运输订单处理机制,例如运输订单的创建...
总的来说,SPI是一种强大的工具,尤其适用于那些需要进行插件式开发或模块化架构的项目。通过定义清晰的契约接口,开发者可以构建可扩展且易于维护的系统。理解和熟练运用SPI机制,将有助于提升软件设计的灵活性和可...
6. **服务提供与发现**:Java的ServiceLoader机制允许模块动态地查找并加载服务实现,这是一种松耦合的模块间服务发现方式。 7. **模块化的好处**:提高代码的可重用性,降低耦合度,使得大型项目更容易管理和维护...
这个过程对于那些需要动态发现和使用其他组件的系统尤其有用,比如那些依赖于插件架构或模块化设计的应用。 该库的主要功能包括: 1. **服务注册**:允许将服务注册到指定的命名空间,例如JNDI目录或自定义注册表...
在Java开发中,插件机制是一种常见的设计模式,它允许应用程序动态加载和卸载功能模块,从而提高了软件的可扩展性和灵活性。`JARCONNECTION`可能是某种自定义的或特定场景下的插件加载机制。虽然没有具体描述,但从...
7. **服务提供者接口(SPI)**:Java的ServiceLoader类允许动态发现和加载实现特定接口的JAR包,这在插件架构中非常有用。 8. **META-INF目录**:JAR文件内部的META-INF目录可以包含MANIFEST.MF文件,其中记录了...
1. **插件化基础概念**:插件是一种可插入主程序的模块,它提供额外的功能或服务,允许软件根据需要添加新功能而无需修改核心代码。 2. **Java插件架构**:在Java中,插件架构通常基于类加载器(Class Loaders)和...
10. **插件架构**:某些Java框架,如OSGi,允许将应用程序分解为多个相互独立的JAR模块,这些模块可以按需加载和卸载,构建高度可扩展的插件式系统。 总结来说,JAR文件是Java生态系统中不可或缺的一部分,它们简化...
3. **服务加载器**:通过`java.util.ServiceLoader`,模组可以声明他们提供的服务,并被其他模组动态发现和使用,这是实现插件式架构的关键部分。 **模组开发基础** 1. **理解生命周期**:模组从加载、初始化、到...
在软件开发中,可插拔功能(或称为插件化设计)是一种强大的设计模式,它允许用户根据需求添加、移除或替换特定的功能模块。这种灵活性对于大型项目尤其重要,因为它使得开发者能够独立地更新和扩展系统的一部分,而...
在Android开发中,插件化技术是一种高级技巧,它允许应用程序在运行时动态加载和卸载其他APK文件,从而实现模块化的应用架构。这种技术能够极大地提高代码的复用性和灵活性,使得开发者可以更新单个功能模块而无需...
"联系人插件"通常指的是在软件或操作系统中用于管理和操作用户联系人信息的扩展功能。在Java开发环境中,创建这样的插件可以帮助开发者为应用程序添加更丰富的用户交互体验,特别是对于那些需要处理大量人脉信息的...
#### 一、Dubbo框架概览与SPI机制 **Dubbo**作为一款高性能、轻量级的微服务框架,在分布式系统领域有着广泛的应用。其核心特性之一是采用了微内核+插件体系的设计理念,使得整体架构既灵活又易于扩展。在这一设计...
Java的模块化设计使得构建插件系统变得可能,开发者可以使用`ServiceLoader`来动态加载和管理插件。 **8. 多线程** 考虑到性能和用户体验,文本编辑器可能会在后台线程中进行大文件加载或保存操作,以防止阻塞主线...
服务治理中间件Dubbo是阿里巴巴基于开源思想使用Java语言实现的高性能RPC框架,它支持服务的注册与发现、负载均衡、容错处理等服务治理功能。Dubbo采用微内核架构加插件体系的设计模式,通过SPI(Service Provider ...