手写一个高仿低配的JDK动态代理

2020-03-04 14:46
1121
0
前文,分析了JDK动态代理的源码,前文地址:点这里,今天就来写写怎么仿照JDK动态代理的思路,来手写一个高仿低配的动态代理。
 
一、简单梳理JDK动态代理
 
在此再重新理一理JDK动态代理是怎么实现的:
1、JDK动态代理是基于接口进行代理。
2、通过实现JDK的代理处理器接口(InvocationHandler)的invoke方法,利用反射来实现代理逻辑。
3、通过动态代理类Proxy中的newProxyInstance方法,传入类加载器、代理的接口和InvocationHandler实例来生成代理对象。
newProxyInstance里做了什么?
1、通过传入的接口生成代理类的class
2、通过class生成构造函数
3、通过构造函数把InvocationHandler的处理器实例进去,实例化对象。
 
二、手写动态代理
 
按照上面的梳理,来一步步手写一个简易的动态代理。
被代理的类还是用的前文的UserService和UserServiceImpl,这里就不再贴出,以免重复。
2.1、自定义处理器接口:MyInvocationHandler
package com.cyx.demo.proxy;

import java.lang.reflect.Method;

/**
 * @see java.lang.reflect.InvocationHandler
 * 自定义的动态代理处理器接口,直接抄的JDK中的InvocationHandler
 */
public interface MyInvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
2.2、动态代理类实现:MyProxy
package com.cyx.demo.proxy;

import sun.reflect.misc.ReflectUtil;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;


/**
 * 高仿低配动态代理类
 */
public class MyProxy {

    protected MyInvocationHandler h;

