JDK动态代理

什么是代理

所谓代理,是指对一个对象A的方法调用会被对象B先截获,然后由B来决定是继续调用A的方法或者做其他操作。也就是说,在代码中,我们直接操作的对象从A变成了B,对A的任何操作都要经过B才能完成。那么就可以说,B是A的代理

如何实现一个代理

要实现一个代理是非常简单的,只需要将代理对象B设置为目标对象A的相同类型,然后将A对象作为B对象的一个属性,然后将B对象提供出去,那么B可以当作A的代理来使用了。如果从A对象得到它的代理对象B的过程是自动完成的,那么就称之为动态代理。

注意,在代理关系中,一定会存在一个代理对象和一个目标对象,而且一般这两个对象不能是同一个。试想,如果代理对象和目标对象都是目标对象本身,则失去了代理的作用。反之,如果代理对象和目标对象都是代理对象本身,那么就会形成循环代理,也就是说,对代理对象B的任何操作,需要代理对象B询问自己该如何进行,该询问动作也包含在对B的任何操作范畴内,所以会发出对询问的询问,这在代码中就变成了无限递归了,最终会导致栈空间溢出

JDK动态动态代理的实现方式

JDK的动态代理需要提供三份信息,一个classLoader和一个接口列表,以及代理方法(InvocationHandler)。下面说说这三这的作用。

  • classLoader一定要提供,是因为代理对象和被代理对象一定要在同一个类继承体系当中,而JVM判断两个类是不是同一个类时,会将类加载器作为判断标识之一,如果类加载器不一样,则不能认为是同一个类。
  • 接口列表一定要提供,因为动态代理是要去充当目标对象被外部使用,那么代理对象一定要有目标对象的方法,外部才能正常使用,因此提供接口列表,就能协助JDK生成相应的方法签名然后供外部调用。
  • InvocationHandler一定要提供,是因为JDK并不清楚代理对象被调用时,应该如何目标对象,因此这部分逻辑需要我们来提供。在Invocationhanlder中,我们可以做一些自己的操作,然后调用目标对象,甚至,我们也不调用目标对象。

从上面那几点来看,似乎在生成动态代理的时候,并没有提供目标对象呀。确实,生成一个动态代理对象,只需要知道这个代理对象要实现的方法即可,目标对象其实是在真正调用的时候,才需要用到的。通常的做法是,在生成动态代理的类中,提供一个属性作为目标对象,然后在Invocationhanlder中调用这个目标对象的方法。如下所示:

        final DoSth worker = new DoSth()
        {
            @Override
            public void doSth()
            {
                System.out.println("I am working");
            }
        };
        final DoSth sb = (DoSth) Proxy.newProxyInstance(JdkProxyTest.class.getClassLoader(), 
                new Class[]{DoSth.class},
                new InvocationHandler()
                {
                    private DoSth target = worker;

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
                    {
                        System.out.println("proxy is called");
                        target.doSth();
                        return null;
                    }
                });

如何查看生成的代理类的代码

在代码中加入如下片段即可在源码目录中得到生成的代理类的class文件

    Field field = System.class.getDeclaredField("props");

    field.setAccessible(true);

    Properties props = (Properties) field.get(null);

    props.put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

一般生成的类名如下:

com.sun.proxy.$Proxy0

生成的代码如下:

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

posted on 2016-11-06 13:28  HuMingChuan  阅读(359)  评论(0编辑  收藏  举报

导航