jdk动态代理—如何执行到invoke方法

jdk动态代理—如何执行到invoke方法

先写一个动态代理demo。
接口:

public interface Inter {
    void test();
}

目标类,即需要被代理的类:

/**
 * 要被代理的类
 */
public class InterImpl implements Inter {
    @Override
    public void test() {
        System.out.println("======执行了test方法======");
    }
}

写一个类生成代理对象:

/**
 * 这个类生成代理对象
 */
public class ProxyFactory implements InvocationHandler {
    /**
     * 要代理的对象
     */
    private Object target;

    /**
     * 构造器传入要代理的对象
     */
    public ProxyFactory(Object target) {
        this.target = target;
    }

    /**
     * 实现InvocationHandler接口的invoke方法
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(target,args);
    }

    /**
     * 这个方法生成并返回代理对象
     */
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }
}

测试一下:

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

        //要被代理的对象(目标对象)
        Inter target = new InterImpl();

        //传入目标对象,生成代理对象
        Inter instance = (Inter) new ProxyFactory(target).getProxyInstance();

        instance.test();
    }
}

结果显然没有问题:

======执行了test方法======

分析:

在主方法输出instance.getClass()

System.out.println(instance.getClass());

输出:

class com.sun.proxy.$Proxy0

可以看出,instance就是动态生成的代理对象,属于com.sun.proxy.$Proxy0这个类。

$Proxy0这个类是在内存中动态生成的,我们可以通过反编译查看源码。

主函数中添加一句:

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

现在主函数长这个样子:

public static void main(String[] args) {
    //这句要放在生成代理对象之前,目的是为了生成$Proxy0.class文件
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    //要被代理的对象(目标对象)
    Inter target = new InterImpl();
    //传入目标对象,生成代理对象
    Inter instance = (Inter) new ProxyFactory(target).getProxyInstance();
    System.out.println(instance.getClass());
    instance.test();
}

执行之后,在项目下多了com文件夹,在com\sun\proxy下生成了$Proxy0.class文件,用idea打开即可自动反编译。

贴上源码:

public final class $Proxy0 extends Proxy implements Inter {
    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("com.example.Inter").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());
        }
    }
}

我们现在返回ProxynewProxyInstance函数,看看代理对象是如何被创建的。

//参数1:类加载器
//参数2:接口列表
//参数3:InvocationHandler
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);
    }

    /**
     * 第一步:生成指定的代理类
     * getProxyClass0()方法根据我们传入的类加载器和接口,
     * 动态生成了$Proxy0类
     */
    Class<?> cl = getProxyClass0(loader, intfs);

    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
		/**
		 * 第二步:获得$Proxy0的构造方法,传入参数类型
		 */
        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);
    }
}

总结一下:

newProxyInstance创建代理对象大致可分为三个步骤:

  1. Class<?> cl = getProxyClass0(loader, intfs);
    

    debug看出,这句话返回的正是代理对象的类$Proxy0在这里插入图片描述

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

    其中,constructorParams是参数类型,代表构造器需要传入InvocationHandler对象。

    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };
    

    debug一下,这句返回的是$Proxy0的构造器,参数是InvocationHandler

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

    最后,通过反射返回$Proxy0的实例,即代理对象。

    其中h正是我们调用newProxyInstance函数时传入的第三个参数。

至此,newProxyInstance方法执行完毕,返回了一个代理对象。

通过代理对象调用方法:

instance.test();

在代理对象调用方法时,我们已经知道instance$Proxy0的实例,所以instance调用的test方法是$Proxy0中的test方法。

$Proxy0中的test方法如下:

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);
    }
}

其中执行方法的语句为:

super.h.invoke(this, m3, (Object[])null);

$Proxy0类继承了Proxy,实现了我们自定义的接口Inter,其中super.hProxyh,即InvocationHandler

Proxy类中聚合了InvocationHandler

 protected InvocationHandler h;

故这句话实际在调用InvocationHandlerinvoke方法,而这个方法,就是我们之前实现的方法。

在我们自定义的方法里,通过反射,就执行到了目标对象的test方法~

为什么jdk动态代理必须是接口

原因很简单,就是因为我们生成的代理类$Proxy0 已经继承了 Proxy,由于java的单继承多实现,无法继承别的类~

  • 15
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值