    private MyProxy() {
    }
    protected MyProxy(MyInvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

    private static final AtomicLong nextUniqueNumber = new AtomicLong();
    private static final String proxyClassNamePrefix = "$MyProxy";
    private static final Class<?>[] constructorParams = { MyInvocationHandler.class };
    private static final MyProxyClassLoader loader = new MyProxyClassLoader();

    public static Object newProxyInstance(Class<?>[] interfaces, MyInvocationHandler h) {
        try {
            //1、生成代理类的class
            Class<?> cl = getProxyClass0(interfaces);//生成了cyx.demo.proxy.$MyProxy0的Class,这里还没初始化
            //2、通过class获得构造函数
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            //3、通过构造函数,把处理器传入进去,实例化对象
            return cons.newInstance(new Object[]{h});
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    public static Class<?> getProxyClass0(Class<?>[] interfaces) {
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
         //为要生成的代理类选择一个名称,其实就是在类名$Proxy后面增加自增的数字
        long num = nextUniqueNumber.getAndIncrement();
        String proxyName =  "cyx.demo.proxy." + proxyClassNamePrefix + num;
        //修改了ProxyGenerator类,因为其内代码是写死的JDK中的Invoationhandler和Proxyy引用
        byte[] proxyClassFile = MyProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
        try {
            //通过类加载器中的defineClass方法实现对字节数组的加载
          return  loader.loadClass(proxyName,proxyClassFile);
        } catch (ClassFormatError e) {
            throw new IllegalArgumentException(e.toString());
        }
    }
}

/**
 * 因为类加载器中的defineClass方法是protected类型的,在此通过继承ClassLoader的方式调用
 */
class MyProxyClassLoader extends  ClassLoader{
    public  Class<?> loadClass(String name,byte[] bytes) {
             return defineClass(name, bytes, 0, bytes.length);
    }
}
 
2.3、重写ProxyGenerator类:MyProxyGenerator
ProxyGenerator是jre的lib文件夹下的rt.jar包里的类,因为其内代码是写死的JDK中的Invoationhandler和Proxy引用,所以在此对其进行反编译,然后改成我们自定义的MyInvoationhandler和MyProxy。也可以通过OpenJDK拿到其源码,但是因为OpenJDK源码下载速度太慢了,下不下来,所以粘贴出的源码可读性不好,见谅。
package com.cyx.demo.proxy;


import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import sun.security.action.GetBooleanAction;

public class MyProxyGenerator {
    private static final int CLASSFILE_MAJOR_VERSION = 49;
    private static final int CLASSFILE_MINOR_VERSION = 0;
    private static final int CONSTANT_UTF8 = 1;
    private static final int CONSTANT_UNICODE = 2;
    private static final int CONSTANT_INTEGER = 3;
    private static final int CONSTANT_FLOAT = 4;
    private static final int CONSTANT_LONG = 5;
    private static final int CONSTANT_DOUBLE = 6;
    private static final int CONSTANT_CLASS = 7;
    private static final int CONSTANT_STRING = 8;
    private static final int CONSTANT_FIELD = 9;
    private static final int CONSTANT_METHOD = 10;
    private static final int CONSTANT_INTERFACEMETHOD = 11;
    private static final int CONSTANT_NAMEANDTYPE = 12;
    private static final int ACC_PUBLIC = 1;
    private static final int ACC_PRIVATE = 2;
    private static final int ACC_STATIC = 8;
    private static final int ACC_FINAL = 16;
    private static final int ACC_SUPER = 32;
    private static final int opc_aconst_null = 1;
    private static final int opc_iconst_0 = 3;
    private static final int opc_bipush = 16;
    private static final int opc_sipush = 17;
    private static final int opc_ldc = 18;
    private static final int opc_ldc_w = 19;
    private static final int opc_iload = 21;
    private static final int opc_lload = 22;
    private static final int opc_fload = 23;
    private static final int opc_dload = 24;
    private static final int opc_aload = 25;
    private static final int opc_iload_0 = 26;
    private static final int opc_lload_0 = 30;
    private static final int opc_fload_0 = 34;
    private static final int opc_dload_0 = 38;
    private static final int opc_aload_0 = 42;
    private static final int opc_astore = 58;
    private static final int opc_astore_0 = 75;
    private static final int opc_aastore = 83;
    private static final int opc_pop = 87;
    private static final int opc_dup = 89;
    private static final int opc_ireturn = 172;
    private static final int opc_lreturn = 173;
    private static final int opc_freturn = 174;
    private static final int opc_dreturn = 175;
    private static final int opc_areturn = 176;
    private static final int opc_return = 177;
    private static final int opc_getstatic = 178;
    private static final int opc_putstatic = 179;
    private static final int opc_getfield = 180;
    private static final int opc_invokevirtual = 182;
    private static final int opc_invokespecial = 183;
    private static final int opc_invokestatic = 184;
    private static final int opc_invokeinterface = 185;
    private static final int opc_new = 187;
    private static final int opc_anewarray = 189;
    private static final int opc_athrow = 191;
    private static final int opc_checkcast = 192;
    private static final int opc_wide = 196;
    private static final String superclassName = "com/cyx/demo/proxy/MyProxy";
    private static final String handlerFieldName = "h";
    private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("MyProxyGenerator.saveGeneratedFiles"));
    private static Method hashCodeMethod;
    private static Method equalsMethod;
    private static Method toStringMethod;
    private String className;
    private Class<?>[] interfaces;
    private int accessFlags;
    private MyProxyGenerator.ConstantPool cp = new MyProxyGenerator.ConstantPool();
    private List<MyProxyGenerator.FieldInfo> fields = new ArrayList<>();
    private List<MyProxyGenerator.MethodInfo> methods = new ArrayList<>();
    private Map<String, List<MyProxyGenerator.ProxyMethod>> proxyMethods = new HashMap<>();
    private int proxyMethodCount = 0;

    public static byte[] generateProxyClass(String var0, Class<?>[] var1) {
        return generateProxyClass(var0, var1, 49);
    }

    public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
        MyProxyGenerator var3 = new MyProxyGenerator(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;
    }

    private MyProxyGenerator(String var1, Class<?>[] var2, int var3) {
        this.className = var1;
        this.interfaces = var2;
        this.accessFlags = var3;
    }

    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()) {
                    MyProxyGenerator.ProxyMethod var16 = (MyProxyGenerator.ProxyMethod)var15.next();
                    this.fields.add(new MyProxyGenerator.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() > 65535) {
            throw new IllegalArgumentException("method limit exceeded");
        } else if (this.fields.size() > 65535) {
            throw new IllegalArgumentException("field limit exceeded");
        } else {
            this.cp.getClass(dotToSlash(this.className));
            this.cp.getClass("com/cyx/demo/proxy/MyProxy");
            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("com/cyx/demo/proxy/MyProxy"));
                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()) {
                    MyProxyGenerator.FieldInfo var20 = (MyProxyGenerator.FieldInfo)var15.next();
                    var20.write(var14);
                }

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

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

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

    private void addProxyMethod(Method var1, Class<?> var2) {
        String var3 = var1.getName();
        Class[] var4 = var1.getParameterTypes();
        Class var5 = var1.getReturnType();
        Class[] var6 = var1.getExceptionTypes();
        String var7 = var3 + getParameterDescriptors(var4);
        List var8 = (List)this.proxyMethods.get(var7);
        if (var8 != null) {
            Iterator var9 = ((List)var8).iterator();

            while(var9.hasNext()) {
                MyProxyGenerator.ProxyMethod var10 = (MyProxyGenerator.ProxyMethod)var9.next();
                if (var5 == var10.returnType) {
                    ArrayList var11 = new ArrayList();
                    collectCompatibleTypes(var6, var10.exceptionTypes, var11);
                    collectCompatibleTypes(var10.exceptionTypes, var6, var11);
                    var10.exceptionTypes = new Class[var11.size()];
                    var10.exceptionTypes = (Class[])var11.toArray(var10.exceptionTypes);
                    return;
                }
            }
        } else {
            var8 = new ArrayList(3);
            this.proxyMethods.put(var7, var8);
        }

        ((List)var8).add(new MyProxyGenerator.ProxyMethod(var3, var4, var5, var6, var2));
    }

    private static void checkReturnTypes(List<MyProxyGenerator.ProxyMethod> var0) {
        if (var0.size() >= 2) {
            LinkedList var1 = new LinkedList();
            Iterator var2 = var0.iterator();

            boolean var5;
            label49:
            do {
                while(var2.hasNext()) {
                    MyProxyGenerator.ProxyMethod var3 = (MyProxyGenerator.ProxyMethod)var2.next();
                    Class var4 = var3.returnType;
                    if (var4.isPrimitive()) {
                        throw new IllegalArgumentException("methods with same signature " + getFriendlyMethodSignature(var3.methodName, var3.parameterTypes) + " but incompatible return types: " + var4.getName() + " and others");
                    }

                    var5 = false;
                    ListIterator var6 = var1.listIterator();

                    while(var6.hasNext()) {
                        Class var7 = (Class)var6.next();
                        if (var4.isAssignableFrom(var7)) {
                            continue label49;
                        }

                        if (var7.isAssignableFrom(var4)) {
                            if (!var5) {
                                var6.set(var4);
                                var5 = true;
                            } else {
                                var6.remove();
                            }
                        }
                    }

                    if (!var5) {
                        var1.add(var4);
                    }
                }

                if (var1.size() > 1) {
                    MyProxyGenerator.ProxyMethod var8 = (MyProxyGenerator.ProxyMethod)var0.get(0);
                    throw new IllegalArgumentException("methods with same signature " + getFriendlyMethodSignature(var8.methodName, var8.parameterTypes) + " but incompatible return types: " + var1);
                }

                return;
            } while(!var5);

            throw new AssertionError();
        }
    }

    private MyProxyGenerator.MethodInfo generateConstructor() throws IOException {
        MyProxyGenerator.MethodInfo var1 = new MyProxyGenerator.MethodInfo("<init>", "(Lcom/cyx/demo/proxy/MyInvocationHandler;)V", 1);
        DataOutputStream var2 = new DataOutputStream(var1.code);
        this.code_aload(0, var2);
        this.code_aload(1, var2);
        var2.writeByte(183);
        var2.writeShort(this.cp.getMethodRef("com/cyx/demo/proxy/MyProxy", "<init>", "(Lcom/cyx/demo/proxy/MyInvocationHandler;)V"));
        var2.writeByte(177);
        var1.maxStack = 10;
        var1.maxLocals = 2;
        var1.declaredExceptions = new short[0];
        return var1;
    }

    private MyProxyGenerator.MethodInfo generateStaticInitializer() throws IOException {
        MyProxyGenerator.MethodInfo var1 = new MyProxyGenerator.MethodInfo("<clinit>", "()V", 8);
        byte var2 = 1;
        byte var4 = 0;
        DataOutputStream var6 = new DataOutputStream(var1.code);
        Iterator var7 = this.proxyMethods.values().iterator();

        while(var7.hasNext()) {
            List var8 = (List)var7.next();
            Iterator var9 = var8.iterator();

            while(var9.hasNext()) {
                MyProxyGenerator.ProxyMethod var10 = (MyProxyGenerator.ProxyMethod)var9.next();
                var10.codeFieldInitialization(var6);
            }
        }

        var6.writeByte(177);
        short var3;
        short var5 = var3 = (short)var1.code.size();
        var1.exceptionTable.add(new MyProxyGenerator.ExceptionTableEntry(var4, var5, var3, this.cp.getClass("java/lang/NoSuchMethodException")));
        this.code_astore(var2, var6);
        var6.writeByte(187);
        var6.writeShort(this.cp.getClass("java/lang/NoSuchMethodError"));
        var6.writeByte(89);
        this.code_aload(var2, var6);
        var6.writeByte(182);
        var6.writeShort(this.cp.getMethodRef("java/lang/Throwable", "getMessage", "()Ljava/lang/String;"));
        var6.writeByte(183);
        var6.writeShort(this.cp.getMethodRef("java/lang/NoSuchMethodError", "<init>", "(Ljava/lang/String;)V"));
        var6.writeByte(191);
        var3 = (short)var1.code.size();
        var1.exceptionTable.add(new MyProxyGenerator.ExceptionTableEntry(var4, var5, var3, this.cp.getClass("java/lang/ClassNotFoundException")));
        this.code_astore(var2, var6);
        var6.writeByte(187);
        var6.writeShort(this.cp.getClass("java/lang/NoClassDefFoundError"));
        var6.writeByte(89);
        this.code_aload(var2, var6);
        var6.writeByte(182);
        var6.writeShort(this.cp.getMethodRef("java/lang/Throwable", "getMessage", "()Ljava/lang/String;"));
        var6.writeByte(183);
        var6.writeShort(this.cp.getMethodRef("java/lang/NoClassDefFoundError", "<init>", "(Ljava/lang/String;)V"));
        var6.writeByte(191);
        if (var1.code.size() > 65535) {
            throw new IllegalArgumentException("code size limit exceeded");
        } else {
            var1.maxStack = 10;
            var1.maxLocals = (short)(var2 + 1);
            var1.declaredExceptions = new short[0];
            return var1;
        }
    }

    private void code_iload(int var1, DataOutputStream var2) throws IOException {
        this.codeLocalLoadStore(var1, 21, 26, var2);
    }

    private void code_lload(int var1, DataOutputStream var2) throws IOException {
        this.codeLocalLoadStore(var1, 22, 30, var2);
    }

    private void code_fload(int var1, DataOutputStream var2) throws IOException {
        this.codeLocalLoadStore(var1, 23, 34, var2);
    }

    private void code_dload(int var1, DataOutputStream var2) throws IOException {
        this.codeLocalLoadStore(var1, 24, 38, var2);
    }

    private void code_aload(int var1, DataOutputStream var2) throws IOException {
        this.codeLocalLoadStore(var1, 25, 42, var2);
    }

    private void code_astore(int var1, DataOutputStream var2) throws IOException {
        this.codeLocalLoadStore(var1, 58, 75, var2);
    }

    private void codeLocalLoadStore(int var1, int var2, int var3, DataOutputStream var4) throws IOException {
        assert var1 >= 0 && var1 <= 65535;

        if (var1 <= 3) {
            var4.writeByte(var3 + var1);
        } else if (var1 <= 255) {
            var4.writeByte(var2);
            var4.writeByte(var1 & 255);
        } else {
            var4.writeByte(196);
            var4.writeByte(var2);
            var4.writeShort(var1 & '\uffff');
        }

    }

    private void code_ldc(int var1, DataOutputStream var2) throws IOException {
        assert var1 >= 0 && var1 <= 65535;

        if (var1 <= 255) {
            var2.writeByte(18);
            var2.writeByte(var1 & 255);
        } else {
            var2.writeByte(19);
            var2.writeShort(var1 & '\uffff');
        }

    }

    private void code_ipush(int var1, DataOutputStream var2) throws IOException {
        if (var1 >= -1 && var1 <= 5) {
            var2.writeByte(3 + var1);
        } else if (var1 >= -128 && var1 <= 127) {
            var2.writeByte(16);
            var2.writeByte(var1 & 255);
        } else {
            if (var1 < -32768 || var1 > 32767) {
                throw new AssertionError();
            }

            var2.writeByte(17);
            var2.writeShort(var1 & '\uffff');
        }

    }

    private void codeClassForName(Class<?> var1, DataOutputStream var2) throws IOException {
        this.code_ldc(this.cp.getString(var1.getName()), var2);
        var2.writeByte(184);
        var2.writeShort(this.cp.getMethodRef("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;"));
    }

    private static String dotToSlash(String var0) {
        return var0.replace('.', '/');
    }

    private static String getMethodDescriptor(Class<?>[] var0, Class<?> var1) {
        return getParameterDescriptors(var0) + (var1 == Void.TYPE ? "V" : getFieldType(var1));
    }

    private static String getParameterDescriptors(Class<?>[] var0) {
        StringBuilder var1 = new StringBuilder("(");

        for(int var2 = 0; var2 < var0.length; ++var2) {
            var1.append(getFieldType(var0[var2]));
        }

        var1.append(')');
        return var1.toString();
    }

    private static String getFieldType(Class<?> var0) {
        if (var0.isPrimitive()) {
            return MyProxyGenerator.PrimitiveTypeInfo.get(var0).baseTypeString;
        } else {
            return var0.isArray() ? var0.getName().replace('.', '/') : "L" + dotToSlash(var0.getName()) + ";";
        }
    }

    private static String getFriendlyMethodSignature(String var0, Class<?>[] var1) {
        StringBuilder var2 = new StringBuilder(var0);
        var2.append('(');

        for(int var3 = 0; var3 < var1.length; ++var3) {
            if (var3 > 0) {
                var2.append(',');
            }

            Class var4 = var1[var3];

            int var5;
            for(var5 = 0; var4.isArray(); ++var5) {
                var4 = var4.getComponentType();
            }

            var2.append(var4.getName());

            while(var5-- > 0) {
                var2.append("[]");
            }
        }

        var2.append(')');
        return var2.toString();
    }

    private static int getWordsPerType(Class<?> var0) {
        return var0 != Long.TYPE && var0 != Double.TYPE ? 1 : 2;
    }

    private static void collectCompatibleTypes(Class<?>[] var0, Class<?>[] var1, List<Class<?>> var2) {
        Class[] var3 = var0;
        int var4 = var0.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            Class var6 = var3[var5];
            if (!var2.contains(var6)) {
                Class[] var7 = var1;
                int var8 = var1.length;

                for(int var9 = 0; var9 < var8; ++var9) {
                    Class var10 = var7[var9];
                    if (var10.isAssignableFrom(var6)) {
                        var2.add(var6);
                        break;
                    }
                }
            }
        }

    }

