美文网首页
Java代理模式

Java代理模式

作者: Whyn | 来源:发表于2017-03-27 23:35 被阅读152次

代理模式(Proxy Pattern)的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

举个例子:

比如国内网络无法直接访问google等外国网址,但是通过访问可以登陆google的服务器就可以实现跨国访问,具体访问如下:
1).本机将网络请求发送给代理服务器
2).代理服务器转发请求给web服务器
3).web服务器返回结果给代理服务器
4).代理服务器转发返回结果给本机
从上面的流程来看,处于转发的服务器实际上就是一个代理,全权负责用户的上网行为转发到实际的委托服务器上。

Java中,代理模式的实现方式有2种:

  • 静态代理:代理类的具体实现在编译时就已经确定了,编译完成后是一个具体的.class文件
  • 动态代理:代理类是在JVM运行期间才动态生成的。

静态代理实现:

由于代理类全权处理被委托类的方法,所以一般的写法是通过公共接口规范代理类和被委托类的实现。

  public interface ICrossWall {
        void visitGoogle();

        void visitYoutube();
    }

被委托类

  //被委托类:web服务器
    public class WebServer implements ICrossWall {
        @Override
        public void visitGoogle() {
            System.out.println("real subject:send http request to visit google");
        }

        @Override
        public void visitYoutube() {
            System.out.println("real subject:send http request to visit youtube");
        }
    }

代理类

  //代理类:处于中间的转发服务器
    public class ProxyServer implements ICrossWall {

        //持有具体被委托类实例
        private ICrossWall subject;

        public ProxyServer(ICrossWall subject) {
            this.subject = subject;
        }

        @Override
        public void visitGoogle() {
            System.out.println("proxy:forward http request:google");
            subject.visitGoogle();
        }

        @Override
        public void visitYoutube() {
            System.out.println("proxy:forward http request:youtube");
            subject.visitYoutube();
        }
    }

本机访问

  public static void main(String[] args) {

        //创建一个被委托类
        ICrossWall webServer = new WebServer();
        //创建一个代理类
        ICrossWall proxyServer = new ProxyServer(webServer);
        //开始翻墙
        proxyServer.visitGoogle();
        proxyServer.visitYoutube();
    }

运行结果

静态代理

