代理的目的到底是什么呢?字面意思上就可以解释:代理就是“代人理事”,也就是说当实现某个操作的时候发现根本不能使用相关的对象或者使用这个对象的效果不好的时候就建立一个“代它理事”的代理对象,让代理对象去完成任务,因为我们关注的是完成任务本身,至于是谁完成了这个任务是不重要的。在这种逻辑下,只要建立的代理对象能够实现像原来那个对象一样的方法就行,更进一步说,我们新建的这个代理对象有足够大的自由度,不仅可以完成原来那个对象的功能,还能完成其他我们附加上的功能。
举两个例子:

  • 如果那个对象是一个是很大的图片,需要花费很长时间才能显示出来,那么当这个图片包含在文档中时,使用编辑器或浏览器打开这个文档,打开文档必须很迅速,不能等待大图片处理完成,这时需要做个图片Proxy来代替真正的图片。

  • 如果那个对象在Internet的某个远端服务器上,直接操作这个对象因为网络速度原因可能比较慢,那我们可以先用Proxy来代替那个对象。

我们来说说静态代理和动态代理:
静态代理:我们的代理对象是手动创建的,代理对象内部是有一个被代理对象对应的类的对象的,所以如果想要执行某些被代理对象的方法时就可以使用这个新建的代理对象的同名方法去执行,这个时候执行的逻辑里面其实就可以用这个内部对象的同名方法。

interface Subject{
        public void operate();
}

class SubjectImpl implements Subject{
        @Override
        public void operate() {
              System. out.println( "real operate");
       }
}

class SubjProxy implements Subject{
       SubjectImpl subjectImpl = new SubjectImpl();
        @Override
        public void operate() {
              System. out.println( "proxy starts");
               subjectImpl.operate();
       }
}

public class TestClass {
        public static void main(String[] args) {
              SubjProxy proxy = new SubjProxy();
              proxy.operate();
       }
}

通过上面的例子我们可以看出来新建的代理对象拥有和原对象相同的接口,所以可以像操作原对象一样地去操作代理对象,代理对象的方法还可以做出拓展,做到原对象做不了的事情。

动态代理:通过查看静态类型的例子,我们有了新的问题:我们其实并想要一个准确的内部对象来完成实际的动作,我们想要的就是原对象的类结构,然后通过重写这个类结构中的相关方法去直接完成任务,在java中,我们完全可以通过反射去解决这个问题,完全不需要再去在代理对象内部搞一个原对象类型的对象出来。

interface Subject{
        public void operate();
}

class SubjectImpl implements Subject{
        @Override
        public void operate() {
              System. out.println( "real operate");
       }
}

public class TestClass {
        public static void main(String[] args) {
              Subject subject = (Subject)Proxy.newProxyInstance(SubjectImpl. class.getClassLoader(), SubjectImpl.class.getInterfaces(), new InvocationHandler() {
                      @Override
                      public Object invoke(Object proxy, Method method, Object[] args)
                                   throws Throwable {
                                  System. out.println( "Proxy operate");
                            return null;
                     }
              });
              subject.operate();
       }
}

动态代理的方式其实是反射的一种体现,为了让最后的代理对象能够真的去执行被代理的对象才能完成的任务,代理对象必须要得到被代理对象的类的结构,这样就能获取到被代理对象的方法,截获这个方法后可以使得方法重定向到代理对象的invoke方法上执行被重写的代理方法。所以新建代理对象实例的时候要将被代理类的类加载器、类实现的接口和重写被代理类方法的处理器对象作为参数构造代理对象,类加载器用以在后面使用这个代理对象的时候能够由被代理类的类加载器所加载,类的实现接口用以使得这个代理对象可以对外宣称自己实现了这些接口,重写方法的处理器用于真正地重写那些需要代理的方法,这样一来我们可以像使用被代理对象一样去使用代理对象,实现了代理对象能够真的做到“代人理事”。

interface Subject{
        public void operate();
        public int func();
}

class SubjectImpl implements Subject{
        @Override
        public void operate() {
              System. out.println( "real operate");
       }

        @Override
        public int func() {
              System. out.println( "func");
               return 2;
       }
       
}

public class TestClass {
        public static void main(String[] args) {
              Subject subject = (Subject)Proxy.newProxyInstance(SubjectImpl. class.getClassLoader(), SubjectImpl.class.getInterfaces(), new InvocationHandler() {
                      @Override
                      public Object invoke(Object proxy, Method method, Object[] args)
                                   throws Throwable {
                            if(method.getName().equals( "operate"))
                                  System. out.println( "Proxy operate");
                            else {
                                  System. out.println( "func");
                                   return new Integer(1);
                           }
                            return null;
                     }
              });
              subject.operate();
              System. out.println(subject.func());
       }
}

代理模式在虚拟机中的实现其实也不复杂,在运行期在字节码的基础上新创建一个临时代理类,临时代理类的各方法其实就是被代理类的同名方法的反射,执行这些方法的时候就会执行被代理的类中的反射出的同名方法。这种在运行期直接通过虚拟机加入二进制字节码的方法是实现动态代理的手段:

public final class $Proxy0 extends java.lang.reflect.Proxy implements Subject{
    public $Proxy0(java.lang.reflect.InvocationHandler)       throws ;
    public final int func()       throws ;
    public final boolean equals(java.lang.Object)       throws ;
    public final int hashCode()       throws ;
    public final void operate()       throws ;
    public final java.lang.String toString()       throws ;
    static {}       throws ;
}

JinhaoPlus
1.5k 声望92 粉丝

扎瓦程序员