代理模式

代理模式是Java常用的设计模式,由代理类和委托类以及它们共同实现的接口组成;客户端通过调用代理类的方法间接调用委托类的方法,代理类可以在调用委托类方法之前做预处理和调用之后后续处理等操作。代理模式结构图:


image.png

静态代理

静态代理:代理类、委托类等在编译期就已经确定下来,在程序运行时相关的.class文件已经存在。
根据上面的结构图,来简单实现一个静态代理的例子。这里随便举个例子,假如某小姐姐购买了一份外卖,外卖需要快递小哥送给他,这里快递小哥就相当于代理类;
首先,创建一个Person接口作为快递小哥(代理类)和小姐姐(委托类)的公共接口;

public interface Person {
    void buy();
}

Girl类实现Person接口,具体实现购买的动作:

public class Girl implements Person {

    @Override
    public void buy() {
        System.out.print("购买了一份麻辣烫");
    }
}

Waiter类为代理类,也实现Person接口,同时持有Girl对象,代理Girl对象的方法:

public class Waiter implements Person {
    private Girl girl;

    public Waiter(Girl girl) {
        this.girl = girl;
    }

    @Override
    public void buy() {
        System.out.print("外卖小哥送餐");
        girl.buy();
    }
}

下面执行一下:

public class Clent {
    public static void main(String... args) {
        Girl girl = new Girl();
        Waiter waiter = new Waiter(girl);
        waiter.buy();
    }
}

执行结果如下:

外卖小哥送餐
购买了一份麻辣烫
Process finished with exit code 0

上面说过客户端通过代理类间接调用委托类的方法,而代理类在代理执行的之前可以做预处理和后续处理。比如这个例子,假如小姐姐想让外卖小哥送餐过来的时候从楼下超市带一瓶水,这时通过代理类就可以做到,而不用去修改委托类的方法:

public class Waiter implements Person {
    private Girl girl;

    public Waiter(Girl girl) {
        this.girl = girl;
    }

    @Override
    public void buy() {
        System.out.println("从楼下超市买了一瓶水");
        girl.buy();
    }
}

运行结果如下:

从楼下超市买了一瓶水
购买了一份麻辣烫
Process finished with exit code 0

可以看到,只需要在代理类中送餐之前,执行其他操作就可以了。这种操作,也是使用代理模式的一个很大的优点。最直白的就是在Spring中的面向切面编程(AOP),我们能在一个切点之前执行一些操作,在一个切点之后执行一些操作,这个切点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。

动态代理

在上面静态代理的例子中,代理类是预先定义好的,在程序运行之前已经编译完成;而在动态代理中,代理类是在程序运行时动态生成的;下面简单实现一下动态代理;

在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
首先创建一个实现了nvocationHandler接口的类:

public class MyInvocationHandler implements InvocationHandler {
    private Girl girl;

    public MyInvocationHandler(Girl girl) {
        this.girl = girl;
    }

    /**
     * proxy:代表动态代理对象
     * method:代表正在执行的方法
     * args:代表调用目标方法时传入的实参
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(girl, args);
        return result;
    }
}

做完上面的工作我们就可以创建代理对象了,请看代码:

Girl girl = new Girl();
MyInvocationHandler invocationHandler = new MyInvocationHandler(girl);
Person proxyInstance = (Person) Proxy.newProxyInstance(girl.getClass().getClassLoader(), new Class[]{Person.class}, h);
proxyInstance.buy();

通过Proxy.newProxyInstance方法创建代理类,该方法有三个参数:

-loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
-interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
-h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

执行代码结果如下:

购买了一份麻辣烫
Process finished with exit code 0

上面说到,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。是因为所有被代理执行的方法,都是通过在InvocationHandler中的invoke方法调用的,所以我们只要在invoke方法中统一处理,就可以对所有被代理的方法进行相同的操作了。

动态代理原理分析

在动态代理中代理关系并不像静态代理那样一目了然,我们并没有看到代理类,而是利用Proxy类的newProxyInstance方法创建了一个动态代理对象,通过InvocationHandler的invoke来实现代理过程的,其中具体是怎么操作的呢?
我们来看看Proxy类的newProxyInstance方法的源码:

public static Object newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h) throws IllegalArgumentException { 

        Objects.requireNonNull(h);

        final Class[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

我们需要关注的是 Class cl = getProxyClass0(loader, intfs);这行代码;在这里,产生了代理类,在通过后面获取构造器产生了代理对象;
在创建代理类对象时 cons.newInstance(new Object[]{h});从代码中我们看到InvocationHandler 对象h通过Object[]的形式传给了代理类,在此我们猜想代理类可能持有InvocationHandler 类型的成员参数,我们可以打印一下代理类验证一下我们的猜想。
代理类代码如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Person;

public final class $Proxy0 extends Proxy implements Person
{
  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;
  
  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }
  
  //这个静态块本来是在最后的,我把它拿到前面来,方便描述
   static
  {
    try
    {
      //看看这儿静态块儿里面有什么,是不是找到了giveMoney方法。请记住giveMoney通过反射得到的名字m3,其他的先不管
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
 
  /**
  * 
  *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
  *this.h.invoke(this, m3, null);这里简单,明了。
  *来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
  *再联系到InvacationHandler中的invoke方法。嗯,就是这样。
  */
  public final void buy()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  //注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和giveMoney方法一毛一样。

}

我们看到代理类中并没有InvocationHandler 类型的成员参数,但该类继承了Proxy 类,我们看下它的构造方法:

public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

我们看到h对象通过构造方法传给了父类,也就是Proxy ,那Proxy 中会不会有呢,我们去Proxy 找找

protected InvocationHandler h;

    /**
     * Prohibits instantiation.
     */
    private Proxy() {
    }

    /**
     * Constructs a new {@code Proxy} instance from a subclass
     * (typically, a dynamic proxy class) with the specified value
     * for its invocation handler.
     *
     * @param  h the invocation handler for this proxy instance
     *
     * @throws NullPointerException if the given invocation handler, {@code h},
     *         is {@code null}.
     */
    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

上面是Proxy类的部分源码,我们看到Proxy中持有一个InvocationHandler 成员参数,我们在前文创建代理类对象时传入的InvocationHandler 将会复制给该成员参数,代理类对象继承了InvocationHandler 参数;
我们再来看看$Proxy0中的buy()方法:

 public final void buy()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

buy()方法直接调用了h.invoke,前面已经清楚了h就是我们传入的InvocationHandler 对象,所以我们在自定义的MyInvocationHandler的invoke方法中即可实现代理的目标;

你可能感兴趣的:(代理模式)