放上我参考的认为写的很好的博客链接,自己写主要是为了学习一遍,谨慎参考。
为什么要用动态代理呢?当然是静态代理太固定了,对于每一个要代理的对象,我们都要写一个代理类,不够灵活。而动态代理能够在运行时创建一个实现了一组给定接口的新类。
动态代理
代理类的特性
代理类实在程序运行过程中创建的,所有的代理类都扩展于Proxy类,为了履行代理对象的职责,所需的任何附加数据都必须存储在调用处理器中。
另外,所有的代理类都覆盖了Object中的方法,这些方法的调用都会调用调用处理器中的invoke。当然这些方法没有被重新定义。
调用处理器(invocation handler)
代理类要能够实现指定的接口,那么它需要下列方法:
- 指定接口所需要的全部方法
- Object类中的全部方法
当然,这些方法的定义自然是不能在运行时定义,这时候就需要用到调用处理器,调用处理器是实现了InvocationHandler接口的类对象。接口中有一个方法:
public Object invoke(Object proxy, Method method, Object[] args)
当调用代理对象的任何方法时,调用处理器的invoke方法都会被调用
- Object proxy
被代理对象 - Method method
要调用的方法 - Object[] args
方法调用时所需要的参数
创建代理对象(Proxy.newProxyInstance)
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
- ClassLoader loader
类的加载器 - Class<?>[] interfaces
需要实现的接口 - InvocationHandler h
一个调用处理器
使用案例(来自于JAVA核心技术卷一)
这个案例实际上代理的是Integer类以及Comparable接口
TraceHandler.java
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Arrays; public class TraceHandler implements InvocationHandler { Object target; //要代理的对象 public TraceHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /** * 打印代理方法的相关信息 */ System.out.print(target); System.out.print("."+method.getName()+"("); System.out.println(Arrays.toString(args)+")"); return method.invoke(target,args); //用反射包中的invoke方法调用实际的方法 } }
ProxyTest.java
import java.lang.reflect.Proxy; import java.util.Arrays; /** * 创建一个数组用于接受代理了Integer类的代理对象,也就是放置0到1000的数值,但不是Integer类型了。 * 代理了Comparable接口,接替了代理类(integer)中的compareTo方法,根据我们的调用处理器定义调用invoke * 方法时它会打印调用方法的相关信息 */ public class ProxyTest { public static void main(String[] args) { Object[] elements = new Object[1000]; for(int i = 0;i< elements.length;i++){ TraceHandler handler = new TraceHandler(Integer.valueOf(i)); Object proxyInstance = Proxy.newProxyInstance(Integer.class.getClassLoader(), new Class[]{Comparable.class}, handler); elements[i] = proxyInstance; } int i = Arrays.binarySearch(elements, 188); //它会多次调用compareTo方法用于比较大小,而调用compareTo方法实际上会调用invoke方法 if(i>=0) System.out.println(elements[i]); } }