To proxy a class with jdk dynamic proxy, the proxied class must implement at least one interface, and only methods in the interface can be proxied.
The implementation of dynamic proxy in jdk is generally divided into three steps:
1. Write interfaces and implementation classes.
2. Write a processor that implements the InvocationHandler interface. This interface has only one method, and its signature is public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable; You can add your own code in the implementation method of the processor before and after the method call to perform dynamic interception. It should be noted that the dynamic proxy class generated by proxy is not the real class we are proxying, so we can add a member variable of type Object to the processor to point to the class we really want to be proxied (that is, the class in step 1 implementation class).
3. Use the newProxyInstance method of the java.lang.reflect.Proxy class to generate a dynamic proxy class. For all calls to proxy methods, you can directly call the method of the generated dynamic proxy class, but you must first perform a forced type conversion on it and convert it into the interface of the method we want to call.
JDK principle analysis:
By analyzing the source code of Proxy, you can see the detailed generation of dynamic proxy classes. The newProxyInstance method first generates a Class instance of the dynamic proxy class, and then calls its constructor whose parameter type is InvocationHandler to generate the dynamic proxy class and return.
How to generate the Class instance of the dynamic proxy class? The ProxyGenerator class is used to generate the class byte stream of the dynamic proxy class and load it into the method area.
Analyzing the process of class byte stream generation, we can see that it uses Proxy as its parent class to implement all methods of the interface to be proxied. The implementation body of each method mainly calls the invoke method of the processor.
The main code of the class byte stream generation process is as follows:
Copy the code code as follows:
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>
Copy the code code as follows:
}
}
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;
Copy the code code as follows:
//Add attributes
for(Iterator iterator3 = fields.iterator(); iterator3.hasNext(); fieldinfo.write(dataoutputstream))
fieldinfo = (FieldInfo)iterator3.next();
//Add method
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();
}
Note: The red part in the code, proxymethod.generateMethod(), generates the method body for each method. By looking at the source code, we can see that they are all calling the invoke method of the implementation processor of the InvocationHandler interface.