深入了解java proxy代理

前段时间去阿里面试被问到 java proxy 感觉自己回答的不是很理想,所以打算通过查看jdk源码深入的学习一下java 动态代理;

上代码:

先写一个接口ProxyTest:

public interface ProxyTest {
    void test1();
}

写一个实现类ProxyTestImpl:

public class ProxyTestImpl implements ProxyTest {
    @Override
    public void test1() {
        System.out.println("方法test执行");
    }
}

写一个main方法实现代理:

public static void main(String[] args) {
        ProxyTest proxyTest = new ProxyTestImpl();
        InvocationHandler h = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getName().equals("test1")) {
                    System.out.println("执行方法前");
                    method.invoke(proxyTest, args);
                    System.out.println("执行方法后");
                }
                return null;
            }
        };
        ProxyTest proxy1 = (ProxyTest) Proxy.newProxyInstance(TEst.class.getClassLoader(), proxyTest.getClass().getInterfaces(), h);
        proxy1.test1();

    }

通过上面代码可以看到在调用方法前和行方法后执行了自己的代码,打印出一段提示文字

执行结果:

很简单的一段动态代理代码,但是请思考一个问题,最终代理得到的proxy1是什么?debug看一下

貌似是一个名为$proxy0@649的一个类实例,并且里面有一个InvocationHandler 的实例 h,h里面有一个接口实现类的实例

ProxyTestImpl@647,到这里我们知道,动态代理是原理是Proxy为我们生成了一个代理类,但是这个类内容是什么?debug看不出来,那我们就继续往下看找这个生成的类存到了哪里(磁盘还是内存)?

查看 Proxy.newProxyInstance 方法源码关键一句:

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

很明显这就是生成类的方法,继续往下看 getProxyClass0 方法 :

    /**
     * Generate a proxy class.  Must call the checkProxyAccess method
     * to perform permission checks before calling this.
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

 

 继续往下 看 proxyClassCache.get(loader, interfaces);

这个方法代码有点多就不贴了,意思就是先要去缓存weakCache中找一下,如果之前生成过这个代理类就会直接返回,否则才去重新生成代理类,并且放入缓存;贴几句关键代码:

   while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
 

 

往下看 suppelier.get()

  @Override
        public synchronized V get() { // serialize access
            // re-check
            Supplier<V> supplier = valuesMap.get(subKey);
            if (supplier != this) {
                // something changed while we were waiting:
                // might be that we were replaced by a CacheValue
                // or were removed because of failure ->
                // return null to signal WeakCache.get() to retry
                // the loop
                return null;
            }
            // else still us (supplier == this)

            // create new value
            V value = null;
            try {
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
            // the only path to reach here is with non-null value
            assert value != null;

            // wrap value with CacheValue (WeakReference)
            CacheValue<V> cacheValue = new CacheValue<>(value);

            // try replacing us with CacheValue (this should always succeed)
            if (valuesMap.replace(subKey, this, cacheValue)) {
                // put also in reverseMap
                reverseMap.put(cacheValue, Boolean.TRUE);
            } else {
                throw new AssertionError("Should not reach here");
            }

            // successfully replaced us with new CacheValue -> return the value
            // wrapped by it
            return value;
        }
    }

关键的一句代码 valueFactory.apply(key, parameter) 继续向下跟 

apply方法中关键几句代码:

 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }

终于看到反回字节码的方法了,,,就是 ProxyGenerator.generateProxyClass,快进去看看!

public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        final byte[] var4 = var3.generateClassFile();
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if (var1 > 0) {
                            Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                            Files.createDirectories(var3);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
                            var2 = Paths.get(var0 + ".class");
                        }

                        Files.write(var2, var4, new OpenOption[0]);
                        return null;
                    } catch (IOException var4x) {
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                }
            });
        }

        return var4;
    }

查看这个方法代码可以知道如果saveGeneratedFiles值为false,生产字节码就直接返回去了,如果值为true就会生成class文件,那我想看class文件就得把这个值设置为true,看一下这个值在哪里定义

private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));

可以看到,这个值是去读的系统变量 sun.misc.ProxyGenerator.saveGeneratedFiles,那我就去把这个变量设置一下,就在main方法的最前面添加一句代码:

System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

然后debug运行,为了拿到class存放路径,就又在Files.write(var2, var4, new OpenOption[0]);往下跟了几个方法,大概调用关系如下:

一直到newByteChannel方法

终于找到代理类路径了,现在就去看一下这个类里到底有啥!代码如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

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

public final class $Proxy0 extends Proxy implements ProxyTest {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

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

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } 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 test1() 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);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("ProxyTest").getMethod("test1");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

关键两句代码:

 super.h.invoke(this, m3, (Object[])null);

 m3 = Class.forName("ProxyTest").getMethod("test1");

可以看到,test1方法调用的是 InvocationHandler.invoke 方法 传入的第二个参数是接口ProxyTest的方法test1;该接口实现类是ProxyTestImpl,所以真正调用的是 ProxyTestImpl 的 test1 方法;再回头看main方法的代码就明白了,哦 原来是这样,搜嘎!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值