Java动态代理

Posted No pain No gain

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java动态代理相关的知识,希望对你有一定的参考价值。

代理模式

为其他对象提供一种代理以控制对这个对象的访问,中介,可去掉功能服务或增加额外的服务

1. 常见代理


远程代理: 为不同地理的对象提供局域网代表对象

虚拟代理: 根据需要将资源消耗很大的对象进行延迟 真正需要的时候进行创建

保护代理: 权限控制

智能引用代理: 提供额外服务

静态代理:代理和被代理对象在代理之前是确定的。他们都实现相同的接口或者相同的抽象类

2. 动态代理实现原理

实现功能: 通过Proxy的newProxyInstance返回代理对象

<1>声明一段源码

<2>编译源码(JDK Complier API),产生新的类(代理类)

<3>将这类load到内存中,产生一个新的对象(代理对象)

<4>return代理对象

<5>调用顺序

根据Proxy.newInstance()传入的参数动态生成.class -->

返回代理类 --> 然后用接口接收返回引用 --> 方法调用

3. 源码分析

Proxy静态方法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()); 
} 
}

类Proxy的getProxyClass方法调用ProxyGenerator的 generateProxyClass方法产生ProxySubject.class的二进制数据:

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

我们可以import sun.misc.ProxyGenerator,调用 generateProxyClass方法产生binary data,然后写入文件,最后通过反编译工具来查看内部实现原理。 反编译后的ProxySubject.java Proxy静态方法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());   
}   
}   
}

ProxyGenerator内部是如何生成class二进制数据,可以参考源代码。

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();



以上是关于Java动态代理的主要内容,如果未能解决你的问题,请参考以下文章

Java设计模式-代理模式之动态代理(附源代码分析)

(java反射-JDK动态代理)+CGLIB动态代理

Java动态代理

Java动态代理

Java动态代理

动态代理