在Android中你用过动态代理吗?Java动态代理原理解析(附github源码)

前言

如果我们是做SDK开发,最常用的架构就是MVC。在MVC中,我们经常会提供给外部C,用于让外部调用我们SDK暴露的功能。如果我们的V,要添加一个按钮,或者V的视图滑动事件,要提供给C去暴露出去,V和C基本要同时写2个方法,比如addButton(Button btn)。这个时候我们发现问题了,每次我们要去提供V、M的功能暴露出去,就要同时去C里面实现一个方法。

那有没有一个方法能够让V、M修改时,C里面有动态的添加添加一个方法。

有。那就是动态代理。

本篇文章会先讲解一个动态代理的例子,然后再从源码剖析动态代理的实现。

正片

 我们举个例子,比如我们的老板(boss)想要去买个东西,他可以让他员工(staff)去帮他买东西,那么这里员工(staff)就是老板(boss)的代理。

step1 实现老板类

那么我们先实现一个老板类Boss,实现一个buy买东西的方法。

public class Boss implements IBossImpl {
    @Override
    public String buy(Object things) {
        return "老板买"+things;
    }
}

然后我们再实现一个员工类(StaticProxyStaff),实现老板(boss)的方法接口(IBossImpl),可以看到员工买东西的时候,实际是调用的boss买东西,那么员工就代理了boss的购买方法。

public class StaticProxyStaff implements IBossImpl {
    Boss boss;

    public StaticProxyStaff(Boss boss) {
        this.boss = boss;
    }

    @Override
    public String buy(Object things) {
        return boss.buy(things);
    }
}

如果我们老板现在想要发送邮件呢?那么代理类还要实现邮件方法

那么我们如果用动态代理怎么操作呢?

step2  proxy.newProxyInstace()

我们可以看到先通过Proxy.newProxyInstance(classloader, class[], invocationHandler)生成一个接口类对象,然后调用员工(staff)购买和发送email的方法

IBossImpl staff = (IBossImpl) Proxy.newProxyInstance(DProxyTest.class.getClassLoader(),
                new Class[]{IBossImpl.class}, new Staff(new Boss(), "小秘"));
staff.buy("奶茶");
staff.email("[email protected]");

step3 InvocationHandler 

我们实现一个Staff类继承InvocationHandler,其中最重要的方法就是invoke(proxy, method, args)方法,这里我们直接调用boss对象的方法。

扫描二维码关注公众号,回复: 5331499 查看本文章
public class Staff implements InvocationHandler {
    private String name;
    private Boss boss;

    public Staff(Boss boss, String name) {
        this.boss = boss;
        this.name = name;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object object = method.invoke(boss, args);
        System.out.println(name+"替"+object);
        return object;
    }
}

结果

源码解读:

等不及的朋友可以翻到最下面看总结。

上面我们能够看到最重要的方法其实是Proxy.newProxyInstance(classloader, class[], invocationHandler),然后生成一个接口对象,至于这个对象是什么,其实我们并不知道。现在我们去看一下这个对象是什么?

我们可以看到这个对象是com.sun.proxy.$Proxy0,可以看到这个对象是可以强转为IBossImpl接口,所以可以说明这个com.sun.proxy.$Proxy0对象是继承IBossImpl接口的。

那么这个对象又是怎么生成的呢?我们看下proxy.newProxyInstace()方法。

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);
        final Class<?>[] intfs = interfaces.clone();

       //查找并生成对应的class
        Class<?> cl = getProxyClass0(loader, intfs);
        
        try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                //如果构造方法不是public 修改权限
                cons.setAccessible(true);
            }
            //构造函数,传入InvocationHandler构造对象
            return cons.newInstance(new Object[]{h});
        } 
        ...
    }

我们可以看到最重要的是getProxyClass0方法,可以看到getProxyClass0生成了一个类,然后这个类的构造函数需要传入一个invocationHandler对象。那么我们能猜到它的内部肯定是操作invocationHandler这个接口。

那么我们先来看下这个代理类的类信息,可以看到成员变量里面有5个Method类型成员变量,然后是集成Proxy类和IBossImpl接口的,他的构造函数里面有一个invocationHandler,其中有5个方法,equals、toString、hashcode、email(IBossImpl里面的方法)、buy(IBossImpl里面的方法)。看吧,果然构造函数需要传入一个invocationHandler,然后我们再来看下这个类是怎么生成的?

我们来看下getProxyClass0内部干了什么?调用的是proxyClassCache.get(classloader, interfaces)方法。

 private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        //如果代理类在缓存里面存在直接返回,不然从ProxyClassFactory创建
        return proxyClassCache.get(loader, interfaces);
    }

step4 proxyClassCache.get(classloader, interfaces)

我们可以看到返回的值是supplier.get()方法返回的,那么supplier又是什么呢?可以看到其实是新建的factory。我们看下factory的get方法。

