java jdk動態(tài)代理詳解
jdk動態(tài)代理要對一個類進行代理,被代理的類必須實現(xiàn)至少一個接口,并且只有接口中的方法才能被代理。
jdk實現(xiàn)動態(tài)代理一般分為三步:
1. 編寫接口和實現(xiàn)類。
2. 寫一個處理器,該處理器實現(xiàn)InvocationHandler接口,該接口只有一個方法,其簽名為public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;可在該處理器的實現(xiàn)方法中,在方法調(diào)用前和調(diào)用后加入自己的代碼,從而進行動態(tài)攔截。要注意的是proxy為生成的動態(tài)代理類,并不是真的我們被代理的類,所以可在處理器中加入Object類型的成員變量,指向我們真正要求被代理的類(即第1步中的實現(xiàn)類)。
3. 使用java.lang.reflect.Proxy類的newProxyInstance方法生成動態(tài)代理類。對所有要代理方法的調(diào)用,都是直接調(diào)用生成的動態(tài)代理類的方法即可,但是要先對它進行強制類型轉(zhuǎn)換,轉(zhuǎn)換成我們要調(diào)用的方法的接口。
JDK原理分析:
通過分析Proxy的源代碼,可看到動態(tài)代理類的詳細生成。newProxyInstance方法首先生成動態(tài)代理類的Class實例,再調(diào)用它的參數(shù)類型為InvocationHandler的構(gòu)造函數(shù)來生成動態(tài)代理類并返回。
動態(tài)代理類的Class實例是怎么生成的呢,是通過ProxyGenerator類來生成動態(tài)代理類的class字節(jié)流,把它載入方法區(qū)。
分析class字節(jié)流生成的過程可以看到它使用Proxy為它的父類,實現(xiàn)所有要代理的接口的方法,每個方法的實現(xiàn)體里主要都是調(diào)用處理器的invoke方法。
class字節(jié)流的生成過程的主要代碼如下:
private byte[] generateClassFile()
{
addProxyMethod(hashCodeMethod, java/lang/Object);
addProxyMethod(equalsMethod, java/lang/Object);
addProxyMethod(toStringMethod, java/lang/Object);
for(int i = 0; i < interfaces.length; i++)
{
Method amethod[] = interfaces[i].getMethods();
for(int k = 0; k < amethod.length; k++)
addProxyMethod(amethod[k], interfaces[i]);
}
List list;
for(Iterator iterator = proxyMethods.values().iterator(); iterator.hasNext(); checkReturnTypes(list))
list = (List)iterator.next();
try
{
methods.add(generateConstructor());
for(Iterator iterator1 = proxyMethods.values().iterator(); iterator1.hasNext();)
{
List list1 = (List)iterator1.next();
Iterator iterator2 = list1.iterator();
while(iterator2.hasNext())
{
ProxyMethod proxymethod = (ProxyMethod)iterator2.next();
fields.add(new FieldInfo(proxymethod.methodFieldName, "Ljava/lang/reflect/Method;", 10));
methods.add(<SPAN style="COLOR: red">proxymethod.generateMethod()</SPAN><SPAN style="COLOR: #000000">);</SPAN>
}
}
methods.add(generateStaticInitializer());
}
catch(IOException ioexception)
{
throw new InternalError("unexpected I/O Exception");
}
if(methods.size() > 65535)
throw new IllegalArgumentException("method limit exceeded");
if(fields.size() > 65535)
throw new IllegalArgumentException("field limit exceeded");
cp.getClass(dotToSlash(className));
cp.getClass("java/lang/reflect/Proxy");
for(int j = 0; j < interfaces.length; j++)
cp.getClass(dotToSlash(interfaces[j].getName()));
cp.setReadOnly();
ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream();
DataOutputStream dataoutputstream = new DataOutputStream(bytearrayoutputstream);
try
{
dataoutputstream.writeInt(-889275714);
dataoutputstream.writeShort(0);
dataoutputstream.writeShort(49);
cp.write(dataoutputstream);
dataoutputstream.writeShort(49);
dataoutputstream.writeShort(cp.getClass(dotToSlash(className)));
dataoutputstream.writeShort(cp.getClass("java/lang/reflect/Proxy"));
dataoutputstream.writeShort(interfaces.length);
for(int l = 0; l < interfaces.length; l++)
dataoutputstream.writeShort(cp.getClass(dotToSlash(interfaces[l].getName())));
dataoutputstream.writeShort(fields.size());
FieldInfo fieldinfo;
//添加屬性
for(Iterator iterator3 = fields.iterator(); iterator3.hasNext(); fieldinfo.write(dataoutputstream))
fieldinfo = (FieldInfo)iterator3.next();
//添加方法
dataoutputstream.writeShort(methods.size());
MethodInfo methodinfo;
for(Iterator iterator4 = methods.iterator(); iterator4.hasNext(); methodinfo.write(dataoutputstream))
methodinfo = (MethodInfo)iterator4.next();
dataoutputstream.writeShort(0);
}
catch(IOException ioexception1)
{
throw new InternalError("unexpected I/O Exception");
}
return bytearrayoutputstream.toByteArray();
}
注:代碼中加紅部分proxymethod.generateMethod()為每個方法生成方法體,通過查看源碼可以看出都是在調(diào)用InvocationHandler接口的實現(xiàn)處理器的invoke方法。
相關(guān)文章
JDBC實現(xiàn)數(shù)據(jù)庫增刪改查功能
這篇文章主要為大家詳細介紹了JDBC實現(xiàn)數(shù)據(jù)庫增刪改查功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-07-07用Spring Native將SpringBoot程序轉(zhuǎn)換為GraalVM
這篇文章主要介紹了用Spring Native將SpringBoot程序轉(zhuǎn)換為GraalVM的方法,幫助大家更好的理解和學習使用SpringBoot,感興趣的朋友可以了解下2021-04-04Java?spring注解@PostConstruct實戰(zhàn)案例講解
我們在Spring項目中經(jīng)常會遇到@PostConstruct注解,可能有的伙伴對這個注解很陌生,下面這篇文章主要給大家介紹了關(guān)于Java?spring注解@PostConstruct實戰(zhàn)案例講解的相關(guān)資料,需要的朋友可以參考下2023-12-12淺談java object對象在heap中的結(jié)構(gòu)
本文主要介紹了淺談java object對象在heap中的結(jié)構(gòu),感興趣的同學,可以參考下。2021-06-06SpringBoot2 整合 ClickHouse數(shù)據(jù)庫案例解析
這篇文章主要介紹了SpringBoot2 整合 ClickHouse數(shù)據(jù)庫案例解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-10-10