java代碼編輯器,java Instrument修改字節碼實現aop功能

 2023-10-18 阅读 32 评论 0

摘要:? java代碼編輯器?Agent工程2個類: public class MyAgent {/*** 該方法在main方法之前運行,與main方法運行在同一個JVM中* 并被同一個System ClassLoader裝載* 被統一的安全策略(security policy)和上下文(context)管理*/public static void premain(String ag

?

java代碼編輯器?Agent工程2個類:

public class MyAgent {/*** 該方法在main方法之前運行,與main方法運行在同一個JVM中* 并被同一個System ClassLoader裝載* 被統一的安全策略(security policy)和上下文(context)管理*/public static void premain(String agentOps, Instrumentation inst) {System.out.println("=========premain方法執行========");System.out.println(agentOps);// 添加Transformerinst.addTransformer(new MyTransformer());}/*** 如果不存在 premain(String agentOps, Instrumentation inst) * 則會執行 premain(String agentOps)*/public static void premain(String agentOps) {System.out.println("=========premain方法執行2========");System.out.println(agentOps);}}
public class MyTransformer implements ClassFileTransformer {final static String prefix = "\nlong startTime = System.currentTimeMillis();\n";final static String postfix = "\nlong endTime = System.currentTimeMillis();\n";// 被處理的方法列表final static Map<String, List<String>> methodMap = new HashMap<String, List<String>>();public MyTransformer() {add("com.hwtest.demo.MyProgram.sayHello");add("com.hwtest.demo.MyProgram.sayHello2");}private void add(String methodString) {String className = methodString.substring(0, methodString.lastIndexOf("."));String methodName = methodString.substring(methodString.lastIndexOf(".") + 1);List<String> list = methodMap.get(className);if (list == null) {list = new ArrayList<String>();methodMap.put(className, list);}list.add(methodName);}@Overridepublic byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {className = className.replace("/", ".");if (methodMap.containsKey(className)) {// 判斷加載的class的包路徑是不是需要監控的類CtClass ctclass = null;try {ctclass = ClassPool.getDefault().get(className);// 使用全稱,用于取得字節碼類<使用javassist>for (String methodName : methodMap.get(className)) {String outputStr = "\nSystem.out.println(\"this method " + methodName+ " cost:\" +(endTime - startTime) +\"ms.\");";CtMethod ctmethod = ctclass.getDeclaredMethod(methodName);// 得到這方法實例String newMethodName = methodName + "$old";// 新定義一個方法叫做比如sayHello$oldctmethod.setName(newMethodName);// 將原來的方法名字修改// 創建新的方法,復制原來的方法,名字為原來的名字CtMethod newMethod = CtNewMethod.copy(ctmethod, methodName, ctclass, null);// 構建新的方法體StringBuilder bodyStr = new StringBuilder();bodyStr.append("{");bodyStr.append(prefix);bodyStr.append(newMethodName + "($$);\n");// 調用原有代碼,類似于method();($$)表示所有的參數
                    bodyStr.append(postfix);bodyStr.append(outputStr);bodyStr.append("}");newMethod.setBody(bodyStr.toString());// 替換新方法ctclass.addMethod(newMethod);// 增加新方法
                }return ctclass.toBytecode();} catch (Exception e) {System.out.println(e.getMessage());e.printStackTrace();}}return null;}
}

原始項目:

public class MyProgram {public static void main(String[] args) {sayHello();sayHello2("hello world222222222");}public static void sayHello() {try {Thread.sleep(2000);System.out.println("hello world!!");} catch (InterruptedException e) {e.printStackTrace();}}public static void sayHello2(String hello) {try {Thread.sleep(1000);System.out.println(hello);} catch (InterruptedException e) {e.printStackTrace();}}
}

agent項目打jar包是配置為:

Manifest-Version: 1.0
Premain-Class: com.hwtest.MyAgent
Can-Redefine-Classes: true
Boot-Class-Path: javassist.jar

cmd執行命令

java -javaagent:MyAgent.jar  -jar MyProgram.jar=========premain方法執行========
null
hello world!!
this method sayHello cost:2000ms.
hello world222222222
this method sayHello2 cost:1000ms.

?

?

附:agent jar中manifest的屬性

  • Premain-Class: 當在VM啟動時,在命令行中指定代理jar時,必須在manifest中設置Premain-Class屬性,值為代理類全類名,并且該代理類必須提供premain方法。否則JVM會異常終止。
  • Agent-Class: 當在VM啟動之后,動態添加代理jar包時,代理jar包中manifest必須設置Agent-Class屬性,值為代理類全類名,并且該代理類必須提供agentmain方法,否則無法啟動該代理。
  • Boot-Class-Path: Bootstrap class loader加載類時的搜索路徑,可選。
  • Can-Redefine-Classes: true/false;標示代理類是否能夠重定義類。可選。
  • Can-Retransform-Classes: true/false;標示代理類是否能夠轉換類定義。可選。
  • Can-Set-Native-Prefix::true/false;標示代理類是否需要本地方法前綴,可選。

當一個代理jar包中的manifest文件中既有Premain-Class又有Agent-Class時,如果以命令行方式在VM啟動前指定代理jar,則使用Premain-Class;反之如果在VM啟動后,動態添加代理jar,則使用Agent-Class

??

地址記錄:

(1)利用ClassFileTransformer實現aop:http://xj84.iteye.com/blog/1221105

(2)Java通過修改類的字節碼實現aop功能:http://www.360doc.com/content/07/0518/11/25392_506401.shtml

(3)java.lang.instrument動態修改替換類代碼:http://zctya.blog.163.com/blog/static/1209178201131944127774/

轉載于:https://www.cnblogs.com/nnavvi/p/7340910.html

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/2/146462.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息