服务加载器
利用 ServiceLoader
可以实现接口与实现的分离, 并且可以让一个 接口有多种实现以供不同情况下的选择, 也可以用于 API 模块 和 实现 模块的分离
使用:
创建公共接口:
- 新建一个项目, 构建系统为 maven
- 删除新建项目的 src 目录
- 新建一个子模块名字叫 api
- 在新建模块下新建一个包名字叫: serviceLoader
- 在新建的包下创建一个接口
Cipher
, 这是加密和解密的接口 - 定义加密解密方法等
Cipher
接口:
package serviceLoader
public interface Cipher {
byte[] encrypt(byte[] source, byte[] key);
byte[] decrypt(byte[] source, byte[] key);
int strength();
}
创建实现类:
- 新建一个子模块名字叫 core
- 在 core 模块中依赖 api 模块 (scope 为 provide)
- 新建一个包名字叫: serviceloader.impl
- 创建实现类继承
Cipher
接口
package serviceLoader.impl
import serviceLoader.Cipher;
public class CaesarCipher implements Cipher {
// 实现加密方法
public byte[] encrypt(byte[] source, byte[] key) {
byte[] result = new byte[source.length];
for (int i = 0; i < source.length; i++) {
result[i] = (byte)(source[i] + key[0]);
}
return result;
}
// 实现解密方法
public byte[] decrypt(byte[] source, byte[] key) {
return encrypt(source, new byte[] { (byte)-key[0] });
}
public int strength() {
return 1;
}
}
配置:
在实现类的模块中创建 resources/META-INF/services 目录
在创建的目录下创建文件 serviceLoader.Cipher
文件内容如下
serviceLoader.impl.CaesarCipher
文件名的格式是: 接口的全限定名(包名.类名), 内容的格式是每个实现类的全限定名
如果有多个实现类, 只需要一行写一个实现类的全限定名即可
加载实现类:
- 在 api 模块中新建包 serviceLoader.impl
- 在新建的包中新建类
CipherImpl
package serviceLoader.impl;
import serviceLoader.Cipher;
import java.util.ServiceLoader;
public class CipherImpl {
public static ServiceLoader<Cipher> cipherLoader = ServiceLoader.load(Cipher.class);
public Cipher getCipher(int minStrength) {
for (Cipher cipher : cipherLoader) {
if (cipher.strength() >= minStrength) {
return cipher;
}
}
throw new RuntimeException("not found any impls");
}
}
- 新建一个子模块 main (名字随意)
- 依赖模块 api 和 core (scope 为 runtime)
- 创建一个类 Main.class
- 在 Main 的 main 方法中测试
import serviceLoader.impl.CipherImpl;
public class Main {
public static void main(String[] args) {
CipherImpl cipher = new CipherImpl();
System.out.println(cipher.getCipher(1));
}
}
运行后打印出东西说明成功
详解:
-
配置文件加载:
ServiceLoader
在初始化时会根据指定的服务提供者接口类型查找对应的配置文件。它通过ClassLoader
的资源加载机制来加载位于META-INF/services/
目录下的配置文件。配置文件的名称是服务提供者接口的全限定名。 -
实例化服务提供者:一旦找到了配置文件,
ServiceLoader
会读取文件中的每一行,每行都包含一个服务提供者实现类的全限定名。ServiceLoader
使用反射机制实例化这些服务提供者类,并返回对应的实例。 -
延迟加载:
ServiceLoader
使用了延迟加载的机制,即在初始化时并不会立即实例化所有的服务提供者类。而是在需要获取服务实例时才会进行实例化。这样可以提高性能,避免不必要的实例化操作。 -
缓存机制:
ServiceLoader
在第一次加载服务提供者时会将其缓存起来,下次再次需要获取实例时可以直接使用缓存中的数据,避免重复加载和实例化。 -
遍历服务提供者:
ServiceLoader
提供了一个迭代器,用于遍历所有已加载的服务提供者实例。通过迭代器,可以依次获取每个服务提供者的实例,从而使用其提供的功能。 -
动态更新:
ServiceLoader
支持动态更新服务提供者。当配置文件或服务提供者模块发生变化时,可以通过重新加载来获取最新的服务提供者实例。通过调用reload()
方法可以触发重新加载操作。
java.util.ServiceLoader
- static <S> ServiceLoader<S> load(Class<S> service)
创建一个服务加载器来加载实现给定服务接口的类
- Iterator<S> iterator()
生成一个懒加载方式加载服务类的迭代器. 随着迭代器推进才会加载类
- Stream<ServiceLoader.Provider<S>> stream()
返回一个流, 也是懒加载
- Optional<S> findFirst()
查找第一个可用的服务提供类 (如果有)
java.util.ServiceLoader.Provider
- Class<? extends S> type()
获得这个提供者的类型
- S get()
或者这个提供者的实例
示例代码:
以下是本篇博客所使用的代码, 使用的工具为 IntelliJ IDEA
https://bloghexofluid.oss-accelerate.aliyuncs.com/halo/example.zip