Java JDK dynamic proxy details

  • 2020-04-01 02:15:45
  • OfStack

To proxy a class, the class must implement at least one interface, and only methods in the interface can be proxy.

JDK implementation of dynamic proxy is generally divided into three steps:

1. Write the interface and implement the class.

2. Write a handler that implements the InvocationHandler interface, which has only one Method signed public Object invoke(Object proxy, Method Method, Object[] args)
Throws Throwable. Dynamic interception can be done by adding your own code in the implementation methods of the processor before and after method invocation. Note that the proxy is the generated dynamic proxy class and is not really our proxy class, so we can add a member variable of type Object to the processor, pointing to the class we really want to be proxy (that is, the implementation class in step 1).

3. Generate the dynamic Proxy class using the newProxyInstance method of the java.lang.reflec.proxy class. All calls to the proxy methods are directly to the method of the generated dynamic proxy class, but they are cast to the interface of the method we are calling.

JDK principle analysis:
By analyzing the Proxy source code, we can see the detailed generation of the dynamic Proxy class. The newProxyInstance method first generates a Class instance of the dynamic proxy Class, then calls its constructor with an argument type of InvocationHandler to generate the dynamic proxy Class and return it.

How is the Class instance of the dynamic proxy Class generated? The Class stream of the dynamic proxy Class is generated by the ProxyGenerator Class and loaded into the method area.

Analyzing the process of class byte stream generation, we can see that it USES the Proxy as its parent class to implement all the methods of the interface to be Proxy, and the implementation body of each method is mainly to call the invoke method of the processor.

The main code of the generation process of the class byte stream is 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>        


        }  
    }  

    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;  


//Add attributes
for(Iterator iterator3 = fields.iterator(); iterator3.hasNext(); fieldinfo.write(dataoutputstream))
fieldinfo = (FieldInfo)iterator3.next();
            //Add methods
            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: Code to add the red part proxymethod. GenerateMethod () method to generate for each method, it can be seen by looking at the source code is in the call InvocationHandler interface implementation of the processor's invoke method.


Related articles: