Java dynamic proxy

proxy mode

Provide a proxy for other objects to control access to this object, intermediary, can remove functional services or add additional services

1. Common agents


Remote Agent: Provides LAN representation objects for objects in different geographies

Virtual proxy: Create objects that consume a lot of resources by delaying them as needed and creating them when they are really needed

Protection Agent: Privilege Control

Smart Citation Proxy: Provides additional services

Static proxy: The proxy and the proxied object are determined before proxying. They both implement the same interface or the same abstract class

2. The principle of dynamic proxy implementation

Implementation function: Return the proxy object through Proxy's newProxyInstance

<1> Declare a piece of source code

<2> Compile the source code (JDK Compiler API) to generate a new class (proxy class)

<3> Load this type into memory to generate a new object (proxy object)

<4>return proxy object

<5> Calling sequence

Dynamically generate .class based on the parameters passed in by Proxy.newInstance() -->

Return to the proxy class --> then use the interface to receive the return reference --> method call

3. Source code analysis

Proxy static method newProxyInstance

public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h) throws IllegalArgumentException { 
// 检查 h 不为空,否则抛异常
if (h == null) { 
throw new NullPointerException(); 
} 

// 获得与指定类装载器和一组接口相关的代理类类型对象
Class cl = getProxyClass(loader, interfaces); 

// 通过反射获取构造函数对象并生成代理类实例
try { 
Constructor cons = cl.getConstructor(constructorParams); 
return (Object) cons.newInstance(new Object[] { h }); 
} catch (NoSuchMethodException e) { throw new InternalError(e.toString()); 
} catch (IllegalAccessException e) { throw new InternalError(e.toString()); 
} catch (InstantiationException e) { throw new InternalError(e.toString()); 
} catch (InvocationTargetException e) { throw new InternalError(e.toString()); 
} 
}

The getProxyClass method of class Proxy calls the generateProxyClass method of ProxyGenerator to generate the binary data of ProxySubject.class:

public static byte[] generateProxyClass(final String name, Class[] interfaces)

We can import sun.misc.ProxyGenerator, call the generateProxyClass method to generate binary data, then write to the file, and finally use the decompilation tool to view the internal implementation principle. Decompiled ProxySubject.java Proxy static method newProxyInstance

import java.lang.reflect.*;   
public final class ProxySubject extends Proxy   
implements Subject   
{   
private static Method m1;   
private static Method m0;   
private static Method m3;   
private static Method m2;   
public ProxySubject(InvocationHandler invocationhandler)   
{   
super(invocationhandler);   
}   
public final boolean equals(Object obj)   
{   
try  
{   
return ((Boolean)super.h.invoke(this, m1, new Object[] {   
obj   
})).booleanValue();   
}   
catch(Error _ex) { }   
catch(Throwable throwable)   
{   
throw new UndeclaredThrowableException(throwable);   
}   
}   
public final int hashCode()   
{   
try  
{   
return ((Integer)super.h.invoke(this, m0, null)).intValue();   
}   
catch(Error _ex) { }   
catch(Throwable throwable)   
{   
throw new UndeclaredThrowableException(throwable);   
}   
}   
public final void doSomething()   
{   
try  
{   
super.h.invoke(this, m3, null);   
return;   
}   
catch(Error _ex) { }   
catch(Throwable throwable)   
{   
throw new UndeclaredThrowableException(throwable);   
}   
}   
public final String toString()   
{   
try  
{   
return (String)super.h.invoke(this, m2, null);   
}   
catch(Error _ex) { }   
catch(Throwable throwable)   
{   
throw new UndeclaredThrowableException(throwable);   
}   
}   
static
{   
try  
{   
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {   
Class.forName("java.lang.Object")   
});   
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);   
m3 = Class.forName("Subject").getMethod("doSomething", new Class[0]);   
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);   
}   
catch(NoSuchMethodException nosuchmethodexception)   
{   
throw new NoSuchMethodError(nosuchmethodexception.getMessage());   
}   
catch(ClassNotFoundException classnotfoundexception)   
{   
throw new NoClassDefFoundError(classnotfoundexception.getMessage());   
}   
}   
}

How to generate class binary data inside ProxyGenerator, you can refer to the source code.

