首先先理解一下为什么要使用代理。在日常生活中,代理可以解决业务相关双方直接交流的不便的问题,同时还可以提供比直接交流更多的功能,而在编程领域,代理类一般是做些除原始类核心功能以外的其他功能,比如修改权限等需要专门的代理来实现。代码每个类代表一个主要功能,而不是将所有功能混在一个类中,这样的代码清晰有条理,易于维护,如果要修改权限,不必修改原始类代码,直接修改权限代理类就可以了。这样不需要修改权限的代码可以还用原始类,需要的,就调用代理类,不会影响原来的功能。
代理类提供一个与原始相同的接口,以便可以在任何时候替代原始。代理类通常在客户端调用传递给原始类之前或之后,执行某个操作,而不是单纯地将调用传递给原始类,同时,代理类可以在执行原始类操作时,附加其他的操作,相当于对原始类进行封装。
而动态代理的作用是,只要建立一个动态代理类,就可以为多个原始类进行代理,解决了静态代理的问题(一个原始类对应一个静态代理,有多少原始类就有多少静态代理,造成代码琐碎)。
在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来看看动态代理如何使用。
最后结果:
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方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用的。
网友评论