代理

125 阅读2分钟

类服务加载器

ServiceLoader类可以加载一个公共接口服务,如下可通过迭代器处理所有服务的实现流查找所要的服务

省略定义一个public Cipher接口并由公共类CaesarCipher实现该接口。

//初始化一个服务加载器
public static ServiceLoader<Cipher> cipherLoader = ServiceLoader.load(Cipher.class);
//迭代器处理
public static Cipher getCipher(int minStrength) {
    for(Cipher cipher : cipherLoader) {
        if(cipher.strength() >= minStrength) return cipher;
    }
}
/**
 * ServiceLoader.Provider接口包含type和get方法,可以用来得到提供者类和提供者实例
 */
public static Optional<Cipher> getCipher2(int minStrength) {
    return cipherLoader.stream()
      .filter(descr -> descr.type() == serviceLoader.impl.CaesarCipher.class)
      .findFirst()
      .map(ServiceLoader.Provider::get);
}

代理

在运行时创建一些给定接口的新类。因为在构建一个对象时,其类可能实现了多个接口,父类可以通过newInstance方法反射找到构造器,接口同样需要类似的操作。这时候我们就需要代理,代理类包含以下的方法:

  • 指定接口所需的全部方法
  • Object类中的全部方法 这里必须提供一个调用处理器(invocation handler)。调用处理器是实现了InvocationHandler接口的类的对象。这个接口只有一个方法:

Object invoke(Object proxy, Method method, Object[] args)

当调用代理对象方法时会调用处理器的invoke方法,并向其传递Method对象和原调用参数,之后处理器会确定如何处理。

创建代理对象

使用Proxy类的newProxyInstance方法,需要三个参数:

  • 一个类加载器(class loader)
  • 一个class对象数组
  • 一个调用处理器

衍生出两个问题:如何定义处理器?代理对象的作用?

使用代理对象的目的可能如下:

  • 将方法调用路由到远程服务器
  • 在运行程序中将用户界面事件与动作关联起来
  • 为了调试,跟踪方法调用

实例

定义一个TraceHandler包装器类存储包装的对象,invoke方法会打印所调用方法的名字和参数,随后用包装的对象作为隐式参数调用这个方法:

class TraceHandler implements InvocationHandler{
    private Object target;

    public TraceHandler(Object t) {
        target = t;
    }

    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
        return m.invoke(target, args);
    }
}
var handler = new TraceHandler(value);
var interfaces = new Class[] {Comparable.class};
Object proxy = Proxy.new ProxyInstance(
    ClassLoader.getSystemClassLoader(),
    new Class[] {Comparable.class}, handler);

代理类的特性

  • 一旦被创建,它们就变成了常规类。
  • 所有代理类都扩展Proxy类
  • 一个代理类只有一个实例字段就是调用处理器
  • 所有代理类都要覆盖Object类toStringequalshashCode方法