java动态代理-jdk动态代理原理解析

场景

动态代理是一个比较常见的场景,广泛用于方法增强、AOP(面向切面编程)、测试、监控等场景。市面上常用的动态代理主要是 jdk动态代理和cglib动态代理,javaassist 和 byteBuddy同样可以做动态代理。一般使用jdk动态代理代理方法 适合于代理接口,它不需要引入额外依赖,一般cglib适合于代理没有实现接口的类,性能较好。Javaassist就更加高级了,可以自由生成类字节码,可以自由改变类的行为,Byte Buddy主要是比较灵活,和 javaassist 差不多。我们之前使用Byte Buddy 做了自动生成接口的操作。

jdk动态代理的原理

demo


interface MyInterface {
    void performAction();
}

class MyInterfaceImpl implements MyInterface {
    public void performAction() {
        System.out.println("Action performed");
    }
}

class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method call");
        Object result = method.invoke(target, args);
        System.out.println("After method call");
        return result;
    }
}

public class JdkProxyDemo {
    public static void main(String[] args) {
        MyInterfaceImpl realObject = new MyInterfaceImpl();
        MyInterface proxyInstance = (MyInterface) Proxy.newProxyInstance(
                MyInterfaceImpl.class.getClassLoader(),
                new Class[]{MyInterface.class},
                new MyInvocationHandler(realObject)
        );
        proxyInstance.performAction();
    }
}

jdk动态代理使用起来很简单,一个接口,n实现类 ,一个handler足够了,他的原理比较简单

    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h) {
        Objects.requireNonNull(h);

        @SuppressWarnings("removal")
        final Class<?> caller = System.getSecurityManager() == null
                                    ? null
                                    : Reflection.getCallerClass();

        /*
         * Look up or generate the designated proxy class and its constructor.
         */
        Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);

        return newProxyInstance(caller, cons, h);
    }

首先是检查handler和验证安全性,然后getProxyConstructor获取代理类的构造函数。

 private static Constructor<?> getProxyConstructor(Class<?> caller,
                                                      ClassLoader loader,
                                                      Class<?>... interfaces)
    {
        // optimization for single interface
        if (interfaces.length == 1) {
            Class<?> intf = interfaces[0];
            if (caller != null) {
                checkProxyAccess(caller, loader, intf);
            }
            return proxyCache.sub(intf).computeIfAbsent(
                loader,
                (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
            );
        } else {
            // interfaces cloned
            final Class<?>[] intfsArray = interfaces.clone();
            if (caller != null) {
                checkProxyAccess(caller, loader, intfsArray);
            }
            final List<Class<?>> intfs = Arrays.asList(intfsArray);
            return proxyCache.sub(intfs).computeIfAbsent(
                loader,
                (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
            );
        }
    }

这个过程也很简单,首先是 单个接口
当只有一个接口时,代码直接使用这个接口(Class<?> intf = interfaces[0];)。
在这种情况下,代理类只需要实现这一个接口,所以处理起来更直接、更简单。
代理缓存(proxyCache)的子映射使用这个单一的接口作为键。首先验证完该类是否可以被代理之后,先去缓存拿取,拿不到则构建

    (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()

代理构造器

        ProxyBuilder(ClassLoader loader, List<Class<?>> interfaces) {
            if (!VM.isModuleSystemInited()) {
                throw new InternalError("Proxy is not supported until "
                        + "module system is fully initialized");
            }
            if (interfaces.size() > 65535) {
                throw new IllegalArgumentException("interface limit exceeded: "
                        + interfaces.size());
            }

            Set<Class<?>> refTypes = referencedTypes(loader, interfaces);

            // IAE if violates any restrictions specified in newProxyInstance
            validateProxyInterfaces(loader, interfaces, refTypes);

            this.interfaces = interfaces;
            this.module = mapToModule(loader, interfaces, refTypes);
            assert getLoader(module) == loader;
        }

首先检查模块是否已经完全初始化,这是j9引入的新特性,模块的概念,模块系统的初始化是启动过程的一个关键部分。它负责设置模块路径,确定哪些模块是可访问的,以及它们如何相互作用。
如果模块系统没有完全初始化,许多基于模块的操作,比如动态代理的创建,都不能正常进行。这是因为代理类可能需要跨越不同模块的边界,访问它们的类或接口。

其次检查接口数量是否大于65535,是否超出限制。

再来就是收集引用类型

      /*
         * Returns all types referenced by all public non-static method signatures of
         * the proxy interfaces
         */
        private static Set<Class<?>> referencedTypes(ClassLoader loader,
                                                     List<Class<?>> interfaces) {
            var types = new HashSet<Class<?>>();
            for (var intf : interfaces) {
                for (Method m : intf.getMethods()) {
                    if (!Modifier.isStatic(m.getModifiers())) {
                        addElementType(types, m.getReturnType());
                        addElementTypes(types, m.getSharedParameterTypes());
                        addElementTypes(types, m.getSharedExceptionTypes());
                    }
                }
            }
            return types;
        }

其实就是收集接口中有哪些方法,方法中有哪些参数,返回值是什么,异常信息是什么这些云云。这个方法的目的是为动态代理类的创建准备必要的类型信息

再来就是验证代理接口:

validateProxyInterfaces(loader, interfaces, refTypes);

非常简单

    private static void validateProxyInterfaces(ClassLoader loader,
                                                    List<Class<?>> interfaces,
                                                    Set<Class<?>> refTypes)
        {
            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.size());
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!intf.isInterface()) {
                    throw new IllegalArgumentException(intf.getName() + " is not an interface");
                }

                if (intf.isHidden()) {
                    throw new IllegalArgumentException(intf.getName() + " is a hidden interface");
                }

                if (intf.isSealed()) {
                    throw new IllegalArgumentException(intf.getName() + " is a sealed interface");
                }

                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                ensureVisible(loader, intf);

                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(intf, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException("repeated interface: " + intf.getName());
                }
            }

            for (Class<?> type : refTypes) {
                ensureVisible(loader, type);
            }
        }

验证是不是一个接口,是不是隐藏和密封(15新特性)的接口,在15,引入了隐藏和密封的概念,随后验证接口和之前获取的类型能不能被获取加载。

再来就是完善构建器

            this.interfaces = interfaces;
            this.module = mapToModule(loader, interfaces, refTypes);
            assert getLoader(module) == loader;

再来就是构建 构造方法了

   Constructor<?> build() {
            Class<?> proxyClass = defineProxyClass(module, interfaces);
            assert !module.isNamed() || module.isOpen(proxyClass.getPackageName(), Proxy.class.getModule());

            final Constructor<?> cons;
            try {
                cons = proxyClass.getConstructor(constructorParams);
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString(), e);
            }
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
            return cons;
        }
  private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
            boolean nonExported = false;

            /*
             * 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;  // non-public, final
                    String pkg = intf.getPackageName();
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                                "non-public interfaces from different packages");
                    }
                } else {
                    if (!intf.getModule().isExported(intf.getPackageName())) {
                        // module-private types
                        nonExported = true;
                    }
                }
            }

            if (proxyPkg == null) {
                // all proxy interfaces are public and exported
                if (!m.isNamed())
                    throw new InternalError("ununamed module: " + m);
                proxyPkg = nonExported ? PROXY_PACKAGE_PREFIX + "." + m.getName()
                                       : m.getName();
            } else if (proxyPkg.isEmpty() && m.isNamed()) {
                throw new IllegalArgumentException(
                        "Unnamed package cannot be added to " + m);
            }

            if (m.isNamed()) {
                if (!m.getDescriptor().packages().contains(proxyPkg)) {
                    throw new InternalError(proxyPkg + " not exist in " + m.getName());
                }
            }

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

            ClassLoader loader = getLoader(m);
            trace(proxyName, m, loader, interfaces);

            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags);
            try {
                Class<?> pc = JLA.defineClass(loader, proxyName, proxyClassFile,
                                              null, "__dynamic_proxy__");
                reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
                return pc;
            } 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());
            }
        }

这个方法意思就是创建一个代理类,方法比较长,但是无难度。

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

首先定义包名访问符和接口是否有接口未被模块导出等变量 一个初始化的操作啊。

   for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;  // non-public, final
                    String pkg = intf.getPackageName();
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                                "non-public interfaces from different packages");
                    }
                } else {
                    if (!intf.getModule().isExported(intf.getPackageName())) {
                        // module-private types
                        nonExported = true;
                    }
                }
            }
             if (proxyPkg == null) {
                // all proxy interfaces are public and exported
                if (!m.isNamed())
                    throw new InternalError("ununamed module: " + m);
                proxyPkg = nonExported ? PROXY_PACKAGE_PREFIX + "." + m.getName()
                                       : m.getName();
            } else if (proxyPkg.isEmpty() && m.isNamed()) {
                throw new IllegalArgumentException(
                        "Unnamed package cannot be added to " + m);
            }

            if (m.isNamed()) {
                if (!m.getDescriptor().packages().contains(proxyPkg)) {
                    throw new InternalError(proxyPkg + " not exist in " + m.getName());
                }
            }

他把接口给遍历了啊,注意这里是有单一包名限制的。
遍历传入的接口列表interfaces。对于每个接口intf:
不是公共的,设置为FINAL,获取包名,如果接口的包名与proxyPkg不同,则抛出异常。
else:如果接口是公共的:
if (!intf.getModule().isExported(intf.getPackageName())):如果接口所在的模块没有导出该接口的包,则将nonExported设置为true。
上面的代码主打一个验证,就是杂七杂八的验证。

给代理类起名字

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

获取类加载器并记录跟踪信息,随后生成代理类的字节码并定义类,然后定义代理


            ClassLoader loader = getLoader(m);
            trace(proxyName, m, loader, interfaces);

            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags);
            try {
                Class<?> pc = JLA.defineClass(loader, proxyName, proxyClassFile,
                                              null, "__dynamic_proxy__");
                reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
                return pc;
            } 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(loader, proxyName, interfaces, accessFlags);:使用ProxyGenerator生成代理类的字节码。
static byte[] generateProxyClass(ClassLoader loader,
                                     final String name,
                                     List<Class<?>> interfaces,
                                     int accessFlags) {
        ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags);
        final byte[] classFile = gen.generateClassFile();

        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 = Path.of(dotToSlash(name.substring(0, i)));
                                    Files.createDirectories(dir);
                                    path = dir.resolve(name.substring(i + 1) + ".class");
                                } else {
                                    path = Path.of(name + ".class");
                                }
                                Files.write(path, classFile);
                                return null;
                            } catch (IOException e) {
                                throw new InternalError(
                                        "I/O exception saving generated file: " + e);
                            }
                        }
                    });
        }

        return classFile;
    }

ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags); 创建代理生成器,生成字节码:

final byte[] classFile = gen.generateClassFile();:通过调用ProxyGenerator对象的generateClassFile方法,生成代理类的字节码,并将其存储在字节数组classFile中。

给字节码中注入方法,先注入基本方法 什么 hashCode 什么 equals 什么 toString ,然后再把接口方法注入进去,然后检查返回参数,随后生成构造方法,其实我们的关注点应该在generateMethod 方法上,这才是灵魂。

  private byte[] generateClassFile() {
        visit(V14, accessFlags, dotToSlash(className), null,
                JLR_PROXY, typeNames(interfaces));

        /*
         * Add proxy methods 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);
        addProxyMethod(equalsMethod);
        addProxyMethod(toStringMethod);

        /*
         * Accumulate all of the methods from the proxy interfaces.
         */
        for (Class<?> intf : interfaces) {
            for (Method m : intf.getMethods()) {
                if (!Modifier.isStatic(m.getModifiers())) {
                    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);
        }

        generateConstructor();

        for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
            for (ProxyMethod pm : sigmethods) {
                // add static field for the Method object
                visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, pm.methodFieldName,
                        LJLR_METHOD, null, null);

                // Generate code for proxy method
                pm.generateMethod(this, className);
            }
        }

        generateStaticInitializer();
        generateLookupAccessor();
        return toByteArray();
    }
  private void addProxyMethod(ProxyMethod pm) {
        String sig = pm.shortSignature;
        List<ProxyMethod> sigmethods = proxyMethods.computeIfAbsent(sig,
                (f) -> new ArrayList<>(3));
        sigmethods.add(pm);
    }

灵魂方法: 这个方法它实现了将方法调用转发到InvocationHandler的逻辑。

      private void generateMethod(ClassWriter cw, String className) {
            MethodType mt = MethodType.methodType(returnType, parameterTypes);
            String desc = mt.toMethodDescriptorString();
            int accessFlags = ACC_PUBLIC | ACC_FINAL;
            if (method.isVarArgs()) accessFlags |= ACC_VARARGS;

            MethodVisitor mv = cw.visitMethod(accessFlags,
                    method.getName(), desc, null,
                    typeNames(Arrays.asList(exceptionTypes)));

            int[] parameterSlot = new int[parameterTypes.length];
            int nextSlot = 1;
            for (int i = 0; i < parameterSlot.length; i++) {
                parameterSlot[i] = nextSlot;
                nextSlot += getWordsPerType(parameterTypes[i]);
            }

            mv.visitCode();
            Label L_startBlock = new Label();
            Label L_endBlock = new Label();
            Label L_RuntimeHandler = new Label();
            Label L_ThrowableHandler = new Label();

            List<Class<?>> catchList = computeUniqueCatchList(exceptionTypes);
            if (catchList.size() > 0) {
                for (Class<?> ex : catchList) {
                    mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_RuntimeHandler,
                            dotToSlash(ex.getName()));
                }

                mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_ThrowableHandler,
                        JL_THROWABLE);
            }
            mv.visitLabel(L_startBlock);

            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETFIELD, JLR_PROXY, handlerFieldName,
                    LJLR_INVOCATION_HANDLER);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETSTATIC, dotToSlash(className), methodFieldName,
                    LJLR_METHOD);

            if (parameterTypes.length > 0) {
                // Create an array and fill with the parameters converting primitives to wrappers
                emitIconstInsn(mv, parameterTypes.length);
                mv.visitTypeInsn(Opcodes.ANEWARRAY, JL_OBJECT);
                for (int i = 0; i < parameterTypes.length; i++) {
                    mv.visitInsn(DUP);
                    emitIconstInsn(mv, i);
                    codeWrapArgument(mv, parameterTypes[i], parameterSlot[i]);
                    mv.visitInsn(Opcodes.AASTORE);
                }
            } else {
                mv.visitInsn(Opcodes.ACONST_NULL);
            }

            mv.visitMethodInsn(INVOKEINTERFACE, JLR_INVOCATION_HANDLER,
                    "invoke",
                    "(Ljava/lang/Object;Ljava/lang/reflect/Method;" +
                            "[Ljava/lang/Object;)Ljava/lang/Object;", true);

            if (returnType == void.class) {
                mv.visitInsn(POP);
                mv.visitInsn(RETURN);
            } else {
                codeUnwrapReturnValue(mv, returnType);
            }

            mv.visitLabel(L_endBlock);

            // Generate exception handler
            mv.visitLabel(L_RuntimeHandler);
            mv.visitInsn(ATHROW);   // just rethrow the exception

            mv.visitLabel(L_ThrowableHandler);
            mv.visitVarInsn(ASTORE, 1);
            mv.visitTypeInsn(Opcodes.NEW, JLR_UNDECLARED_THROWABLE_EX);
            mv.visitInsn(DUP);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKESPECIAL, JLR_UNDECLARED_THROWABLE_EX,
                    "<init>", "(Ljava/lang/Throwable;)V", false);
            mv.visitInsn(ATHROW);
            // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
            mv.visitMaxs(-1, -1);
            mv.visitEnd();
        }

