其他分享
首页 > 其他分享> > 老生常谈系列之Aop--JDK动态代理的底层实现原理

老生常谈系列之Aop--JDK动态代理的底层实现原理

作者:互联网

老生常谈系列之Aop--JDK动态代理的底层实现原理

前言

在Aop系列里面有两篇文章,分别是老生常谈系列之Aop--Spring Aop原理浅析老生常谈系列之Aop--Spring Aop源码解析(二)都有涉及JDK动态代理的使用,但是没有详细分析JDK动态代理的实现原理,只是贴出了使用方法。本着知其然更要知其所以然的目标,这一篇文章,我们就来深扒一下JDK动态代理的实现原理。

原理分析

这里的代码分析是基于JDK1.8

Proxy.newProxyInstance()

说到Proxy.newProxyInstance()方法,首先我们来回忆一下老生常谈系列之Aop--Spring Aop源码解析(二)文章中调用JDK动态代理的例子,可以看到如下代码。

    public Object getProxy(){
        return Proxy.newProxyInstance(calculateService.getClass().getClassLoader(),
                                      calculateService.getClass().getInterfaces(),
                                      new MyInvocationHandler
                                      (newCalculateServiceImpl()));
    }

我们是通过Proxy.newProxyInstance()方法来获取一个代理类的,那么Proxy.newProxyInstance()帮我们做了什么呢?

直接跟进源码,这里保留了方法上的英文注释,去除了抛出异常部分的注释,各位看官用心体会。同时这里的代码精简了一部分JDK的权限校验,只留下了核心代码并且添加了注释,逻辑还是比较简单的。

    /**
     * Returns an instance of a proxy class for the specified interfaces
     * that dispatches method invocations to the specified invocation
     * handler.
     *
     * <p>{@code Proxy.newProxyInstance} throws
     * {@code IllegalArgumentException} for the same reasons that
     * {@code Proxy.getProxyClass} does.
     *
     * @param   loader the class loader to define the proxy class
     * @param   interfaces the list of interfaces for the proxy class
     *          to implement
     * @param   h the invocation handler to dispatch method invocations to
     * @return  a proxy instance with the specified invocation handler of a
     *          proxy class that is defined by the specified class loader
     *          and that implements the specified interfaces
     */
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        // 去除一些权限校验

        /*
         * 查找或生成指定的代理类
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * 获取指定的构造函数,这里指定的构造函数为入参为InvocationHandler的构造函数
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            // 去除一些权限校验
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            // 去除一些权限校验
            // 调用构造函数生产代理类实例
            return cons.newInstance(new Object[]{h});
        } catch (... e) {
            // 省略异常处理
        } 
    }

翻译一下:返回将方法调用分派到指定调用处理器的指定接口的代理类的实例。

显而易见,这里通过了一层转发,实现了在调用方法之前先回调到自定义的InvocationHandlerinvoke()方法。这一实现原理,在前面的文章是有说到的。但是JDK动态代理是怎么生成了一个可以回调到自定义InvocationHandler的代理类的呢?它是怎么将具体实现类的方法和生成的代理类的方法进行关联的呢?下面让我们来逐一解释。

getProxyClass0()

很显然,getProxyClass0()是需要重点关注的方法。从这个方法的注释就可以看出来,这个方法是查找或生成指定的代理类,很显然,我们要获取的类对象的结构和内容是在这个方法里完成的。也就是说,这个方法会通过我们传入的ClassLoader loaderClass<?>[] interfaces来构造完成一个Class<?>对象。

跟进代码

    /**
     * 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去获取,如果存在缓存直接返回,否则通过ProxyClassFactory来生成代理类,那我们来看一下proxyClassCache是个什么东东。

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

proxyClassCache是一个WeakCache类型的缓存集合,该对象维护了两个BiFunction<T, U, R>属性

    private final BiFunction<K, P, ?> subKeyFactory;
    private final BiFunction<K, P, V> valueFactory;

对应到这里也就是上面代码的KeyFactoryProxyClassFactory,分别对应一个Key生成工厂和value生成工厂,这两个属性会在new WeakCache的时候赋值。我们可以重点关注ProxyClassFactory里面的apply(ClassLoader loader, Class<?>[] interfaces) 方法,该方法就是实现了生成代理类Class对象的方法。

ProxyClassFactory

跟进ProxyClassFactory代码,JDK的源码很多校验和控制,看起来没那么清晰,但是不要慌,把这些边边角角和异常控制去掉,逻辑还是很清晰的。

    /**
     * A factory function that generates, defines and returns the proxy class given
     * the ClassLoader and array of interfaces.
     */
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names
        // 代理类的名称
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names
        // 生成代理类的序号
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * 验证类加载器是否将此接口的名称解析为相同的 Class 对象。
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * 验证 Class 对象实际上是不是代表一个接口。
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * 验证此接口不是重复的。
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * 记录一个非公共代理接口的包,以便在同一个包中定义代理类。验证所有非公共代理接口是否在同一个包中
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            // 前面的都是校验和生成accessFlags,proxyName等信息

            /*
             * 生成指定的代理类,可以看到委托给了ProxyGenerator.generateProxyClass()实现
             * Generate the specified proxy class.
             */
            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());
            }
        }
    }

