gpt4 book ai didi

java - 转换类没有效果

转载 作者:行者123 更新时间:2023-12-02 07:54:16 29 4
gpt4 key购买 nike

根据本教程,我尝试让 java 代理工作。 https://www.baeldung.com/java-instrumentation#loading-a-java-agent

我确实得到了[Agent]转换类TestApplication我没有错误,但我看不到转换类的任何效果。

最终我想让静态加载和动态加载都工作,但现在我专注于静态方式。


public class Static_Agent {

public static void premain(String agentArgs, Instrumentation inst) {
String[] tokens = agentArgs.split(";");
String className = tokens[0];
String methodName = tokens[1];

System.out.println(">> "+className);
System.out.println(">> "+methodName);
transformClass(className, methodName, inst);
}



public static void transformClass(String className, String methodName, Instrumentation instrumentation) {
Class<?> targetCls = null;
ClassLoader targetClassLoader = null;
// see if we can get the class using forName
try {
targetCls = Class.forName(className);
targetClassLoader = targetCls.getClassLoader();
transform(targetCls, methodName, targetClassLoader, instrumentation);
return;
} catch (Exception ex) {
ex.printStackTrace();
}
// otherwise iterate all loaded classes and find what we want
for(Class<?> clazz: instrumentation.getAllLoadedClasses()) {
if(clazz.getName().equals(className)) {
targetCls = clazz;
targetClassLoader = targetCls.getClassLoader();
transform(targetCls, methodName, targetClassLoader, instrumentation);
return;
}
}
throw new RuntimeException("Failed to find class [" + className + "]");
}


public static void transform(Class<?> clazz, String methodName, ClassLoader classLoader, Instrumentation instrumentation) {
Transformer dt = new Transformer(clazz.getName(), methodName, classLoader);
instrumentation.addTransformer(dt, true);
try {
instrumentation.retransformClasses(clazz);
} catch (Exception ex) {
throw new RuntimeException("Transform failed for class: [" + clazz.getName() + "]", ex);
}
}



}
public class Transformer implements ClassFileTransformer {


/** The internal form class name of the class to transform */
private String targetClassName;
/** The class loader of the class we want to transform */
private ClassLoader targetClassLoader;

private String targetMethodName;

public Transformer(String targetClassName, String targetMethodName, ClassLoader targetClassLoader) {
this.targetClassName = targetClassName;
this.targetClassLoader = targetClassLoader;
this.targetMethodName = targetMethodName;
}

@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
byte[] byteCode = classfileBuffer;

String finalTargetClassName = this.targetClassName.replaceAll("\\.", "/");
if (!className.equals(finalTargetClassName)) {
return byteCode;
}

if (className.equals(finalTargetClassName) && loader.equals(targetClassLoader)) {
System.out.println("[Agent] Transforming class TestApplication");
try {

ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get(targetClassName);
CtMethod m = cc.getDeclaredMethod(targetMethodName);
m.addLocalVariable("startTime", CtClass.longType);
m.insertBefore("startTime = System.currentTimeMillis();");

StringBuilder endBlock = new StringBuilder();

m.addLocalVariable("endTime", CtClass.longType);
m.addLocalVariable("opTime", CtClass.longType);
endBlock.append("endTime = System.currentTimeMillis();");
endBlock.append("opTime = (endTime-startTime)/1000;");

endBlock.append("System.out.println(\"[Application] Withdrawal operation completed in:\" + opTime + \" seconds!\");");

m.insertAfter(endBlock.toString());

byteCode = cc.toBytecode();
cc.detach();
} catch (Exception e) {
System.out.println("Exception"+e);
}
}
return byteCode;
}
}
public class TestApplication {

public static void main(String[] args) {

try {
TestApplication.run();
} catch (Exception e) {
e.printStackTrace();
}
}


public static void run() throws Exception {
System.out.println("--- start ---");

while (true) {
test();
Thread.sleep(4_000);
}


}


static int count = 0;

public static void test() {
System.out.println(count++);
}

}

我启动时使用:

java -javaagent:static_agent.jar="doeke.application.TestApplication;test"-jar application.jar

如果有帮助的话,该项目在这里: https://github.com/clankill3r/java_agent

编辑:

在文件末尾附近的 Transformer.java 中,我现在使用 e.printStackTrace();

我收到以下错误:

[Agent] Transforming class TestApplication javassist.NotFoundException: doeke.application.TestApplication at javassist.ClassPool.get(ClassPool.java:436) at doeke.transformer.Transformer.transform(Transformer.java:48) at java.instrument/java.lang.instrument.ClassFileTransformer.transform(ClassFileTransformer.java:246) at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188) at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:563) at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses0(Native Method) at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:167) at doeke.static_agent.Static_Agent.transform(Static_Agent.java:56) at doeke.static_agent.Static_Agent.transformClass(Static_Agent.java:34) at doeke.static_agent.Static_Agent.premain(Static_Agent.java:22) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:513) at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:525)

