在Java的动态代理机制中,有两个重要的类。一个是InvocationHandler,另一个是Proxy。

  • InvocationHandler:每一个动态代理类都必须要实现InvocationHandler接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke方法来进行调用。

    /**
     * 
     * @param proxy     代理的真实对象
     * @param method    调用真实对象的某个方法的method对象
     * @param args      调用方法时传的参数
     * @return          调用结果
     * @throws Throwable
     */@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {}
  • Proxy:动态的创建一个代理对象的类,我们用的比较多的就是newProxyInstance这个方法。

     /**
     *
     * @param loader      ClassLoader对象,由哪个ClassLoader对象来对生成的代理对象进行加载
     * @param interfaces  Interface对象数组,提供一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这个我们就能调用这组接口中的方法了
     * @param h           InvocationHandler对象,当这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
     */@CallerSensitivepublic static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) throws IllegalArgumentException {}
  • Demo

    Subject接口

    public interface Subject {    void say(String str);
    }

    RealSubject类

    public class RealSubject implements Subject {    @Override
        public void say(String str) {
            System.out.println("hello , " + str);
        }
    }

    动态代理类

    public class DynamicProxy implements InvocationHandler {    // 要代理的真实对象
        private Object target;    // 给代理的真实对象赋初始值
        public DynamicProxy(Object target) {        this.target = target;
        }    @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            before();        // 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
            method.invoke(target, args);
            after();        return null;
        }    public void before() {
            System.out.println("before...");
        }    public void after() {
            System.out.println("after...");
        }
    }

    Client类

    public class Client {    public static void main(String[] args) {
            Subject realSubject = new RealSubject();        // 将要代理的真实对象传进去,最后是通过该真实对象来调用其方法的
            InvocationHandler handler = new DynamicProxy(realSubject);        // 通过Proxy来创建对象
            Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler);
            System.out.println(subject.getClass().getName());
            subject.say("webb");
        }
    }

    输出结果

    com.sun.proxy.$Proxy0
    before...
    hello , webbafter...

    我们看到subject.getClass().getName()看到打印出来的是 $Proxy0,首先解释下为什么这里可以将其转换为Subject类型的对象?原因就是在newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么这个代理对象就会实现这种组接口,这个时候我们可以将这个代理对象强制类型转化为这组接口中的任意一个,因为这里的接口是Subject类型,所以就可以将它转化为Subject类型。同时我们一定要记住,通过Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。

    当我们执行subject.say("webb");的时候,会自动跳转到由这个代理对象关联到的handler中的invoke方法去执行,而handler对象又接受了一个RealSubject类型的参数,表示我要代理的就是这个真实对象,所以此时就会调用handler中的invoke方法去执行。