    private static List<Class<?>> computeUniqueCatchList(Class<?>[] var0) {
        ArrayList var1 = new ArrayList();
        var1.add(Error.class);
        var1.add(RuntimeException.class);
        Class[] var2 = var0;
        int var3 = var0.length;

        label36:
        for(int var4 = 0; var4 < var3; ++var4) {
            Class var5 = var2[var4];
            if (var5.isAssignableFrom(Throwable.class)) {
                var1.clear();
                break;
            }

            if (Throwable.class.isAssignableFrom(var5)) {
                int var6 = 0;

                while(var6 < var1.size()) {
                    Class var7 = (Class)var1.get(var6);
                    if (var7.isAssignableFrom(var5)) {
                        continue label36;
                    }

                    if (var5.isAssignableFrom(var7)) {
                        var1.remove(var6);
                    } else {
                        ++var6;
                    }
                }

                var1.add(var5);
            }
        }

        return var1;
    }

    static {
        try {
            hashCodeMethod = Object.class.getMethod("hashCode");
            equalsMethod = Object.class.getMethod("equals", Object.class);
            toStringMethod = Object.class.getMethod("toString");
        } catch (NoSuchMethodException var1) {
            throw new NoSuchMethodError(var1.getMessage());
        }
    }

    private static class ConstantPool {
        private List<MyProxyGenerator.ConstantPool.Entry> pool;
        private Map<Object, Short> map;
        private boolean readOnly;

        private ConstantPool() {
            this.pool = new ArrayList(32);
            this.map = new HashMap(16);
            this.readOnly = false;
        }

        public short getUtf8(String var1) {
            if (var1 == null) {
                throw new NullPointerException();
            } else {
                return this.getValue(var1);
            }
        }

        public short getInteger(int var1) {
            return this.getValue(new Integer(var1));
        }

        public short getFloat(float var1) {
            return this.getValue(new Float(var1));
        }

        public short getClass(String var1) {
            short var2 = this.getUtf8(var1);
            return this.getIndirect(new MyProxyGenerator.ConstantPool.IndirectEntry(7, var2));
        }

        public short getString(String var1) {
            short var2 = this.getUtf8(var1);
            return this.getIndirect(new MyProxyGenerator.ConstantPool.IndirectEntry(8, var2));
        }

        public short getFieldRef(String var1, String var2, String var3) {
            short var4 = this.getClass(var1);
            short var5 = this.getNameAndType(var2, var3);
            return this.getIndirect(new MyProxyGenerator.ConstantPool.IndirectEntry(9, var4, var5));
        }

        public short getMethodRef(String var1, String var2, String var3) {
            short var4 = this.getClass(var1);
            short var5 = this.getNameAndType(var2, var3);
            return this.getIndirect(new MyProxyGenerator.ConstantPool.IndirectEntry(10, var4, var5));
        }

        public short getInterfaceMethodRef(String var1, String var2, String var3) {
            short var4 = this.getClass(var1);
            short var5 = this.getNameAndType(var2, var3);
            return this.getIndirect(new MyProxyGenerator.ConstantPool.IndirectEntry(11, var4, var5));
        }

        public short getNameAndType(String var1, String var2) {
            short var3 = this.getUtf8(var1);
            short var4 = this.getUtf8(var2);
            return this.getIndirect(new MyProxyGenerator.ConstantPool.IndirectEntry(12, var3, var4));
        }

        public void setReadOnly() {
            this.readOnly = true;
        }

        public void write(OutputStream var1) throws IOException {
            DataOutputStream var2 = new DataOutputStream(var1);
            var2.writeShort(this.pool.size() + 1);
            Iterator var3 = this.pool.iterator();

            while(var3.hasNext()) {
                MyProxyGenerator.ConstantPool.Entry var4 = (MyProxyGenerator.ConstantPool.Entry)var3.next();
                var4.write(var2);
            }

        }

        private short addEntry(MyProxyGenerator.ConstantPool.Entry var1) {
            this.pool.add(var1);
            if (this.pool.size() >= 65535) {
                throw new IllegalArgumentException("constant pool size limit exceeded");
            } else {
                return (short)this.pool.size();
            }
        }

        private short getValue(Object var1) {
            Short var2 = (Short)this.map.get(var1);
            if (var2 != null) {
                return var2;
            } else if (this.readOnly) {
                throw new InternalError("late constant pool addition: " + var1);
            } else {
                short var3 = this.addEntry(new MyProxyGenerator.ConstantPool.ValueEntry(var1));
                this.map.put(var1, new Short(var3));
                return var3;
            }
        }

        private short getIndirect(MyProxyGenerator.ConstantPool.IndirectEntry var1) {
            Short var2 = (Short)this.map.get(var1);
            if (var2 != null) {
                return var2;
            } else if (this.readOnly) {
                throw new InternalError("late constant pool addition");
            } else {
                short var3 = this.addEntry(var1);
                this.map.put(var1, new Short(var3));
                return var3;
            }
        }

        private abstract static class Entry {
            private Entry() {
            }

            public abstract void write(DataOutputStream var1) throws IOException;
        }

        private static class IndirectEntry extends MyProxyGenerator.ConstantPool.Entry {
            private int tag;
            private short index0;
            private short index1;

            public IndirectEntry(int var1, short var2) {
                super();
                this.tag = var1;
                this.index0 = var2;
                this.index1 = 0;
            }

            public IndirectEntry(int var1, short var2, short var3) {
                super();
                this.tag = var1;
                this.index0 = var2;
                this.index1 = var3;
            }

            public void write(DataOutputStream var1) throws IOException {
                var1.writeByte(this.tag);
                var1.writeShort(this.index0);
                if (this.tag == 9 || this.tag == 10 || this.tag == 11 || this.tag == 12) {
                    var1.writeShort(this.index1);
                }

            }

            public int hashCode() {
                return this.tag + this.index0 + this.index1;
            }

            public boolean equals(Object var1) {
                if (var1 instanceof MyProxyGenerator.ConstantPool.IndirectEntry) {
                    MyProxyGenerator.ConstantPool.IndirectEntry var2 = (MyProxyGenerator.ConstantPool.IndirectEntry)var1;
                    if (this.tag == var2.tag && this.index0 == var2.index0 && this.index1 == var2.index1) {
                        return true;
                    }
                }

                return false;
            }
        }

        private static class ValueEntry extends MyProxyGenerator.ConstantPool.Entry {
            private Object value;

            public ValueEntry(Object var1) {
                super();
                this.value = var1;
            }

            public void write(DataOutputStream var1) throws IOException {
                if (this.value instanceof String) {
                    var1.writeByte(1);
                    var1.writeUTF((String)this.value);
                } else if (this.value instanceof Integer) {
                    var1.writeByte(3);
                    var1.writeInt((Integer)this.value);
                } else if (this.value instanceof Float) {
                    var1.writeByte(4);
                    var1.writeFloat((Float)this.value);
                } else if (this.value instanceof Long) {
                    var1.writeByte(5);
                    var1.writeLong((Long)this.value);
                } else {
                    if (!(this.value instanceof Double)) {
                        throw new InternalError("bogus value entry: " + this.value);
                    }

                    var1.writeDouble(6.0D);
                    var1.writeDouble((Double)this.value);
                }

            }
        }
    }