--- start ---

0

1

最佳答案

感谢您提出这个问题,让我有机会了解一下 Java Instrumentation。

花了一些时间交叉检查您的示例代码和提供的教程后。问题不在于编程代码,而在于如何启动程序。

如果在Transformer.java中的transform()方法中添加一些记录器,运行后会发现代码路径被破坏:

ClassPool cp = ClassPool.getDefault();

并且,在替换相同方法中的异常捕获代码后:

} catch (Exception e) {

至:

} catch (NotFoundException | CannotCompileException | IOException e) {

它会给您更多提示,如下所示:

Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(Unknown Source)
at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(Unknown Source)
Caused by: java.lang.NoClassDefFoundError: javassist/NotFoundException
at doeke.static_agent.Static_Agent.transform(Static_Agent.java:60)
at doeke.static_agent.Static_Agent.transformClass(Static_Agent.java:40)
at doeke.static_agent.Static_Agent.premain(Static_Agent.java:28)
... 6 more
Caused by: java.lang.ClassNotFoundException: javassist.NotFoundException
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 9 more
FATAL ERROR in native method: processing of -javaagent failed

至此,根本原因就更加明显了。这是因为在启动程序时,那些javassist相关的类(例如ClassPool、CtClass、CtMethod等)在运行时无法引用其对应的库。

所以,解决方案是:

  1. 假设您已将 static_agent.jar 导出到与 application.jar 相同的“build”文件夹中

  2. 所有其他文件夹结构与您提供的 github 中显示的保持相同

  3. 让我们“cd”到命令控制台中的build文件夹

  4. 将原程序启动脚本修改如下

Windows 操作系统:

java -javaagent:static_agent.jar="doeke.application.TestApplication;test" -cp ../libs/javassist-3.12.1.GA.jar;application.jar doeke.application.TestApplication

Unix/Linux 操作系统:

java -javaagent:static_agent.jar="doeke.application.TestApplication;test" -cp ../libs/javassist-3.12.1.GA.jar:application.jar doeke.application.TestApplication

你最终会得到你预期的结果:

[Agent] In premain method.
>> doeke.application.TestApplication
>> test
[Agent] Transforming class
--- start ---
0
[Application] Withdrawal operation completed in:0 seconds!
1
[Application] Withdrawal operation completed in:0 seconds!
<小时/>

编辑

另外,我贴一些关于如何通过javassist在方法中间插入代码的代码。

如果 TestApplication.java 中的 test() 方法更改为:

line 30    public static void test() {
line 31 System.out.println(count++);
line 32
line 33 System.out.println("Last line of test() method");
line 34 }

假设我们要在 count========= 之间添加一行,假设“这是行分隔符”,即结果如下:

1 
-- This is line separator --
Last line of test() method

然后,在Transformer.java的transform(...)方法中,您可以添加如下代码行:

m.insertAt(32,"System.out.println(\"-- This is line separator --\");");

这使得它变成:

@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
byte[] byteCode = classfileBuffer;

String finalTargetClassName = this.targetClassName.replaceAll("\\.", "/");
if (!className.equals(finalTargetClassName)) {
return byteCode;
}

if (className.equals(finalTargetClassName) && loader.equals(targetClassLoader)) {
System.out.println("[Agent] Transforming class TestApplication");
try {
// Step 1 Preparation
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get(targetClassName);
CtMethod m = cc.getDeclaredMethod(targetMethodName);

// Step 2 Declare variables
m.addLocalVariable("startTime", CtClass.longType);
m.addLocalVariable("endTime", CtClass.longType);
m.addLocalVariable("opTime", CtClass.longType);

// Step 3 Insertion of extra logics/implementation
m.insertBefore("startTime = System.currentTimeMillis();");

m.insertAt(32,"System.out.println(\"-- This is line separator --\");");

StringBuilder endBlock = new StringBuilder();

endBlock.append("endTime = System.currentTimeMillis();");
endBlock.append("opTime = (endTime-startTime)/1000;");
endBlock.append("System.out.println(\"[Application] Withdrawal operation completed in:\" + opTime + \" seconds!\");");

m.insertAfter(endBlock.toString());

// Step 4 Detach from ClassPool and clean up stuff
byteCode = cc.toBytecode();
cc.detach();
} catch (NotFoundException | CannotCompileException | IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}

}
return byteCode;
}

最后,在方法中间打印代码会得到如下所示的结果:

[Agent] In premain method.
className=doeke.application.TestApplication
methodName=test
>> doeke.application.TestApplication
>> test
[Agent] Transforming class TestApplication
--- start ---
0
-- This is line separator --
=========
[Application] Withdrawal operation completed in:0 seconds!
1
-- This is line separator --
=========
[Application] Withdrawal operation completed in:0 seconds!
2
-- This is line separator --
=========
[Application] Withdrawal operation completed in:0 seconds!

关于java - 转换类没有效果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58156624/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com