private byte[] generateClassFile() {   
  /*  
   * Record that proxy methods are needed for the hashCode, equals,  
   * and toString methods of java.lang.Object.  This is done before  
   * the methods from the proxy interfaces so that the methods from  
   * java.lang.Object take precedence over duplicate methods in the  
   * proxy interfaces.  
   */  
  addProxyMethod(hashCodeMethod, Object.class);   
  addProxyMethod(equalsMethod, Object.class);   
  addProxyMethod(toStringMethod, Object.class);   
  /*  
   * Now record all of the methods from the proxy interfaces, giving  
   * earlier interfaces precedence over later ones with duplicate  
   * methods.  
   */  
  for (int i = 0; i < interfaces.length; i++) {   
  Method[] methods = interfaces[i].getMethods();   
  for (int j = 0; j < methods.length; j++) {   
addProxyMethod(methods[j], interfaces[i]);   
  }   
  }   
  /*  
   * For each set of proxy methods with the same signature,  
   * verify that the methods' return types are compatible.  
   */  
  for (List<ProxyMethod> sigmethods : proxyMethods.values()) {   
  checkReturnTypes(sigmethods);   
  }   
  /* ============================================================  
   * Step 2: Assemble FieldInfo and MethodInfo structs for all of  
   * fields and methods in the class we are generating.  
   */  
  try {   
  methods.add(generateConstructor());   
  for (List<ProxyMethod> sigmethods : proxyMethods.values()) {   
for (ProxyMethod pm : sigmethods) {   
// add static field for method's Method object   
fields.add(new FieldInfo(pm.methodFieldName,   
  "Ljava/lang/reflect/Method;",   
   ACC_PRIVATE | ACC_STATIC));   
// generate code for proxy method and add it   
methods.add(pm.generateMethod());   
}   
  }   
  methods.add(generateStaticInitializer());   
  } catch (IOException e) {   
  throw new InternalError("unexpected I/O Exception");   
  }   
  /* ============================================================  
   * Step 3: Write the final class file.  
   */  
  /*  
   * Make sure that constant pool indexes are reserved for the  
   * following items before starting to write the final class file.  
   */  
  cp.getClass(dotToSlash(className));   
  cp.getClass(superclassName);   
  for (int i = 0; i < interfaces.length; i++) {   
  cp.getClass(dotToSlash(interfaces[i].getName()));   
  }   
  /*  
   * Disallow new constant pool additions beyond this point, since  
   * we are about to write the final constant pool table.  
   */  
  cp.setReadOnly();   
  ByteArrayOutputStream bout = new ByteArrayOutputStream();   
  DataOutputStream dout = new DataOutputStream(bout);   
  try {   
  /*  
   * Write all the items of the "ClassFile" structure.  
   * See JVMS section 4.1.  
   */  
  // u4 magic;   
  dout.writeInt(0xCAFEBABE);   
  // u2 minor_version;   
  dout.writeShort(CLASSFILE_MINOR_VERSION);   
  // u2 major_version;   
  dout.writeShort(CLASSFILE_MAJOR_VERSION);   
  cp.write(dout);   // (write constant pool)   
  // u2 access_flags;   
  dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);   
  // u2 this_class;   
  dout.writeShort(cp.getClass(dotToSlash(className)));   
  // u2 super_class;   
  dout.writeShort(cp.getClass(superclassName));   
  // u2 interfaces_count;   
  dout.writeShort(interfaces.length);   
  // u2 interfaces[interfaces_count];   
  for (int i = 0; i < interfaces.length; i++) {   
dout.writeShort(cp.getClass(   
dotToSlash(interfaces[i].getName())));   
  }   
  // u2 fields_count;   
  dout.writeShort(fields.size());   
  // field_info fields[fields_count];   
  for (FieldInfo f : fields) {   
f.write(dout);   
  }   
  // u2 methods_count;   
  dout.writeShort(methods.size());   
  // method_info methods[methods_count];   
  for (MethodInfo m : methods) {   
m.write(dout);   
  }   
 // u2 attributes_count;   
  dout.writeShort(0); // (no ClassFile attributes for proxy classes)   
  } catch (IOException e) {   
  throw new InternalError("unexpected I/O Exception");   
  }   
  return bout.toByteArray();

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325373605&siteId=291194637