    private static class ExceptionTableEntry {
        public short startPc;
        public short endPc;
        public short handlerPc;
        public short catchType;

        public ExceptionTableEntry(short var1, short var2, short var3, short var4) {
            this.startPc = var1;
            this.endPc = var2;
            this.handlerPc = var3;
            this.catchType = var4;
        }
    }

    private class FieldInfo {
        public int accessFlags;
        public String name;
        public String descriptor;

        public FieldInfo(String var2, String var3, int var4) {
            this.name = var2;
            this.descriptor = var3;
            this.accessFlags = var4;
            MyProxyGenerator.this.cp.getUtf8(var2);
            MyProxyGenerator.this.cp.getUtf8(var3);
        }

        public void write(DataOutputStream var1) throws IOException {
            var1.writeShort(this.accessFlags);
            var1.writeShort(MyProxyGenerator.this.cp.getUtf8(this.name));
            var1.writeShort(MyProxyGenerator.this.cp.getUtf8(this.descriptor));
            var1.writeShort(0);
        }
    }

    private class MethodInfo {
        public int accessFlags;
        public String name;
        public String descriptor;
        public short maxStack;
        public short maxLocals;
        public ByteArrayOutputStream code = new ByteArrayOutputStream();
        public List<MyProxyGenerator.ExceptionTableEntry> exceptionTable = new ArrayList();
        public short[] declaredExceptions;

        public MethodInfo(String var2, String var3, int var4) {
            this.name = var2;
            this.descriptor = var3;
            this.accessFlags = var4;
            MyProxyGenerator.this.cp.getUtf8(var2);
            MyProxyGenerator.this.cp.getUtf8(var3);
            MyProxyGenerator.this.cp.getUtf8("Code");
            MyProxyGenerator.this.cp.getUtf8("Exceptions");
        }

        public void write(DataOutputStream var1) throws IOException {
            var1.writeShort(this.accessFlags);
            var1.writeShort(MyProxyGenerator.this.cp.getUtf8(this.name));
            var1.writeShort(MyProxyGenerator.this.cp.getUtf8(this.descriptor));
            var1.writeShort(2);
            var1.writeShort(MyProxyGenerator.this.cp.getUtf8("Code"));
            var1.writeInt(12 + this.code.size() + 8 * this.exceptionTable.size());
            var1.writeShort(this.maxStack);
            var1.writeShort(this.maxLocals);
            var1.writeInt(this.code.size());
            this.code.writeTo(var1);
            var1.writeShort(this.exceptionTable.size());
            Iterator var2 = this.exceptionTable.iterator();

            while(var2.hasNext()) {
                MyProxyGenerator.ExceptionTableEntry var3 = (MyProxyGenerator.ExceptionTableEntry)var2.next();
                var1.writeShort(var3.startPc);
                var1.writeShort(var3.endPc);
                var1.writeShort(var3.handlerPc);
                var1.writeShort(var3.catchType);
            }

            var1.writeShort(0);
            var1.writeShort(MyProxyGenerator.this.cp.getUtf8("Exceptions"));
            var1.writeInt(2 + 2 * this.declaredExceptions.length);
            var1.writeShort(this.declaredExceptions.length);
            short[] var6 = this.declaredExceptions;
            int var7 = var6.length;

            for(int var4 = 0; var4 < var7; ++var4) {
                short var5 = var6[var4];
                var1.writeShort(var5);
            }

        }
    }

    private static class PrimitiveTypeInfo {
        public String baseTypeString;
        public String wrapperClassName;
        public String wrapperValueOfDesc;
        public String unwrapMethodName;
        public String unwrapMethodDesc;
        private static Map<Class<?>, MyProxyGenerator.PrimitiveTypeInfo> table = new HashMap();

        private static void add(Class<?> var0, Class<?> var1) {
            table.put(var0, new MyProxyGenerator.PrimitiveTypeInfo(var0, var1));
        }

        private PrimitiveTypeInfo(Class<?> var1, Class<?> var2) {
            assert var1.isPrimitive();

            this.baseTypeString = Array.newInstance(var1, 0).getClass().getName().substring(1);
            this.wrapperClassName = MyProxyGenerator.dotToSlash(var2.getName());
            this.wrapperValueOfDesc = "(" + this.baseTypeString + ")L" + this.wrapperClassName + ";";
            this.unwrapMethodName = var1.getName() + "Value";
            this.unwrapMethodDesc = "()" + this.baseTypeString;
        }

        public static MyProxyGenerator.PrimitiveTypeInfo get(Class<?> var0) {
            return (MyProxyGenerator.PrimitiveTypeInfo)table.get(var0);
        }

        static {
            add(Byte.TYPE, Byte.class);
            add(Character.TYPE, Character.class);
            add(Double.TYPE, Double.class);
            add(Float.TYPE, Float.class);
            add(Integer.TYPE, Integer.class);
            add(Long.TYPE, Long.class);
            add(Short.TYPE, Short.class);
            add(Boolean.TYPE, Boolean.class);
        }
    }

    private class ProxyMethod {
        public String methodName;
        public Class<?>[] parameterTypes;
        public Class<?> returnType;
        public Class<?>[] exceptionTypes;
        public Class<?> fromClass;
        public String methodFieldName;

