使用JDK动态代理实现装饰器

使用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对象上

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章