现在假设想测试下看web服务器访问网页所花费的时间,那么上面的程序就要进行如下修改:

  //被委托类:web服务器
    public class WebServer implements ICrossWall {
        @Override
        public void visitGoogle() {
            long startTime = System.nanoTime();
            System.out.println("real subject:send http request to visit google");
            //模拟访问时间
            try {
                Thread.sleep(130);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            long endTime = System.nanoTime();
            System.out.println("cost:" + (endTime - startTime)/Math.pow(10,6) + "ms");
        }

        @Override
        public void visitYoutube() {
            long startTime = System.nanoTime();
            System.out.println("real subject:send http request to visit youtube");
            //模拟访问时间
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            long endTime = System.nanoTime();
            System.out.println("cost:" + (endTime - startTime)/Math.pow(10,6) + "ms");
        }
    }

结果

静态代理
从上面的代码中我们可以看出,通过静态代理实现同一附加需求需要在各个方法中都添加相应的逻辑代码,上面示例只有2个方法,手动添加还不算太麻烦,但是如果接口中含有数十上百个方法,手动添加的工作量就大大增加了,并且代码冗余度也大大增加了。那么,有没有什么办法可以优化这个流程呢?答案自然是肯定的,只需通过动态代理动态生成代理类,然后在该代理类内加上相应的计时逻辑代码即可,这样就无需修改每一个方法了,而是在调用相应方法的时候,会执行这些计时逻辑代码。具体实现请看后续部分内容。

动态代理实现

1)接口类:同上
2)被委托类:同上

  //被委托类:web服务器
    public class WebServer implements ICrossWall {
        @Override
        public void visitGoogle() {
            System.out.println("real subject:send http request to visit google");
            //模拟访问时间
            try {
                Thread.sleep(130);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void visitYoutube() {
            System.out.println("real subject:send http request to visit youtube");
            //模拟访问时间
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

3)中介类:InvocationHandler

  //计时拦截器
    public static class CostInterceptor implements InvocationHandler{
        //具体被委托类
        private Object target;
        public CostInterceptor(Object target){
            this.target = target;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            long startTime = System.nanoTime();
            //被委托类原始方法执行
            Object result = method.invoke(target, args);
            long endTime = System.nanoTime();
            System.out.println("cost:" + (endTime - startTime)/Math.pow(10,6) + "ms");
            return result;
        }
    }

4)调用

   public static void main(String[] args) {

        //创建一个被委托类
        ICrossWall webServer = new WebServer();
        //创建一个代理类
        ICrossWall dynamicProxy = (ICrossWall)Proxy.newProxyInstance(
                ICrossWall.class.getClassLoader(),
                webServer.getClass().getInterfaces(),
                new CostInterceptor(webServer));
        //开始翻墙
        dynamicProxy.visitGoogle();
        dynamicProxy.visitYoutube();
    }

结果

动态代理
从上面的动态代理示例可以看到,我们通过动态生成的代理类调用接口方法时,都会执行InvocationHandler内部invoke方法,从而让我们的附加逻辑得以运行。

动态代理实现原理简析:

InvocationHandler(中介类)持有一个被委托类对象引用,然后在内部invoke方法中对被委托类相应方法进行调用,这个实现方式看起来是不是很熟悉-,这不就是我们上面静态代理的实现方式吗!!所以,其实动态代理可以看成是2个静态代理叠加实现:
1.InvocationHandler是具体被委托类的静态代理
2.动态生成的代理是InvocationHandler的代理,InvocationHandler是具体的被委托类
所以,调用链是:DynamicProxy.method()--->InvocationHandler.invoke()--->RealSubject.method()

动态代理内部实现原理:

我们可以通过如下方法获取到动态生成的代理类.class文件,然后通过反编译.class文件就可以看到java为我们动态生成的代理类代码详情:

   /**
     * 保存代理类二进制源码
     * @param name 动态生成的代理类名称
     * @param classes 接口类(动态代理实现的接口类:dynamicProxy implements classes)
     */
    public static void createProxyClassFile(String name, Class<?>[] classes) {
        byte[] data = ProxyGenerator.generateProxyClass(name, classes);
        try {
            FileOutputStream out = new FileOutputStream(name + ".class");
            out.write(data);
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

调用一下上述方法:

 public static void main(String[] args) {
     createProxyClassFile("ProxyICrossWall", new Class[]{ICrossWall.class});
  }

生成的ICrossWall的动态代理源码如下所示:


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

    public ProxyICrossWall(InvocationHandler var1) throws  {
        super(var1);
    }

    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 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);
        }
    }

    public final void visitYoutube() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void visitGoogle() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    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);
        }
    }

    static {
        try {
            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]);
            m4 = Class.forName("com.example.ProxyDemo$ICrossWall").getMethod("visitYoutube", new Class[0]);
            m3 = Class.forName("com.example.ProxyDemo$ICrossWall").getMethod("visitGoogle", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看出:

  • 动态生成的代理类都是继承自Proxy,并且实现相应接口。
  • 调用动态代理的任何方法最终都会调用InvocationHandler.invoke()方法,包括equals(),toString(),hashCode()等等。
  • 调用链是:DynamicProxy.method()--->InvocationHandler.invoke()--->RealSubject.method()

然后,我们看下Proxy的实现:

  /**
     * 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) {
          if (obj == null)
            throw new NullPointerException();
        this.h = h;
    }

可以看到,Proxy的构造函数是private和protected的,所以Proxy是无法直接创建的。所以我们动态生成的代理类都是继承Proxy带InvocationHandler的有参构造函数。

public ProxyICrossWall(InvocationHandler var1) throws  {
        super(var1);
    }

而创建的动态代理是通过Proxy.newProxyInstance()方法生成的,那么我们看下newProxyInstance()源码(经简化,方便理解)

    /** parameter types of a proxy class constructor */
    private static final Class<?>[] constructorParams =
            { InvocationHandler.class };

    /**
     * a cache of proxy classes
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
            proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    /**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;

  /**
     * Returns an instance of a proxy class for the specified interfaces
     * that dispatches method invocations to the specified invocation
     * handler.
    **/    
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
            throws IllegalArgumentException
    {
        if (h == null)
            throw new NullPointerException();

        final Class<?>[] intfs = interfaces.clone();
        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            return cons.newInstance(new Object[]{h});
        } 
    }

newProxyInstance会从getProxyClass0()中得到一个代理类类实例(如果代理类之前已经创建过,那么会从proxyClassCache缓存中获取,否则,则创建一个),得到代理类类实例后,通过反射获取带参构造函数对象并生成代理类实例。

相关文章

  • java动态代理(JDK和cglib)(转载自http://ww

    java动态代理(JDK和cglib) JAVA的动态代理 代理模式 代理模式是常用的java设计模式,他的特征是...

  • Java代理模式之JDK动态代理

    了解什么是动态代理模式,可参考Java设计模式之代理模式 简介 JDK动态代理是java.lang.reflect...

  • java代理模式的那些事

    java代理模式-登场 什么是代理模式? 代理模式是java中的一种设计模式,它其实就是设置一个中间环节来代理你要...

  • java建造者模式

    其他设计模式java单例模式java建造者模式java策略模式java代理模式java观察者模式java适配器模式...

  • java单例模式

    其他设计模式java单例模式java建造者模式java策略模式java代理模式java观察者模式java适配器模式...

  • java外观模式

    其他设计模式java单例模式java建造者模式java策略模式java代理模式java观察者模式java适配器模式...

  • java适配器模式

    其他设计模式java单例模式java建造者模式java策略模式java代理模式java观察者模式java适配器模式...

  • java观察者模式

    其他设计模式java单例模式java建造者模式java策略模式java代理模式java观察者模式java适配器模式...

  • java代理模式

    其他设计模式java单例模式java建造者模式java策略模式java代理模式java观察者模式java适配器模式...

  • java策略模式

    其他设计模式java单例模式java建造者模式java策略模式java代理模式java观察者模式java适配器模式...

网友评论

      本文标题:Java代理模式

      本文链接:https://www.haomeiwen.com/subject/mzqdottx.html