1. ServiceLoader类介绍
在Java 6中,引入了一种服务提供者接口(Service Provider Interface, SPI)机制,其核心类为java.util.ServiceLoader。ServiceLoader类的作用是从classpath下的META-INF/services目录下读取配置文件,根据配置文件指定的实现类创建对象,并返回相应的服务接口实现类。
2. 使用ServiceLoader类
2.1 在未使用模块化的Java应用中使用ServiceLoader类
在未使用模块化的Java应用中使用ServiceLoader类非常简单,只需要在classpath下的META-INF/services目录下创建一个文件,文件名为服务接口的全限定类名,文件内容为实现类的全限定类名,即可使用ServiceLoader类动态加载并创建服务接口的实现类。
// 服务接口
public interface MessageService {
void send(String message);
}
// 服务接口实现类1
public class EmailMessageServiceImpl implements MessageService {
@Override
public void send(String message) {
System.out.println("通过邮件发送消息:" + message);
}
}
// 服务接口实现类2
public class SmsMessageServiceImpl implements MessageService {
@Override
public void send(String message) {
System.out.println("通过短信发送消息:" + message);
}
}
// 配置文件
// 在classpath下的META-INF/services目录下创建文件:com.example.MessageService
// 文件内容为实现类的全限定类名,如下所示:
com.example.EmailMessageServiceImpl
com.example.SmsMessageServiceImpl
// 加载服务接口的实现类
ServiceLoader<MessageService> messageServices = ServiceLoader.load(MessageService.class);
for (MessageService messageService : messageServices) {
messageService.send("hello world");
}
// 执行结果为:
// 通过邮件发送消息:hello world
// 通过短信发送消息:hello world
2.2 在模块中使用ServiceLoader类
在Java 9中,引入了模块化机制,模块化使得Java应用更具可维护性、可重用性、可伸缩性等方面的优势。在模块化的Java应用中,可以在module-info.java文件中使用requires和provides关键字来声明服务接口和服务实现类。
2.2.1 声明服务接口
在module-info.java文件中使用requires关键字声明服务接口模块,如下所示:
module com.example.message {
exports com.example.message;
requires java.base;
}
上面的代码声明了com.example.message模块,该模块导出com.example.message包,同时依赖于java.base模块。
2.2.2 声明服务接口实现类
在module-info.java文件中使用provides和with关键字声明服务接口实现类,如下所示:
module com.example.sms {
requires com.example.message;
provides com.example.message.MessageService
with com.example.sms.SmsMessageServiceImpl;
}
module com.example.email {
requires com.example.message;
provides com.example.message.MessageService
with com.example.email.EmailMessageServiceImpl;
}
上面的代码声明了com.example.sms和com.example.email两个模块,这两个模块都依赖于com.example.message模块,同时使用provides和with关键字声明了提供服务的服务接口实现类。
2.2.3 使用ServiceLoader类获取服务接口实现类
在模块化的Java应用中,可以使用ServiceLoader类来获取服务接口的实现类,如下所示:
ServiceLoader<MessageService> messageServices = ServiceLoader.load(ModuleLayer.boot(), MessageService.class);
for (MessageService messageService : messageServices) {
messageService.send("hello world");
}
上面的代码使用ServiceLoader.load()方法获取服务接口的实现类,使用ModuleLayer.boot()方法获取引导层模块,使得ServiceLoader类可以加载来自引导层和应用层的服务接口实现类。
3. 小结
ServiceLoader类是Java中的一种全新的机制,它可以更方便、更灵活地实现模块之间的解耦和扩展。在未使用模块化的Java应用中,ServiceLoader类的使用非常简单,只需要在classpath下的META-INF/services目录下创建一个文件即可。在模块化的Java应用中,使用ServiceLoader类获取服务接口实现类需要在module-info.java文件中使用provides和with关键字声明服务接口实现类。