從JVM中dump出動(dòng)態(tài)代理生成的class

由于動(dòng)態(tài)代理生成的 class 是直接以二進(jìn)制的方式加載進(jìn)內(nèi)存中的,并沒(méi)有對(duì)應(yīng)的.class 文件生成,所以如果想通過(guò)反編譯工具查看動(dòng)態(tài)代理生成的代碼需要通過(guò)特殊的手段來(lái)處理。

方案一

設(shè)置運(yùn)行環(huán)境變量,運(yùn)行后會(huì)把 class 文件生成在 classpath 目錄下

//動(dòng)態(tài)代理時(shí)生成class文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

缺點(diǎn)是只適用于 JDK 動(dòng)態(tài)代理

方案二

使用 ClassDump,可以 dump 出 JVM 中所有已加載的 class。ClassDump 位于$JAVA_HOME/lib/sa-jdi.jar 中(注:windows 版本 JDK 從 1.7 開始才有此工具),直接以命令行執(zhí)行。

#查看PID
E:\work\Test\bin>jps

#缺省輸出該P(yáng)ID下所有已加載的class文件至./目錄
E:\work\Test\bin>java -classpath ".;./bin;%JAVA_HOME%/lib/sa-jdi.jar" sun.jvm.hotspot.tools.jcore.ClassDump <PID>
//導(dǎo)入sa-jdi.jar包,實(shí)現(xiàn)ClassFilter接口,只輸出匹配的class文件
public class MyFilter implements ClassFilter{

    @Override
    public boolean canInclude(InstanceKlass arg0) {
        return arg0.getName().asString().startsWith("com/sun/proxy/$Proxy0");
    }

}
#查看PID
E:\work\Test\bin>jps

#使用ClassFilter輸出匹配的class文件,并指定輸出目錄
E:\work\Test\bin>java -classpath ".;./bin;%JAVA_HOME%/lib/sa-jdi.jar" -Dsun.jvm.hotspot.tools.jcore.filter=proxy.MyFilter -Dsun.jvm.hotspot.tools.jcore.outputDir=e:/dump sun.jvm.hotspot.tools.jcore.ClassDump <PID>

此方案基于 JVM 層的 ClassDump 所以可以支持 javassist、cglib、asm 動(dòng)態(tài)生成的 class。

最后貼下 JDK 動(dòng)態(tài)代理反編譯出來(lái)的代碼

package com.sun.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Run;   //目標(biāo)代理類接口

//繼承了Proxy類,實(shí)現(xiàn)目標(biāo)代理類接口
public final class $Proxy0
  extends Proxy
  implements Run
{
  private static Method m1;
  private static Method m3;
  private static Method m0;
  private static Method m2;

  public $Proxy0(InvocationHandler paramInvocationHandler)
  {
    super(paramInvocationHandler);
  }

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      //獲取目標(biāo)代理類的方法
      m3 = Class.forName("proxy.Run").getMethod("run", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }

  //方法重寫
  public final String run()
  {
    try
    {
      //this.h就是InvocationHandler的實(shí)現(xiàn)類了,調(diào)用invoke方法,在實(shí)現(xiàn)類里面做攔截處理
      return (String)this.h.invoke(this, m3, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final boolean equals(Object paramObject)
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
}

參考

http://rednaxelafx.iteye.com/blog/727938

本文首發(fā)于我的博客:https://monkeywie.cn,不定期分享JAVA、Golang前端、docker、k8s等干貨知識(shí)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容