根据单例设计模式使用服务加载器创建可扩展应用程序

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 字段。