上面的代码很长?

无所谓的,看到这一句就好了 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags),这里生产了代理类的byte[]对象,然后再调用defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length)生成一个Class对象。

ProxyGenerator.generateProxyClass()

跟进ProxyGenerator.generateProxyClass()方法。

    /**
     * Generate a proxy class given a name and a list of proxy interfaces.
     *
     * @param name        the class name of the proxy class
     * @param interfaces  proxy interfaces
     * @param accessFlags access flags of the proxy class
    */
    public static byte[] generateProxyClass(final String name,
                                            Class<?>[] interfaces,
                                            int accessFlags)
    {
        ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
        // 生成代理类的byte[]数组
        final byte[] classFile = gen.generateClassFile();

        // 是否保存动态代理生成的代码,sun.misc.ProxyGenerator.saveGeneratedFiles
        if (saveGeneratedFiles) {
            java.security.AccessController.doPrivileged(
            new java.security.PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int i = name.lastIndexOf('.');
                        Path path;
                        if (i > 0) {
                            Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));
                            Files.createDirectories(dir);
                            path = dir.resolve(name.substring(i+1, name.length()) + ".class");
                        } else {
                            path = Paths.get(name + ".class");
                        }
                        Files.write(path, classFile);
                        return null;
                    } catch (IOException e) {
                        throw new InternalError(
                            "I/O exception saving generated file: " + e);
                    }
                }
            });
        }

        return classFile;
    }
    /** debugging flag for saving generated class files */
    private final static boolean saveGeneratedFiles =
        java.security.AccessController.doPrivileged(
            new GetBooleanAction(
                "sun.misc.ProxyGenerator.saveGeneratedFiles")).booleanValue();

到这里这个ProxyGenerator才是真正的生产代理类的文件的byte[]数组

    /**
     * Construct a ProxyGenerator to generate a proxy class with the
     * specified name and for the given interfaces.
     *
     * A ProxyGenerator object contains the state for the ongoing
     * generation of a particular proxy class.
     */
    private ProxyGenerator(String className, Class<?>[] interfaces, int accessFlags) {
        this.className = className;
        this.interfaces = interfaces;
        this.accessFlags = accessFlags;
    }

这个就是生成的具体逻辑了。

其中前面第一第二点的逻辑都较好理解,第三点就是在写class文件了,这个按照JVM的字节码结构去拼接一份class字节码。例如dout.writeInt(0xCAFEBABE)在文件开头写入魔数,dout.writeShort(CLASSFILE_MINOR_VERSION)dout.writeShort(CLASSFILE_MAJOR_VERSION)分别为写小版本号和大版本号。这些顺序都是JVM字节码规定的顺序,需要严格按照此顺序拼接。关于字节码更多的知识,可以看JVM字节码结构。这个方法的代码很长,但是我不打算删减,这里的步骤缺一不可。

    /**
     * Generate a class file for the proxy class.  This method drives the
     * class file generation process.
     */
    private byte[] generateClassFile() {

        /* ============================================================
         * Step 1: Assemble ProxyMethod objects for all methods to
         * generate proxy dispatching code for.
         */

        /*
         * Record that proxy methods are needed for the hashCode, equals,
         * and toString methods of java.lang.Object.  This is done before
         * the methods from the proxy interfaces so that the methods from
         * java.lang.Object take precedence over duplicate methods in the
         * proxy interfaces.
         */
        addProxyMethod(hashCodeMethod, Object.class);
        addProxyMethod(equalsMethod, Object.class);
        addProxyMethod(toStringMethod, Object.class);

        /*
         * Now record all of the methods from the proxy interfaces, giving
         * earlier interfaces precedence over later ones with duplicate
         * methods.
         */
        for (Class<?> intf : interfaces) {
            for (Method m : intf.getMethods()) {
                addProxyMethod(m, intf);
            }
        }

        /*
         * For each set of proxy methods with the same signature,
         * verify that the methods' return types are compatible.
         */
        for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
            checkReturnTypes(sigmethods);
        }

        /* ============================================================
         * Step 2: Assemble FieldInfo and MethodInfo structs for all of
         * fields and methods in the class we are generating.
         */
        try {
            methods.add(generateConstructor());

            for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
                for (ProxyMethod pm : sigmethods) {

                    // add static field for method's Method object
                    fields.add(new FieldInfo(pm.methodFieldName,
                        "Ljava/lang/reflect/Method;",
                         ACC_PRIVATE | ACC_STATIC));

                    // generate code for proxy method and add it
                    methods.add(pm.generateMethod());
                }
            }

            methods.add(generateStaticInitializer());

        } catch (IOException e) {
            throw new InternalError("unexpected I/O Exception", e);
        }

        if (methods.size() > 65535) {
            throw new IllegalArgumentException("method limit exceeded");
        }
        if (fields.size() > 65535) {
            throw new IllegalArgumentException("field limit exceeded");
        }

        /* ============================================================
         * Step 3: Write the final class file.
         */

        /*
         * Make sure that constant pool indexes are reserved for the
         * following items before starting to write the final class file.
         */
        cp.getClass(dotToSlash(className));
        cp.getClass(superclassName);
        for (Class<?> intf: interfaces) {
            cp.getClass(dotToSlash(intf.getName()));
        }

        /*
         * Disallow new constant pool additions beyond this point, since
         * we are about to write the final constant pool table.
         */
        cp.setReadOnly();

        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        DataOutputStream dout = new DataOutputStream(bout);

        try {
            /*
             * Write all the items of the "ClassFile" structure.
             * See JVMS section 4.1.
             */
                                        // u4 magic;
            dout.writeInt(0xCAFEBABE);
                                        // u2 minor_version;
            dout.writeShort(CLASSFILE_MINOR_VERSION);
                                        // u2 major_version;
            dout.writeShort(CLASSFILE_MAJOR_VERSION);

            cp.write(dout);             // (write constant pool)

                                        // u2 access_flags;
            dout.writeShort(accessFlags);
                                        // u2 this_class;
            dout.writeShort(cp.getClass(dotToSlash(className)));
                                        // u2 super_class;
            dout.writeShort(cp.getClass(superclassName));

                                        // u2 interfaces_count;
            dout.writeShort(interfaces.length);
                                        // u2 interfaces[interfaces_count];
            for (Class<?> intf : interfaces) {
                dout.writeShort(cp.getClass(
                    dotToSlash(intf.getName())));
            }

                                        // u2 fields_count;
            dout.writeShort(fields.size());
                                        // field_info fields[fields_count];
            for (FieldInfo f : fields) {
                f.write(dout);
            }

                                        // u2 methods_count;
            dout.writeShort(methods.size());
                                        // method_info methods[methods_count];
            for (MethodInfo m : methods) {
                m.write(dout);
            }

                                         // u2 attributes_count;
            dout.writeShort(0); // (no ClassFile attributes for proxy classes)

        } catch (IOException e) {
            throw new InternalError("unexpected I/O Exception", e);
        }

        return bout.toByteArray();
    }

这个方法最终生成了byte[]数组,也就是说,我们已经拥有了一份类的二进制文件了,可以说到这一步,几乎已经完成了所有工作,只需要把这一份二进制文件加载进JVM,就可以得到一个可运行的Class<?>对象了。

最后这一步由defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length)实现,这是一个native方法,由JVM实现,具体代码在ClassLoader.c

// The existence or signature of this method is not guaranteed since it
// supports a private method.  This method will be changed in 1.7.
JNIEXPORT jclass JNICALL
Java_java_lang_ClassLoader_defineClass0(JNIEnv *env,
                                        jobject loader,
                                        jstring name,
                                        jbyteArray data,
                                        jint offset,
                                        jint length,
                                        jobject pd)
{
    return Java_java_lang_ClassLoader_defineClass1(env, loader, name, data, offset,
                                                   length, pd, NULL);
}

到这里已经获取了Class<?>,让我们回到Proxy.newProxyInstance()方法,生成代理类已经完成了。接下来是调用

Constructor<?> cons = cl.getConstructor(constructorParams);

获取入参为InvocationHandler的构造函数,最后调用构造函数生产代理对象实例。

cons.newInstance(new Object[]{h});

到这,生成动态代理的逻辑已经全部完成。脑子里有没有一定收获,记不记得发生了什么?没有?那不怪你,我也是乱写的。

手动实现一个JDK动态代理

既然原理都已经清晰了,那我们能不能自己实现一个动态代理。为了简单起见,我这里不会直接去写二进制的byte[]数组,因为我不会字节码的结构。这个例子的实现是拼接字符串,然后调用编译器去编译成class文件,再调用defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length)去构造Class<?>对象,然后照葫芦画瓢获取它的构造函数,生成一个代理对象。

还是复用这篇文章老生常谈系列之Aop--Spring Aop原理浅析中JDK动态代理里面的代码。

生成动态代理是这行代码

        return Proxy.newProxyInstance(calculateService.getClass().getClassLoader(),
                                      calculateService.getClass().getInterfaces(),
                                      new MyInvocationHandler(new CalculateServiceImpl()));

实现MyProxy类

那么我们自己实现一个MyProxy类。

首先那就搞个MyProxy类,实现newProxyInstance()方法。这里的实现很简单,分为以下几步:

package io.codegitz.proxy;
/**
 * @author Codegitz
 * @date 2022/1/14 16:41
 **/
public class MyProxy {

    /**
     * 生成代理对象
     * @param loader
     * @param interfaces
     * @param h
     * @return
     * @throws IllegalArgumentException
     */
    public static Object newProxyInstance(MyClassLoader loader,
                                          Class<?> interfaces,
                                          InvocationHandler h)
            throws IllegalArgumentException, IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException {
        // 根据传入接口获取源代码
        String sourceCode = getSourceCode(interfaces);
        String path = MyProxy.class.getResource("").getPath();
        File file = new File(path+"$Proxy0.java");

        FileWriter fw = new FileWriter(file);
        fw.write(sourceCode);
        fw.close();

        // 获取java编译器
        JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
        // 标注java文件管理器,用来获取java字节码文件
        StandardJavaFileManager manager = javaCompiler.getStandardFileManager(null,null,null);
        Iterable iterable = manager.getJavaFileObjects(file);

        // 创建task,通过java字节码文件将类信息加载到JVM中
        JavaCompiler.CompilationTask task = javaCompiler.getTask(null,manager,null,null,null,iterable);
        // 开始执行task
        Boolean call = task.call();
        // 关闭管理器
        manager.close();
        Class proxyClass = loader.findClass("$Proxy0");
        // 返回被代理后的代理对象
        Constructor c = proxyClass.getConstructor(InvocationHandler.class);
        return c.newInstance(h);
    }

