根据单例设计模式使用服务加载器创建可扩展应用程序
Creating Extensible Applications using service-loaders according to a singleton design pattern
我正在 IntelliJ IDEA
项目上工作,我想在我的 java 应用程序中添加对可扩展应用程序的支持。
方法是,创建一个jar文件,在这个jar文件中应该有一个META-INF/services目录,在这个目录里面,我需要添加一个与完整名称相同的文件-提供服务的接口的限定名称,在该文件中它应该具有该接口实现的完全限定名称。
这是我项目中的加解密服务。
这两个是我提供服务的程序中的文件
这是声明服务方法的接口。
package serviceLoader;
public interface Cipher {
byte[] encrypt(byte[] source, byte[] key);
byte[] decrypt(byte[] source, byte[] key);
int strength();
}
这是这些服务的实施 class。
package serviceLoader.impl;
import serviceLoader.Cipher;
public class CaesarCipher implements Cipher {
public byte[] encrypt(byte[] source, byte[] key) {
var 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;
}
}
我想知道如何根据单例设计模式创建此程序,我是否需要任何额外的 java 文件来实现单例设计模式?
您需要一个 class 来定位、加载和实例化服务提供者(Cipher 的特定实现)并捕获它们。
有一些实现单例的方法。最常见的方法之一是基于保持构造函数私有并导出 public 静态成员以提供对唯一实例的访问。这个 public 静态成员可以是静态字段或静态方法工厂(我们的示例代码中的 getInstance()
):
import serviceLoader.Cipher;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;
public final class CipherManager {
private final static ArrayList<Cipher> ciphers = new ArrayList<>();
private final static CipherManager INSTANCE = new CipherManager();
private CipherManager() {
load();
}
private static void load() {
ServiceLoader<Cipher> cipherLoader = ServiceLoader.load(Cipher.class);
Iterator<Cipher> ciphersIterator = cipherLoader.iterator();
List<Cipher> cipherProviders = new ArrayList<>();
ciphersIterator.forEachRemaining(cipherProviders::add);
updateCiphers(cipherProviders);
}
static synchronized void updateCiphers(List<Cipher> cipherProviders) {
ciphers.clear();
ciphers.addAll(cipherProviders);
}
public static CipherManager getInstance() {
return INSTANCE;
}
public void reload() {
load();
}
public Cipher getCipher() {
if (!ciphers.isEmpty()) {
return ciphers.get(0);
}
return null;
}
}
getCipher()
方法应该根据需要实现。也许您想通过 class 名称或您服务的任何唯一 属性(例如提供者 ID、名称等)获得特权。考虑到这些,我更喜欢使用 HashMap 来捕获服务提供者(而不是 ArrayList)。
private final static HashMap<String, Cipher> ciphers = new HashMap<>();
实现单例的另一种方法是声明单元素枚举:
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.ServiceLoader;
public enum CipherManager {
INSTANCE;
private final static HashMap<String, Cipher> ciphers = new HashMap<>();
static {
load();
}
public static CipherManager getInstance() {
return INSTANCE;
}
public void reload() {
load();
}
private static void load() {
ServiceLoader<Cipher> cipherLoader = ServiceLoader.load(Cipher.class);
Iterator<Cipher> ciphersIterator = cipherLoader.iterator();
HashMap<String, Cipher> cipherProviders = new HashMap<>();
ciphersIterator.forEachRemaining(cipher -> cipherProviders.put(cipher.getClass().getName(), cipher));
updateCiphers(cipherProviders);
}
static synchronized void updateCiphers(Map<String, Cipher> cipherProviders) {
ciphers.clear();
ciphers.putAll(cipherProviders);
}
public Cipher getCipher(String className) {
return ciphers.get(className);
}
}
在这种方法中,添加了 getInstance()
方法来表示 CipherManager 遵循单例模式。因此,getInstance()
方法可以从代码中删除,我们可以直接使用 INSTANCE
字段。
我正在 IntelliJ IDEA
项目上工作,我想在我的 java 应用程序中添加对可扩展应用程序的支持。
方法是,创建一个jar文件,在这个jar文件中应该有一个META-INF/services目录,在这个目录里面,我需要添加一个与完整名称相同的文件-提供服务的接口的限定名称,在该文件中它应该具有该接口实现的完全限定名称。
这是我项目中的加解密服务。
这两个是我提供服务的程序中的文件
这是声明服务方法的接口。
package serviceLoader;
public interface Cipher {
byte[] encrypt(byte[] source, byte[] key);
byte[] decrypt(byte[] source, byte[] key);
int strength();
}
这是这些服务的实施 class。
package serviceLoader.impl;
import serviceLoader.Cipher;
public class CaesarCipher implements Cipher {
public byte[] encrypt(byte[] source, byte[] key) {
var 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;
}
}
我想知道如何根据单例设计模式创建此程序,我是否需要任何额外的 java 文件来实现单例设计模式?
您需要一个 class 来定位、加载和实例化服务提供者(Cipher 的特定实现)并捕获它们。
有一些实现单例的方法。最常见的方法之一是基于保持构造函数私有并导出 public 静态成员以提供对唯一实例的访问。这个 public 静态成员可以是静态字段或静态方法工厂(我们的示例代码中的 getInstance()
):
import serviceLoader.Cipher;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;
public final class CipherManager {
private final static ArrayList<Cipher> ciphers = new ArrayList<>();
private final static CipherManager INSTANCE = new CipherManager();
private CipherManager() {
load();
}
private static void load() {
ServiceLoader<Cipher> cipherLoader = ServiceLoader.load(Cipher.class);
Iterator<Cipher> ciphersIterator = cipherLoader.iterator();
List<Cipher> cipherProviders = new ArrayList<>();
ciphersIterator.forEachRemaining(cipherProviders::add);
updateCiphers(cipherProviders);
}
static synchronized void updateCiphers(List<Cipher> cipherProviders) {
ciphers.clear();
ciphers.addAll(cipherProviders);
}
public static CipherManager getInstance() {
return INSTANCE;
}
public void reload() {
load();
}
public Cipher getCipher() {
if (!ciphers.isEmpty()) {
return ciphers.get(0);
}
return null;
}
}
getCipher()
方法应该根据需要实现。也许您想通过 class 名称或您服务的任何唯一 属性(例如提供者 ID、名称等)获得特权。考虑到这些,我更喜欢使用 HashMap 来捕获服务提供者(而不是 ArrayList)。
private final static HashMap<String, Cipher> ciphers = new HashMap<>();
实现单例的另一种方法是声明单元素枚举:
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.ServiceLoader;
public enum CipherManager {
INSTANCE;
private final static HashMap<String, Cipher> ciphers = new HashMap<>();
static {
load();
}
public static CipherManager getInstance() {
return INSTANCE;
}
public void reload() {
load();
}
private static void load() {
ServiceLoader<Cipher> cipherLoader = ServiceLoader.load(Cipher.class);
Iterator<Cipher> ciphersIterator = cipherLoader.iterator();
HashMap<String, Cipher> cipherProviders = new HashMap<>();
ciphersIterator.forEachRemaining(cipher -> cipherProviders.put(cipher.getClass().getName(), cipher));
updateCiphers(cipherProviders);
}
static synchronized void updateCiphers(Map<String, Cipher> cipherProviders) {
ciphers.clear();
ciphers.putAll(cipherProviders);
}
public Cipher getCipher(String className) {
return ciphers.get(className);
}
}
在这种方法中,添加了 getInstance()
方法来表示 CipherManager 遵循单例模式。因此,getInstance()
方法可以从代码中删除,我们可以直接使用 INSTANCE
字段。