public V get(K key, P parameter) {
        ...

        while (true) {
            if (supplier != null) {
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
           
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // 成功安装factory
                    supplier = factory;
                }
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    supplier = factory;
                } else {
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

step5 Factory.get()

可以看到valueFactory.apply()方法,我们看下这个方法

       V get(){
          try {
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
            ...
            return value;
        }

step6  ProxyClassFactory.apply(ClassLoader loader, Class<?>[] interfaces)

可以看到类的名字是com.sun.proxy.&Proxy数字,类是由Proxy.defineClass0(var1, var23, var22, 0, var22.length)生成的,其中的var22是由ProxyGenerator.generateProxyClass(var23, var2, var17)生成的byte[]。defineClass是一个native方法,我们来看一下ProxyGenerator.generateProxyClass()方法。

public Class<?> apply(ClassLoader var1, Class<?>[] var2){
            IdentityHashMap var3 = new IdentityHashMap(var2.length);
            Class[] var4 = var2;
            int var5 = var2.length;
            //检查类是否已经被classloader加载
            for(int var6 = 0; var6 < var5; ++var6) {
                Class var7 = var4[var6];
                Class var8 = null;
                try {
                    var8 = Class.forName(var7.getName(), false, var1);
                } catch (ClassNotFoundException var15) {
                    ;
                }}
            }

            if(var16 == null) {
                var16 = "com.sun.proxy.";
            }

            long var19 = nextUniqueNumber.getAndIncrement();
            String var23 = var16 + "$Proxy" + var19;
            byte[] var22 = ProxyGenerator.generateProxyClass(var23, var2, var17);

            try {
                return Proxy.defineClass0(var1, var23, var22, 0, var22.length);
            } catch (ClassFormatError var14) {
                throw new IllegalArgumentException(var14.toString());
            }
        }

step7 ProxyGenerator.generateProxyClass(var23, var2, var17)

public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        final byte[] var4 = var3.generateClassFile();
        return var4;
    }

step8 ProxyGenerator.generateClassFile()

private byte[] generateClassFile() {
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);
        Class[] var1 = this.interfaces;
        int var2 = var1.length;

        int var3;
        Class var4;
        for(var3 = 0; var3 < var2; ++var3) {
            var4 = var1[var3];
            Method[] var5 = var4.getMethods();
            int var6 = var5.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                Method var8 = var5[var7];
                this.addProxyMethod(var8, var4);
            }
        }

        Iterator var11 = this.proxyMethods.values().iterator();

        List var12;
        while(var11.hasNext()) {
            var12 = (List)var11.next();
            checkReturnTypes(var12);
        }

        Iterator var15;
        try {
            this.methods.add(this.generateConstructor());
            var11 = this.proxyMethods.values().iterator();

            while(var11.hasNext()) {
                var12 = (List)var11.next();
                var15 = var12.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
                    this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                    this.methods.add(var16.generateMethod());
                }
            }

            this.methods.add(this.generateStaticInitializer());
        } catch (IOException var10) {
            throw new InternalError("unexpected I/O Exception", var10);
        }

        if(this.methods.size() > '\uffff') {
            throw new IllegalArgumentException("method limit exceeded");
        } else if(this.fields.size() > '\uffff') {
            throw new IllegalArgumentException("field limit exceeded");
        } else {
            this.cp.getClass(dotToSlash(this.className));
            this.cp.getClass("java/lang/reflect/Proxy");
            var1 = this.interfaces;
            var2 = var1.length;

            for(var3 = 0; var3 < var2; ++var3) {
                var4 = var1[var3];
                this.cp.getClass(dotToSlash(var4.getName()));
            }

            this.cp.setReadOnly();
            ByteArrayOutputStream var13 = new ByteArrayOutputStream();
            DataOutputStream var14 = new DataOutputStream(var13);

            try {
                var14.writeInt(-889275714);
                var14.writeShort(0);
                var14.writeShort(49);
                this.cp.write(var14);
                var14.writeShort(this.accessFlags);
                var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
                var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
                var14.writeShort(this.interfaces.length);
                Class[] var17 = this.interfaces;
                int var18 = var17.length;

                for(int var19 = 0; var19 < var18; ++var19) {
                    Class var22 = var17[var19];
                    var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
                }

                var14.writeShort(this.fields.size());
                var15 = this.fields.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
                    var20.write(var14);
                }

                var14.writeShort(this.methods.size());
                var15 = this.methods.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
                    var21.write(var14);
                }

                var14.writeShort(0);
                return var13.toByteArray();
            } catch (IOException var9) {
                throw new InternalError("unexpected I/O Exception", var9);
            }
        }
    }

总结

1.动态代理使用时,需要传入一个接口A和InvocationHandler,生成一个继承Proxy、实现接口A的类的对象。

2.生成一个byte[],其中继承接口A并且实现方法,然后通过native方法Proxy.defineClass0()生成class。

3.每个方法内部调用invocationHandler的invoke(Object proxy, Method method, Object[] args)方法,其中proxy是生成的代理本身,method是接口的method,args被代理对象方法中传入的参数。

参考:

JDK动态代理[3]----WeakCache缓存的实现机制

JDK动态代理[4]----ProxyGenerator生成代理类的字节码文件解析

从一道面试题开始说起 枚举、动态代理的原理

完整Android学习路径 请戳我的Android学习之旅(持续更新中...)

从源码角度分析Activity的生命周期怎么触发的(onCreate onStart onResume onPause onStop onDestroy)(附测试代码)

基于AIDL的 Activity、Service跨进程观察者模式实现与源码解读

走进源码,Android面试最常见Handler、Looper、Message问题总结与解答

Android面试---ListView原理及fling分析

5分钟告诉你,Activity的视图绘制流程(onMeasure、onLayout、onDraw的调用和参数解释)

猜你喜欢

转载自blog.csdn.net/wangzhibo666/article/details/86677197
今日推荐