    private static String getSourceCode(Class<?> interfaces){
        StringBuilder src = new StringBuilder();
        src.append("package io.codegitz.proxy;").append("\n")
                .append("import java.lang.reflect.Method;").append("\n")
                .append("public class $Proxy0 implements ").append(interfaces.getName()).append("{").append("\n")
                .append("private java.lang.reflect.InvocationHandler h;").append("\n")
                .append("public $Proxy0(java.lang.reflect.InvocationHandler h){").append("\n")
                .append("this.h=h;").append("\n")
                .append("}").append("\n");

        for(Method method:interfaces.getMethods()){
            src.append("public ").append(method.getReturnType()).append(" ").append(method.getName()).append("() {").append("\n")
                    .append("try {").append("\n")
                    .append("Method m = ").append(interfaces.getName()).append(".class.getMethod(\"").append(method.getName()).append("\");").append("\n")
                    .append("this.h.invoke(this, m, new Object[]{});").append("\n")
                    .append("}catch (Throwable e){").append("\n")
                    .append("e.printStackTrace();").append("\n")
                    .append("}").append("\n")
                    .append("}").append("\n");
        }
        src.append("}");

        return src.toString();

    }
}

实现MyClassLoader类

定义MyClassLoader对象,实现findClass()方法,这里主要是为了定制获取自己编译好的class文件,否则直接使用原有的ClassLoader是没问题的。

/**
 * @author Codegitz
 * @date 2022/1/14 17:24
 **/
public class MyClassLoader extends ClassLoader {

    private String baseDir;

    public MyClassLoader() {
        this.baseDir = MyClassLoader.class.getResource("").getPath();
    }

    /**
     * 通过类名称加载类字节码文件到JVM中
     *
     * @param name 类名
     * @return 类的Class独享
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // 获取类名
        String className = MyClassLoader.class.getPackage().getName() + "." + name;
        if (null == baseDir) {
            throw new ClassNotFoundException();
        }

        // 获取类文件
        File file = new File(baseDir, name + ".class");
        if (!file.exists()) {
            throw new ClassNotFoundException();
        }

        // 将类文件转换为字节数组
        try (
                FileInputStream in = new FileInputStream(file);
                ByteArrayOutputStream out = new ByteArrayOutputStream();
            ) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }

            // 调用父类方法生成class实例
            return defineClass(className, out.toByteArray(), 0, out.size());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

测试运行

最后测试方法改成使用自己的方法

    public Object getMyProxy() throws IOException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException, InstantiationException, IllegalAccessException {
        return MyProxy.newProxyInstance(new MyClassLoader(),calculateService.getClass().getInterfaces()[0],new MyInvocationHandler(new CalculateServiceImpl()));
    }

万事俱备只欠东风,搞个方法测试一把,代码如下:

    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
        CalculateService proxy = (CalculateService) new CalculateServiceProxy(new CalculateServiceImpl()).getMyProxy();
        proxy.calculate();
    }

如图,这里已经实现了自定义返回动态代理。

1642153160730运行结果如下,跟我们使用原生的JDK动态代理实现了一样的效果。虽然实现得非常简陋,但是原理就是这么个原理。

1642153335903

总结

好了,到这里文章已经结束。我们首先介绍了JDK动态代理的实现逻辑,随后自己动手实现了一个简陋版的JDK动态代理。

这里再回顾一下思路:

是不是还是很简单的,跟我们平时的操作是一样的,只不过是手动搞了一遍。又水一篇,简简单单。

如果有人看到这里,那在这里老话重提。与君共勉,路漫漫其修远兮,吾将上下而求索。

标签:JDK,--,interfaces,Class,proxy,Aop,new,class,append
来源: https://www.cnblogs.com/codegitz/p/15802906.html