类服务加载器
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类的toString、equals和hashCode方法