javaagent項目中使用


相關代碼參考:http://blog.csdn.net/catoop/article/details/51034778

近期項目中需要對SpringMVC中的Controller方法進行攔截做預處理,才接觸到javaagent,僅作記錄。

思路:

1.聲明MyTransformer類,實現ClassFileTransformer接口,該接口只有一個方法:byte[] transform(ClassLoader loader,String className,Class<?> classBeingRedefined,ProtectionDomain protectionDomain,byte[] classfileBuffer) throws IllegalClassFormatException;在該方法中獲取指定類的指定方法,修改其字節碼,達到攔截的目的;如果需要修改方法字節碼,則需要引入javassist-*.*.*-GA.jar的包。

 1     import java.lang.instrument.ClassFileTransformer;  2     import java.lang.instrument.IllegalClassFormatException;  3     import java.security.ProtectionDomain;  4     import java.util.ArrayList;  5     import java.util.HashMap;  6     import java.util.List;  7     import java.util.Map;  8     import javassist.ClassPool;  9     import javassist.CtClass; 10     import javassist.CtMethod; 11     import javassist.CtNewMethod; 12     
13     public class MyTransformer implements ClassFileTransformer { 14 
15     final static String prefix = "\nlong startTime = System.currentTimeMillis();\n"; 16     final static String postfix = "\nlong endTime = System.currentTimeMillis();\n"; 17 
18     // 被處理的方法列表
19     final static Map<String, List<String>> methodMap = new HashMap<String, List<String>>(); 20 
21     public MyTransformer() { 22         add("com.shanhy.demo.TimeTest.sayHello"); 23         add("com.shanhy.demo.TimeTest.sayHello2"); 24  } 25 
26     private void add(String methodString) { 27         String className = methodString.substring(0, methodString.lastIndexOf(".")); 28         String methodName = methodString.substring(methodString.lastIndexOf(".") + 1); 29         List<String> list = methodMap.get(className); 30         if (list == null) { 31             list = new ArrayList<String>(); 32  methodMap.put(className, list); 33  } 34  list.add(methodName); 35  } 36 
37  @Override 38     public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, 39             ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { 40         className = className.replace("/", "."); 41         if (methodMap.containsKey(className)) {// 判斷加載的class的包路徑是不是需要監控的類
42             CtClass ctclass = null; 43             try { 44                 ctclass = ClassPool.getDefault().get(className);// 使用全稱,用於取得字節碼類<使用javassist>
45                 for (String methodName : methodMap.get(className)) { 46                     String outputStr = "\nSystem.out.println(\"this method " + methodName 47                             + " cost:\" +(endTime - startTime) +\"ms.\");"; 48                     CtMethod ctmethod = ctclass.getDeclaredMethod(methodName);// 得到這方法實例
49                     String newMethodName = methodName + "$old";// 新定義一個方法叫做比如sayHello$old
50                     ctmethod.setName(newMethodName);// 將原來的方法名字修改 51                     // 創建新的方法,復制原來的方法,名字為原來的名字
52                     CtMethod newMethod = CtNewMethod.copy(ctmethod, methodName, ctclass, null); 53                     // 構建新的方法體
54                     StringBuilder bodyStr = new StringBuilder(); 55                     bodyStr.append("{"); 56  bodyStr.append(prefix); 57                     bodyStr.append(newMethodName + "($$);\n");// 調用原有代碼,類似於method();($$)表示所有的參數
58  bodyStr.append(postfix); 59  bodyStr.append(outputStr); 60                     bodyStr.append("}"); 61 
62                     newMethod.setBody(bodyStr.toString());// 替換新方法
63                     ctclass.addMethod(newMethod);// 增加新方法
64  } 65                 return ctclass.toBytecode(); 66             } catch (Exception e) { 67  System.out.println(e.getMessage()); 68  e.printStackTrace(); 69  } 70  } 71         return null; 72  } 73     }
View Code

2.然后聲明MyAgent類,實現方法:

1      public static void premain(String args, Instrumentation inst){ 2         inst.addTransformer(new MyTransformer()); 3     }
View Code

3.將工程打成jar包,例如Myagent.jar,需要修改MANIFEST.MF內容,添加

 1 Premain-Class: com.test.demo.agent.MyAgent 

4.使用時只需要java -javaagent:D:/Myagent.jar -jar MyWebApp.jar就可攔截transform邏輯中想要的方法。-javaagent:D:/*.jar可以使用多個,放到-jar *.jar前面即可。
5.實際項目中,在使用maven工程編譯jarinstall后,執行java -javaagent:D:/Myagent.jar -jar MyWebApp.jar中文字符亂碼,導致啟動異常.

  解決辦法:在啟動命令中添加-Dfile.encoding=utf-8,如下:
       java -Dfile.encoding=utf-8 -javaagent:D:/Myagent.jar -jar MyWebApp.jar程序正常運行。
6.實現transform方式時,遇到一個問題,在手動修改methodbody時,例如newMethod.setBody(bodyStr.toString());bodyStr為處理后的方法體,簡單的語句,如:System.out.println("message")是可以的,但是復雜的邏輯不行,程序運行沒有反映,控制台也沒有異常打印。后來發現需要寫對象的全路徑,比如List需要寫成java.util.List等。其中涉及javassist操作,參見http://blog.csdn.net/u011425751/article/details/51917895
7.遺留問題:
  SpringMVCorg.springframework.web.servlet.DispatcherServlet繼承了抽象類FrameworkServletFrameworkServlet繼承了抽象類HttpServletBeanHttpServletBean繼承抽象類HttpServletHttpServlet繼承了抽象類GenericServletGenericServlet實現了ServletServletConfig接口。

具體如下:  

1     public class DispatcherServlet extends FrameworkServlet 2     public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware 3     public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware 4     public abstract class HttpServlet extends GenericServlet 5     public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable 6     public interface Servlet

項目中攔截DispatcherServletdoService方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM