jdk动态代理返回类对象的可行性方案

本文探讨如何修改JDK动态代理的源码,使其像CGlib一样返回被代理类的实例,通过修改ProxyGenerator类实现这一目标,提高了大量实例创建时的效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

熟悉动态代理的小伙伴都知道,java的动态代理有两个常用的方式,一个是jdk动态代理,一个是cglib,他们两个在使用上的最大区别就是前者是返回一个接口对象(其实质是Proxy类的子类),后者是返回了被代理类的类对象。

我今天所探讨的,就是修改jdk动态代理的源码使其像cglib一样返回被代理类的类对象。

首先看下Proxy的关键源码:

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

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * 注意这里,就是这里返回了代理类的class对象
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            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);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

Proxy类中的newProxyInstance通过getProxyClass0获取到代理类的class对象,通过对代码调用的追踪,发现这个class对象是由ProxyGenerator类生成的。

在ProxyGenerator类中,这个地方需要注意:

ProxyGenerator的generateClassFile方法中会将Proxy.newProxyInstance方法传进来的接口的所有方法都弄成代理方法。这个方法下边还有个地方指定了代理类的父类:

其返回的类示例:

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

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

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void test() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

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

其中TestInterface :

由此可见,生成的代理类正如我所说,是继承于Proxy类,并将传进来的所有接口的方法弄成代理方法。


基于此,若想将jdk动态代理返回的对象是被代理类的对象,我们主要修改的地方就是ProxyGenerator类,将继承的父类改成被代理类,将被代理类的所有方法设置为代理方法。

这是主要修改的地方,我现在只是提供一个思路,其它还有很多地方需要修改,我就不一一贴出来,有需要可看源码。


先来看下修改后生成的代理类:

public final class $MyProxy0 extends Tester {
    private static Method m1;
    private static Method m4;
    private static Method m3;
    private static Method m2;
    private static Method m0;

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

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.invocationHandler.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void doa() throws  {
        try {
            super.invocationHandler.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void test() throws  {
        try {
            super.invocationHandler.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.invocationHandler.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.invocationHandler.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

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

其中 Tester :

测试:

public class Test {

    public static void main(String[] as){
        System.setProperty("MyJDKProxy.Proxy.ProxyGenerator.saveGeneratedFiles", "true");
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        Tester baseTester = new Tester(null);
        TestHandler testHandler = new TestHandler(baseTester);

        Tester tester = ProxyUtil.newProxyInstance("invocationHandler", Tester.class, testHandler);
        tester.test();
        System.out.println();
        tester.doa();

        System.out.println();
        System.out.println();

        System.out.println("百:");
        long m = System.currentTimeMillis();
        for(int i=0;i<100;i++){
            TestInterface testInterface = (TestInterface) Proxy.newProxyInstance(Tester.class.getClassLoader(), new Class[]{TestInterface.class}, testHandler);
        }
        System.out.println("修改前:"+(System.currentTimeMillis()-m));

        m=System.currentTimeMillis();
        for(int i=0;i<100;i++){
            Tester tester1 = ProxyUtil.newProxyInstance("invocationHandler",Tester.class, testHandler);
        }
        System.out.println("修改后:"+(System.currentTimeMillis()-m));

        System.out.println();
        System.out.println("万:");
        m = System.currentTimeMillis();
        for(int i=0;i<10000;i++){
            TestInterface testInterface = (TestInterface) Proxy.newProxyInstance(Tester.class.getClassLoader(), new Class[]{TestInterface.class}, testHandler);
        }
        System.out.println("修改前:"+(System.currentTimeMillis()-m));

        m=System.currentTimeMillis();
        for(int i=0;i<10000;i++){
            Tester tester1 = ProxyUtil.newProxyInstance("invocationHandler",Tester.class, testHandler);
        }
        System.out.println("修改后:"+(System.currentTimeMillis()-m));

        System.out.println();
        System.out.println("千万:");
        m = System.currentTimeMillis();
        for(int i=0;i<10000000;i++){
            TestInterface testInterface = (TestInterface) Proxy.newProxyInstance(Tester.class.getClassLoader(), new Class[]{TestInterface.class}, testHandler);
        }
        System.out.println("修改前:"+(System.currentTimeMillis()-m));

        m=System.currentTimeMillis();
        for(int i=0;i<10000000;i++){
            Tester tester1 = ProxyUtil.newProxyInstance("invocationHandler",Tester.class, testHandler);
        }
        System.out.println("修改后:"+(System.currentTimeMillis()-m));

    }
}

测试结果:

可以看出,在数量少的时候两者相差不大,但是到了千万级明显修改后的更快,这主要是因为修改后少了关于传进来的接口的代码,原来的jdk代理还要对接口进行判断,还要一个个从接口中获取方法,现在只需要从被代理类中获取方法就行了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值