        private ProxyMethod(String var2, Class<?>[] var3, Class<?> var4, Class<?>[] var5, Class<?> var6) {
            this.methodName = var2;
            this.parameterTypes = var3;
            this.returnType = var4;
            this.exceptionTypes = var5;
            this.fromClass = var6;
            this.methodFieldName = "m" + MyProxyGenerator.this.proxyMethodCount++;
        }

        private MyProxyGenerator.MethodInfo generateMethod() throws IOException {
            String var1 = MyProxyGenerator.getMethodDescriptor(this.parameterTypes, this.returnType);
            MyProxyGenerator.MethodInfo var2 = MyProxyGenerator.this.new MethodInfo(this.methodName, var1, 17);
            int[] var3 = new int[this.parameterTypes.length];
            int var4 = 1;

            for(int var5 = 0; var5 < var3.length; ++var5) {
                var3[var5] = var4;
                var4 += MyProxyGenerator.getWordsPerType(this.parameterTypes[var5]);
            }

            byte var7 = 0;
            DataOutputStream var9 = new DataOutputStream(var2.code);
            MyProxyGenerator.this.code_aload(0, var9);
            var9.writeByte(180);
            var9.writeShort(MyProxyGenerator.this.cp.getFieldRef("com/cyx/demo/proxy/MyProxy", "h", "Lcom/cyx/demo/proxy/MyInvocationHandler;"));
            MyProxyGenerator.this.code_aload(0, var9);
            var9.writeByte(178);
            var9.writeShort(MyProxyGenerator.this.cp.getFieldRef(MyProxyGenerator.dotToSlash(MyProxyGenerator.this.className), this.methodFieldName, "Ljava/lang/reflect/Method;"));
            if (this.parameterTypes.length > 0) {
                MyProxyGenerator.this.code_ipush(this.parameterTypes.length, var9);
                var9.writeByte(189);
                var9.writeShort(MyProxyGenerator.this.cp.getClass("java/lang/Object"));

                for(int var10 = 0; var10 < this.parameterTypes.length; ++var10) {
                    var9.writeByte(89);
                    MyProxyGenerator.this.code_ipush(var10, var9);
                    this.codeWrapArgument(this.parameterTypes[var10], var3[var10], var9);
                    var9.writeByte(83);
                }
            } else {
                var9.writeByte(1);
            }

            var9.writeByte(185);
            var9.writeShort(MyProxyGenerator.this.cp.getInterfaceMethodRef("com/cyx/demo/proxy/MyInvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"));
            var9.writeByte(4);
            var9.writeByte(0);
            if (this.returnType == Void.TYPE) {
                var9.writeByte(87);
                var9.writeByte(177);
            } else {
                this.codeUnwrapReturnValue(this.returnType, var9);
            }

            short var6;
            short var8 = var6 = (short)var2.code.size();
            List var13 = MyProxyGenerator.computeUniqueCatchList(this.exceptionTypes);
            if (var13.size() > 0) {
                Iterator var11 = var13.iterator();

                while(var11.hasNext()) {
                    Class var12 = (Class)var11.next();
                    var2.exceptionTable.add(new MyProxyGenerator.ExceptionTableEntry(var7, var8, var6, MyProxyGenerator.this.cp.getClass(MyProxyGenerator.dotToSlash(var12.getName()))));
                }

                var9.writeByte(191);
                var6 = (short)var2.code.size();
                var2.exceptionTable.add(new MyProxyGenerator.ExceptionTableEntry(var7, var8, var6, MyProxyGenerator.this.cp.getClass("java/lang/Throwable")));
                MyProxyGenerator.this.code_astore(var4, var9);
                var9.writeByte(187);
                var9.writeShort(MyProxyGenerator.this.cp.getClass("java/lang/reflect/UndeclaredThrowableException"));
                var9.writeByte(89);
                MyProxyGenerator.this.code_aload(var4, var9);
                var9.writeByte(183);
                var9.writeShort(MyProxyGenerator.this.cp.getMethodRef("java/lang/reflect/UndeclaredThrowableException", "<init>", "(Ljava/lang/Throwable;)V"));
                var9.writeByte(191);
            }

            if (var2.code.size() > 65535) {
                throw new IllegalArgumentException("code size limit exceeded");
            } else {
                var2.maxStack = 10;
                var2.maxLocals = (short)(var4 + 1);
                var2.declaredExceptions = new short[this.exceptionTypes.length];

                for(int var14 = 0; var14 < this.exceptionTypes.length; ++var14) {
                    var2.declaredExceptions[var14] = MyProxyGenerator.this.cp.getClass(MyProxyGenerator.dotToSlash(this.exceptionTypes[var14].getName()));
                }

                return var2;
            }
        }

        private void codeWrapArgument(Class<?> var1, int var2, DataOutputStream var3) throws IOException {
            if (var1.isPrimitive()) {
                MyProxyGenerator.PrimitiveTypeInfo var4 = MyProxyGenerator.PrimitiveTypeInfo.get(var1);
                if (var1 != Integer.TYPE && var1 != Boolean.TYPE && var1 != Byte.TYPE && var1 != Character.TYPE && var1 != Short.TYPE) {
                    if (var1 == Long.TYPE) {
                        MyProxyGenerator.this.code_lload(var2, var3);
                    } else if (var1 == Float.TYPE) {
                        MyProxyGenerator.this.code_fload(var2, var3);
                    } else {
                        if (var1 != Double.TYPE) {
                            throw new AssertionError();
                        }

                        MyProxyGenerator.this.code_dload(var2, var3);
                    }
                } else {
                    MyProxyGenerator.this.code_iload(var2, var3);
                }

                var3.writeByte(184);
                var3.writeShort(MyProxyGenerator.this.cp.getMethodRef(var4.wrapperClassName, "valueOf", var4.wrapperValueOfDesc));
            } else {
                MyProxyGenerator.this.code_aload(var2, var3);
            }

        }

