Java 中的代理模式

为其他对象提供一个代理对象,从而让其他对象操作该代理对象来间接完成对某个指定对象的访问,这个就是代理模式。

而在 Java 中,针对代理对象的创建方式,又划分了 静态代理动态代理

静态代理

代理模式的一个特征就是 代理类和指定对象有着同样的方法调用,因此一般都是通过接口来设计代理模式。

  1. 首先定义个接口:

    public interface Subject {
    
        void request();
    
        void cancel();
    }
    
  2. 定义接口的实现类:

    public class RealSubject implements Subject {
    
        @Override
        public void request() {
            System.out.println(getClass().getCanonicalName() + "发出了请求");
        }
    
        @Override
        public void cancel() {
            System.out.println(getClass().getCanonicalName() + "取消了请求");
        }
    }
    
  3. 定义一个代理对象:

    public class ProxySubject implements Subject {
    
        private Subject mSubject;
    
        public ProxySubject(Subject subject) {
            this.mSubject = subject;
        }
    
       @Override
        public void request() {
            if (mSubject != null) {
                mSubject.request();
            }
        }
    
        @Override
        public void cancel() {
            if (mSubject != null) {
                mSubject.cancel();
            }
        }
    }
    
  4. 最后直接调用即可:

    public class Client {
    
        public static void main(String[] args) {
            RealSubject realSubject = new RealSubject();
            ProxySubject proxySubject = new ProxySubject(realSubject);
            proxySubject.request();
            proxySubject.cancel();
        }
    }
    

以上就是简单的静态代理模式,通过上面的代码我们可以知道代理模式可以帮我们解决 Java 中让人讨厌的空指针问题。

正确来说,我们可以通过代理模式来在执行指定对象的方法之前,添加各种私货,这也正是 AOP(面向切面编程 ) 的原理,我们能在 AOP 的一个切点执行之前或之后加入一些操作,就是因为这些切点其实都给代理了,在代理的过程加入一些我们期待的操作,如上面的判断非空的操作。

除此之外,代理模式能很好地隔离各个代码模块,同时代理模式还可以延迟操作对象的拷贝,及时断开和操作对象的关系,如果在某个操作对象是非常耗费资源的,那么我们也应该使用代理模式去操作它,减少一些不要的开销。

动态代理

静态代理是代码编程成 class 时 生成代理对象,而动态代理则是代码运行时在内存里面生成一个代理对象,也就是 proxy 对象。

在 Java 的动态代理机制中,有两个关键的 API 是必须知道的,一个是 InvocationHandler(Interface)、另一个则是 Proxy(class)

先看一下 InvocationHandler 这个接口,因为生成一个动态代理类这个是不可缺的

public interface InvocationHandler {
   /**
    * @param proxy  动态生成的代理对象
    * @param method 我们所要调用操作对象的某个方法的Method对象
    * @param args   调用操作对象某个方法时接受的参数
    * @return 操作对象的方法返回的值或null
    * @throws Throwabl 可能遇到的异常
    */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

搞明白接口方法的参数后,那么就好办了:

public class SubjectInvocationHandler implements InvocationHandler {

    private Subject mSubject;

    public SubjectInvocationHandler(Subject subject) {
        this.mSubject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //这里完成操作方法调用的反射处理
        return method.invoke(mSubject, args);
    }
}

然后再去看看 Proxy 这个类,它的作用就是用来动态创建一个代理对象的类,通常我们使用 newProxyInstance 这个方法来创建代理对象:

/**
* @param loader  定义了由哪个ClassLoader对象来对生成的代理对象进行加载
* @param interfaces  一个Interface对象的Class数组,表示生成的代理对象需要实现那些接口
* @param h   一个InvocationHandler对象,表示的是当这个动态代理对象在调用方法的时候
*            ,会关联到哪一个InvocationHandler对象上
* @return 实现了指定接口的代理对象
*/
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

把这个 API 也搞明白后,接下来也好办了:

public class Client {

    public static void main(String[] args) {
        Subject subject = new RealSubject();
        //生成一个InvocationHandler
        SubjectInvocationHandler handler = new SubjectInvocationHandler(subject);
        //生成动态代理对象
        Subject proxySubject = (Subject)Proxy.newProxyInstance(subject.getClass()
        .getClassLoader(), new Class[]{Subject.class}, handler);
        proxySubject.request();
        proxySubject.cancel();
    }
}

看一下控制台的输出:

proxy.RealSubject发出了请求
proxy.RealSubject取消了请求

可以看到,动态代理可以很方便的对操作对象的方法进行统一的处理,而不用修改每个代理对象中的方法,同时也是一个解耦的好方法,比如著名的网络请求框架 retrofit 的核心模式就是动态代理。这里虽然用到不少反射,但其实在反射次数不多的情况下,对性能影响其实是可以忽略不计的。

应该有人会疑惑到底是怎么生成代理对象的,为什么代理对象执行的方法都会通过 InvocationHandler 中的invoke 方法来执行,让我们带着疑问去看下源码

动态代理的原理分析

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces
									,InvocationHandler h)
{
      //得到全部的接口
      final Class<?>[] intfs = interfaces.clone();
      ....
      //生成实现指定接口的代理对象的Class对象
      Class<?> cl = getProxyClass0(loader, intfs);
      ....
      //获取代理对象的构造方法对象    
      final Constructor<?> cons = cl.getConstructor(constructorParams);
      ....
      //初始化代理对象    
      return cons.newInstance(new Object[]{h});
}

从上面的代码就可以知道,这里是直接生成代理对象的 Class 对象,然后再进行得到我们需要的代理对象,这里就不具体分析是如何生成 Class 对象了,知道它是存放在内存之中就足够了,我们可以通过 JDK 自带的方法将它转为 byte 数组,然后打印到本地:

 byte[] buffer = ProxyGenerator.generateProxyClass(subject.getClass().getSimpleName()
                , subject.getClass().getInterfaces());
 try {
       FileOutputStream fos = new FileOutputStream("out/SubjectProxy.class");
       fos.write(buffer);
       fos.flush();
       fos.close();
      } catch (Exception e) {
       e.printStackTrace();
      }

运行就可以看到生成的 class 文件了:

在这里插入图片描述

通过 IDE 的反编译可以很轻松看到里面的代码:

public final class RealSubject extends Proxy implements Subject {
    private static Method m1;
    private static Method m4;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public RealSubject(InvocationHandler var1) throws  {
        super(var1);
    }
    
    //发射生成接口的方法对象
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m4 = Class.forName("proxy.Subject").getMethod("cancel");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("proxy.Subject").getMethod("request");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }

    public final void cancel() throws  {
        try {
            //这里调用了InvocationHandler的方法,再联想到刚才实现的InvocationHandler对象
            //简单明了
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void request() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    
    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
    
    
    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    
    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
}

动态代理直接为我们生成了一个 RealSubject 的代理类(好想吐槽这名字),这个类文件是存放在内存中的,我们运行代码创建代理对象就是通过反射获得这个类的构造方法,然后创建的代理实例。

InvocationHandler 其实就是个中间人,它真正持有了操作对象的实例,通过组合的方式完成了代理对象对操作对象的方法调用,也就是上面看到了代理对象 RealSubject 里面通过调用 InvocationHandlerinvoke 方法最终完成了对操作对象的方法调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值