Java--动态代理

首先先理解一下为什么要使用代理。在日常生活中,代理可以解决业务相关双方直接交流的不便的问题,同时还可以提供比直接交流更多的功能,而在编程领域,代理类一般是做些除原始类核心功能以外的其他功能,比如修改权限等需要专门的代理来实现。代码每个类代表一个主要功能,而不是将所有功能混在一个类中,这样的代码清晰有条理,易于维护,如果要修改权限,不必修改原始类代码,直接修改权限代理类就可以了。这样不需要修改权限的代码可以还用原始类,需要的,就调用代理类,不会影响原来的功能。
代理类提供一个与原始相同的接口,以便可以在任何时候替代原始。代理类通常在客户端调用传递给原始类之前或之后,执行某个操作,而不是单纯地将调用传递给原始类,同时,代理类可以在执行原始类操作时,附加其他的操作,相当于对原始类进行封装。
而动态代理的作用是,只要建立一个动态代理类,就可以为多个原始类进行代理,解决了静态代理的问题(一个原始类对应一个静态代理,有多少原始类就有多少静态代理,造成代码琐碎)。
在java的动态代理机制中,有一个重要的接口 InvocationHandler(Interface)和一个重要的类 Proxy(Class),这两个是实现动态代理所必须用到的。每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用

* @param proxy 所代理的真实对象
     *            the proxy instance on which the method was invoked
     * @param method 所要调用真实对象的某个方法的Method对象
     *            the method invoked on the proxy instance
     * @param args 调用真实对象某个方法时接受的参数
     *            an array of objects containing the parameters passed to the
     *            method, or {@code null} if no arguments are expected.
     *            Primitive types are boxed.
     *
     * @return the result of executing the method. Primitive types are boxed.
     *
     * @throws Throwable
     *             the exception to throw from the invoked method on the proxy.
     *             The exception must match one of the declared exception types
     *             of the invoked method or any unchecked exception type. If not
     *             then an {@code UndeclaredThrowableException} is thrown
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

再来看看Proxy类,它的作用是用来动态创建一个代理对象的类,它提供了许多的方法,但是用的最多的就是 newProxyInstance 这个方法

* @param loader 一个ClassLoader对象,
                            定义了由哪个ClassLoader对象来对生成的代理对象进行加载
     *            the class loader that will define the proxy class
     * @param interfaces 一个Interface对象的数组,
                                表示的是将要给需要代理的对象提供一组什么接口,
                                如果提供了一组接口给它,
                                那么这个代理对象就宣称实现了该接口(多态),
                                这样就能调用这组接口中的方法了
     *            an array of {@code Class} objects, each one identifying an
     *            interface that will be implemented by the returned proxy
     *            object
     * @param invocationHandler 一个InvocationHandler对象,
                                        表示的是当这个动态代理对象在调用方法的时候,
                                        会关联到哪一个InvocationHandler对象上
     *            the invocation handler that handles the dispatched method
     *            invocations
     * @return a new proxy object that delegates to the handler {@code h}
     * @throws IllegalArgumentException
     *                if any of the interface restrictions are violated
     * @throws NullPointerException
     *                if the interfaces or any of its elements are null
     */
    public static Object newProxyInstance(ClassLoader loader, Class[] interfaces,
                                          InvocationHandler invocationHandler)
            throws IllegalArgumentException {

之后看一个demo DynamicProxy20170607来看看动态代理如何使用。
最后结果:


Java--动态代理_第1张图片
Paste_Image.png

“$ProxyN”是代理类名称,由


Paste_Image.png

打印出的,其中 N 是一个逐一递增的阿拉伯数字,代表 Proxy 类第 N 次生成的动态代理类,但不是每次调用 Proxy 的静态方法创建动态代理类都会使得 N 值增加,因为如果对同一组接口(包括接口排列的顺序相同)试图重复创建动态代理类,它会返回先前已经创建好的代理类的类对象,而不会再尝试去创建一个全新的代理类,这样可以节省不必要的代码重复生成,提高了代理类的创建效率。
通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是InvocationHandler类型,也不是定义的那组接口的类型,而是在运行是动态生成的一个对象。
之后调用
Paste_Image.png

通过代理对象来调用接口中的方法,程序就会跳转到由这个代理对象关联到的 handler 中的invoke方法去执行,而这个 handler 对象又接受了一个 helloImpl类型的参数


Paste_Image.png

表示要代理的就是这个helloImpl真实对象,所以此时就会调用 handler(DynamicProxy) 中的invoke方法去执行,可以看到DynamicProxy中的invoke不仅调用了helloImpl这个真实对象(helloImpl实现了Hello接口),还在前后添加了一些操作,另外通过
Paste_Image.png

可以看到具体调用了什么方法,
结果中before say hello,和after say hello都是添加的操作,从Method:public abstract void DynamicProxy20170607.Hello.helloCat()可以看到,确实是调用的Hello接口helloCat的实现方法。
Paste_Image.png

这也就证明了当通过代理对象来调用方法的时候,起实际就是委托由其关联到的 handler 对象的invoke方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用的。

你可能感兴趣的:(Java--动态代理)