        private void codeUnwrapReturnValue(Class<?> var1, DataOutputStream var2) throws IOException {
            if (var1.isPrimitive()) {
                MyProxyGenerator.PrimitiveTypeInfo var3 = MyProxyGenerator.PrimitiveTypeInfo.get(var1);
                var2.writeByte(192);
                var2.writeShort(MyProxyGenerator.this.cp.getClass(var3.wrapperClassName));
                var2.writeByte(182);
                var2.writeShort(MyProxyGenerator.this.cp.getMethodRef(var3.wrapperClassName, var3.unwrapMethodName, var3.unwrapMethodDesc));
                if (var1 != Integer.TYPE && var1 != Boolean.TYPE && var1 != Byte.TYPE && var1 != Character.TYPE && var1 != Short.TYPE) {
                    if (var1 == Long.TYPE) {
                        var2.writeByte(173);
                    } else if (var1 == Float.TYPE) {
                        var2.writeByte(174);
                    } else {
                        if (var1 != Double.TYPE) {
                            throw new AssertionError();
                        }

                        var2.writeByte(175);
                    }
                } else {
                    var2.writeByte(172);
                }
            } else {
                var2.writeByte(192);
                var2.writeShort(MyProxyGenerator.this.cp.getClass(MyProxyGenerator.dotToSlash(var1.getName())));
                var2.writeByte(176);
            }

        }

        private void codeFieldInitialization(DataOutputStream var1) throws IOException {
            MyProxyGenerator.this.codeClassForName(this.fromClass, var1);
            MyProxyGenerator.this.code_ldc(MyProxyGenerator.this.cp.getString(this.methodName), var1);
            MyProxyGenerator.this.code_ipush(this.parameterTypes.length, var1);
            var1.writeByte(189);
            var1.writeShort(MyProxyGenerator.this.cp.getClass("java/lang/Class"));

            for(int var2 = 0; var2 < this.parameterTypes.length; ++var2) {
                var1.writeByte(89);
                MyProxyGenerator.this.code_ipush(var2, var1);
                if (this.parameterTypes[var2].isPrimitive()) {
                    MyProxyGenerator.PrimitiveTypeInfo var3 = MyProxyGenerator.PrimitiveTypeInfo.get(this.parameterTypes[var2]);
                    var1.writeByte(178);
                    var1.writeShort(MyProxyGenerator.this.cp.getFieldRef(var3.wrapperClassName, "TYPE", "Ljava/lang/Class;"));
                } else {
                    MyProxyGenerator.this.codeClassForName(this.parameterTypes[var2], var1);
                }

                var1.writeByte(83);
            }

            var1.writeByte(182);
            var1.writeShort(MyProxyGenerator.this.cp.getMethodRef("java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"));
            var1.writeByte(179);
            var1.writeShort(MyProxyGenerator.this.cp.getFieldRef(MyProxyGenerator.dotToSlash(MyProxyGenerator.this.className), this.methodFieldName, "Ljava/lang/reflect/Method;"));
        }
    }
}
2.4、处理器实现
package com.cyx.demo.proxy;

import com.cyx.demo.service.UserService;
import java.lang.reflect.Method;

/**
 * 打印运行时间的动态代理处理器
 */
public class MyRunTimeInvocationHandler implements MyInvocationHandler {
    /**
     * 被代理类对象 目标代理对象
     */
    private Object target;

    public MyRunTimeInvocationHandler(UserService userService) {
        target = userService;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("生成的代理类:"+proxy.getClass().toString());
        Long start = System.currentTimeMillis();
        Object reuslt = method.invoke(target, args);// java的反射机制执行方法 执行目标对象的方法
        System.out.println("方法执行结束,共用时:"+(System.currentTimeMillis()-start)+"毫秒");
        return reuslt;
    }

    /**
     * 使用jdk动态代理创建代理类
     * @param <T>
     * @return
     */
    public <T> T getProxy() {
        return (T) MyProxy.newProxyInstance(target.getClass().getInterfaces(), this);
    }
}
2.5、测试代码
package com.cyx.demo;

import com.cyx.demo.proxy.MyRunTimeInvocationHandler;
import com.cyx.demo.service.UserService;
import com.cyx.demo.service.impl.UserServiceImpl;

public class Application {
    public static void main(String[] args) {

        UserService proxy = new MyRunTimeInvocationHandler(new UserServiceImpl()).getProxy();
        proxy.findUser();
    }
}
2.6、运行结果
 
三、总结
1、MyProxy记得要实现有参构造函数,传入MyInvocationhandler。因为实现的代理类是继承自MyProxy
2、生成代理类class的工具得改造,因为其内部是写死的JDK中的InvocationHadler和Proxy。
3、JDK动态代理里面,加载生成的代理类字节数组,是通过的Native方法,在此可以改成通过类加载器中的defineClass方法实现。
 

全部评论