使用JDK动态代理实现装饰器
众所周知,装饰器模式和代理模式非常类似,只不过一个是为了增强功能,一个是访问控制。
最近在做一个项目,中间遇到了这么一个需求,在运行时动态的给一个类新增接口。
假设现有的类A是接口 IA 的实现,期望其能够增加接口 IB 的实现,为了使用 IB 的方法,首先需要先实现这个方法,可以使用接口默认实现,然后声明一个匿名类实现,不过为了简单起见,创建了一个类 B,其实现了接口 IB。
class Main{
static interface IA {
void a();
}
static interface IB {
void b();
}
static class A implements IA {
@Override
public void a() {
System.out.println("a");
}
}
static class B implements IB {
@Override
public void b() {
System.out.println("b");
}
}
public static void main(String[] args) {
A a = new A();
B b = new B();
Class<?>[] interfaces = a.getClass().getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println(anInterface);
}
Class<?>[] newInterface = new Class[interfaces.length + 1];
System.arraycopy(interfaces, 0, newInterface, 0, interfaces.length);
newInterface[interfaces.length] = IB.class;
Set<Method> collect = Stream.of(IB.class.getMethods()).collect(Collectors.toSet());//获取接口 IB 的函数列表
Object o = Proxy.newProxyInstance(a.getClass().getClassLoader(), newInterface, (proxy, method, arg) -> {
if (collect.contains(method)) {//如果是 IB 的函数调用,则使用b 对象进行实现
return method.invoke(b, arg);
} else {
return method.invoke(a, arg);
}
});
System.out.println(o instanceof IB);
((IA) o).a();
((IB) o).b();
}
}
这个方式可以组合两个完全不相干的接口,因此系统中的非功能型接口,例如打印时希望有一个有意义的名字,名字可以运行时设置,就可以不去都继承非功能型接口,而是使用装饰器将他们组合起来,避免因为继承导致的体系脆弱以及现有系统的大量修改。
InvocationHandler接口
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
我们看到这个方法一共接受三个参数,那么这三个参数分别代表什么呢?
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
proxy: - 指代我们所代理的那个真实对象
method: - 指代的是我们所要调用真实对象的某个方法的Method对象
args: - 指代的是调用真实对象某个方法时接受的参数
Proxy#newProxyInstance函数
这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException;
loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上