java动态代理的invoke以及源码理解

invoke 方法介绍
想要知道 invoke方法为什么会自动调用我们先要来了解一下这个方法
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)

首先 该方法来自于接口InvocationHandler ,该接口中仅有一个invoke方法 ,该方法中有三个参数
1
2
3
4
5

  • @param proxy the proxy instance that the method was invoked
    1
    这是接口源码注释中对第一个参数proxy的解释:方法被调用的代理实例 我们可以认为它就是一个代理实例

但是代理又是什么呢?这时候可能有小伙伴蒙了。
代理实例其实是代理类本身的一个实例,下面是我自己写的一个动态代理小案例

这是一个接口,动态代理必须有一个接口的存在
1
2
3
4
5
6
public interface PorxyInte {
public void test();
}
1
2
3
这是一个实现了接口的实现类,写的有些简便,主要看起来清晰
1
public class ProImp implements PorxyInte {
@Override
public void test() {
System.out.println(“test”);
}
}
1
2
3
4
5
6
这是测试类 通过了Proxy.newProxyInstance方法(具体作用等下说)返回了一个代理实例

ProImp proImp = new ProImp();//创建了实例类对象
PorxyInte porxyInte =(PorxyInte) Proxy.newProxyInstance(ProImp.class.getClassLoader(), ProImp.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("…"+proxy.getClass().getName());//输出proxy对象的字节码文件名
Method[] declaredMethods = this.getClass().getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod.getName());
}

            method.invoke(proImp,null);
            return null;
        }
    });
    System.out.println(porxyInte.getClass().getName());//输出实现类的字节码文件名
    porxyInte.test();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
com.sun.proxy. P r o x y 0 / / 实 现 类 的 字 节 码 文 件 名 . . . c o m . s u n . p r o x y . Proxy0 // 实现类的字节码文件名 ...com.sun.proxy. Proxy0//...com.sun.proxy.Proxy0// 参数proxy的字节码文件名
1
2
在这边我们发现我自己定义的实现类和参数proxy的字节码文件名是一致的

而根据前面对参数proxy的注释定义,我们不难得出$Proxy0就是一个代理类

那么为什么我调用了.newProxyInstance方法会生成一个代理对象呢?
1
2
3
4
5
newProxyInstance方法介绍
这是package java.lang.reflect.proxy下的一个静态方法

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)
1
我们需要注意的是它的三个参数

首先先介绍一下
第一个参数ClassLoader loader:类的加载器,传入我们自定义类的加载器

第二个参数Class<?>[] interfaces 注意很重要 这个参数是传入一个接口数组

第三个参数 h:类型是InvocationHandler,传入InvocationHandler接口的子类

在newProxyInstance方法中 所做的几件事情

!!重头戏来了

第一步:

Class<?> cl = getProxyClass0(loader, intfs);
1
调用了getProxyClass0方法, 该方法 需要传入两个参数 一个是类加载器,一个是接口数组
在方法getProxyClass0 中 会创建出一个类$Proxy0 ,并且创建出这个内部类的引用返回
我通过生出的内部类文件,反编译出源码可以看下:

public final class $Proxy0 extends Proxy
implements PorxyInte
{

private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;

public $Proxy0(InvocationHandler invocationhandler)
{
	super(invocationhandler);
}

public final boolean equals(Object obj)
{
	try
	{
		return ((Boolean)super.h.invoke(this, m1, new Object[] {
			obj
		})).booleanValue();
	}
	catch (Error ) { }
	catch (Throwable throwable)
	{
		throw new UndeclaredThrowableException(throwable);
	}
}

public final void test()
{
	try
	{
		super.h.invoke(this, m3, null);
		return;
	}
	catch (Error ) { }
	catch (Throwable throwable)
	{
		throw new UndeclaredThrowableException(throwable);
	}
}

public final String toString()
{
	try
	{
		return (String)super.h.invoke(this, m2, null);
	}
	catch (Error ) { }
	catch (Throwable throwable)
	{
		throw new UndeclaredThrowableException(throwable);
	}
}

public final int hashCode()
{
	try
	{
		return ((Integer)super.h.invoke(this, m0, null)).intValue();
	}
	catch (Error ) { }
	catch (Throwable throwable)
	{
		throw new UndeclaredThrowableException(throwable);
	}
}

static 
{
	try
	{
		m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
			Class.forName("java.lang.Object")
		});
		m3 = Class.forName("cn.itcast.web.Test.PorxyInte").getMethod("test", new Class[0]);
		m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
		m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
	}
	catch (NoSuchMethodException nosuchmethodexception)
	{
		throw new NoSuchMethodError(nosuchmethodexception.getMessage());
	}
	catch (ClassNotFoundException classnotfoundexception)
	{
		throw new NoClassDefFoundError(classnotfoundexception.getMessage());
	}
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
注意:我们可以发现这个生成的类继承了proxy 并且实现了我自己定义的那个接口
这是因为在调用getProxyClass0 传入的接口数组,他会将这个数组遍历,并且实现

这是他实现的test方法:
1
2
3
4
5

public final void test()
{
try
{
super.h.invoke(this, m3, null);
return;
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
super不难理解 是proxy类对象
h :但是h是什么我相信小伙伴们可能有点懵,这里留下个悬念,我们先把newProxyInstance方法聊完

刚刚说过newProxyInstance第一步返回了$proxy引用对象

第二步:

1
2
3
4
5
6
//通过反射创建出构造器对象,并且传入constructorParams 类型参数
private static final Class<?>[] constructorParams =
{ InvocationHandler.class };

final Constructor<?> cons = cl.getConstructor(constructorParams);//这是$proxy0的构造方法内部通过super(constructorParams) 创建父类对象

//细心的小伙伴可能发现了,我们刚刚在$proxy中的h,在Proxy中进行了赋值
//其实这个h就是我们在调用newProxyInstance方法是传进来的第三个参数

final InvocationHandler ih = h;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
最后一步: return cons.newInstance(new Object[]{h});
通过反射 返回$proxy的实例

这个就是我们在掉用newProxyInstance方法所做的事情
1
2
3
4
那么先回到我们之前的疑问?invoke方法为什么会自动运行?

我相信现在小伙伴们也能理解
总体流程: 我在测试类中通过返回的$proxy引用调用test方法

porxyInte.test();
1
这时候会去调用$proxy方法中的test方法

public final void test()
{
	try
	{
		super.h.invoke(this, m3, null);
		return;
	}
	catch (Error ) { }
	catch (Throwable throwable)
	{
		throw new UndeclaredThrowableException(throwable);
	}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
在test方法中
super代表父类Proxy,h代表父类中的变量,也就是我们传进来的InvocationHandler接口实例
然后又调用了实例中的invoke方法,这个时候是不是就一目了然,这就是为什么我们调用test方法,而 InvocationHandler中的invoke方法会自动运行的原因,这是因为在代理类中的test方法内容重新定义了

需要注意的点:
第一点:
很多刚接触代理的小萌新包括博主我 在刚开始的时候都一直代理类当做是我们的自定义实现类对象
代理类不是我们定义的类,而是Proxy创建的$proxy类
第二点:
invoke方法中的第一个参数Proxy,这边注意Proxy在invoke方法被赋值为this,this是谁呢?
他就是调用test方法的对象也就是我们的代理实例

return (String)super.h.invoke(this, m2, null);

1
到此为止,为什么动态代理invoke方法会自动运行的原因了
以上所有理解:都是博主自己的理解,如果有错误的地方,还请大家能一一指出,谢谢!!

文章转载于https://blog.csdn.net/qq_39056197/article/details/102598674

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值