核心是 生成代理方法的主体:
加载this和InvocationHandler引用,以及被调用的方法对象。
创建参数对象数组,并将方法参数转换为对象,存储在数组中。
使用InvocationHandler的invoke方法来实现方法的动态代理调用。

加载this和InvocationHandler引用,以及被调用的方法对象:
mv.visitVarInsn(ALOAD, 0);:加载this引用到栈顶。在Java字节码中,ALOAD用于加载一个对象引用到栈顶,而这里的0表示加载位于局部变量表中索引为0的对象,即this引用。

mv.visitFieldInsn(GETFIELD, JLR_PROXY, handlerFieldName, LJLR_INVOCATION_HANDLER);:获取当前代理实例的InvocationHandler字段,将其压入操作数栈。这里GETFIELD是用于访问对象的字段。

mv.visitVarInsn(ALOAD, 0);:再次加载this引用到栈顶。

mv.visitFieldInsn(GETSTATIC, dotToSlash(className), methodFieldName, LJLR_METHOD);:获取静态字段,即当前正在执行的Method对象的引用。

我们回来

     Constructor<?> build() {
            Class<?> proxyClass = defineProxyClass(module, interfaces);
            assert !module.isNamed() || module.isOpen(proxyClass.getPackageName(), Proxy.class.getModule());

            final Constructor<?> cons;
            try {
                cons = proxyClass.getConstructor(constructorParams);
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString(), e);
            }
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
            return cons;
        }

类都生成了 拿到构造简直是小菜一碟了,后面自然是绕过安全检查,设置构造可见。


    private static Object newProxyInstance(Class<?> caller, // null if no SecurityManager
                                           Constructor<?> cons,
                                           InvocationHandler h) {
        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (caller != null) {
                checkNewProxyPermission(caller, cons.getDeclaringClass());
            }

            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException | InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        }

然后就示例化了,最复杂的是生成代理类那一块。

生成后代理类伪代码应该是

import java.lang.reflect.*;

public class DynamicProxyClass implements HelloInterface {
    private InvocationHandler handler;

    public DynamicProxyClass(InvocationHandler handler) {
        this.handler = handler;
    }

    @Override
    public void performAction() {
        try {
            Method method = HelloInterface.class.getMethod("performAction");
            this.handler.invoke(this, method, null);
        } catch (Throwable throwable) {
            // 处理异常
        }
    }
    @Override
    public String toString() {
        try {
            Method method = Object.class.getMethod("toString");
            return (String) handler.invoke(this, method, null);
        } catch (Throwable throwable) {
            // 处理异常
            return null;
        }
    }

    @Override
    public boolean equals(Object obj) {
        try {
            Method method = Object.class.getMethod("equals", Object.class);
            return (Boolean) handler.invoke(this, method, new Object[]{obj});
        } catch (Throwable throwable) {
            // 处理异常
            return false;
        }
    }

    @Override
    public int hashCode() {
        try {
            Method method = Object.class.getMethod("hashCode");
            return (Integer) handler.invoke(this, method, null);
        } catch (Throwable throwable) {
            // 处理异常
            return 0;
        }
    }
}

差不多就这样。

总结

差不多就是 先 接口定义->实现InvocationHandler -> 安全性检查 -> 生成代理类字节码 -> 实例化代理类 不复杂。 比起 javaaasit和bybtBuddy 还是很简单,弟中弟了 属于是。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值