如何为字节码检测设置 Intellij / jar 清单?
How do I set up Intellij / jar manifests for bytecode instrumentation?
开始研究 Java 字节码检测。
像往常一样,我从 Hello World 开始。
简短版:我构建了一个打印 Hello World
的项目,我现在想要检测 class 以便它打印 Hello ASM
, 反而。为此,我实施了一个虚拟代理,我稍后打算对其进行修改,以便它进行必要的更改。我如何修改 build.gradle
文件以便我可以 use/test 它?
[编辑:git clone https://bitbucket.org/RKor/helloasm.git
]
长版:
package helloasm;
import helloasm.instrumentation_targets.HelloWorld;
public class Main {
public static void main(String[] args) {
new HelloWorld().doSomething();
}
}
哪里
package helloasm.instrumentation_targets;
public class HelloWorld {
public String doSomething(){
String output = "Hello World";
System.out.println(output);
return output;
}
}
我将其设为 return 输出字符串,这样我们就可以在 JUnit 中将其与一个简单的测试用例进行实际比较
package helloasm.instrumentation_targets;
import org.junit.Test;
import static org.junit.Assert.*;
public class HelloWorldTest {
@Test
public void doSomething() throws Exception {
assertEquals("Hello ASM",new HelloWorld().doSomething());
}
}
现在很明显,测试会失败,程序会顽固地打印Hello World
。让我们改变它。
Java 字节码检测需要提供 .jar
格式的代理。
当我使用 Intellij 时,我创建了一个新模块 "HelloWorldAgent",并在 its build.gradle
文件中添加了
implementation "org.ow2.asm:asm:5.2"
作为依赖项。
现在我们添加一个代理
package helloasm.agents
import java.lang.instrument.Instrumentation;
public class Agent {
public static void premain(String args, Instrumentation instrumentation){
HelloWorldInterceptor transformer = new HelloWorldInterceptor();
instrumentation.addTransformer(transformer);
}
}
和变压器
package helloasm.agents
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
public class HelloWorldInterceptor implements ClassFileTransformer{
@Override
public byte[] transform(
ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer
) throws IllegalClassFormatException {
System.out.println(className + " loaded");
return classfileBuffer;
}
}
还没有做任何令人兴奋的事情,但在我继续之前我想先把它全部弄到 运行。
我们添加到子模块的build.gradle
:
jar {
archiveName = "${rootProject.name}-${rootProject.version}-agent.jar"
manifest {
attributes(
'Premain-Class': 'ch.ethz.koradir.helloasm.agents.Agent',
'Can-Redefine-Classes': 'true',
'Can-Retransform-Classes': 'true',
'Can-Set-Native-Method-Prefix': 'true',
'Implementation-Title': "HelloWorldInterceptor",
'Implementation-Version': rootProject.version
)
}
}
现在呢?回到根项目的 build.gradle
,我需要以某种方式指定
- 它应该首先将子模块
HelloWorldAgent
编译成一个 jar
- 它应该 运行(或者可以选择 运行)带有
-javaagents
标志,提供那个 jar
- 它应该使用检测代码执行(或可以选择执行)测试
我该怎么做?
- 从
HelloWorldAgent
中删除 settings.gradle
文件
- 通过将
include 'HelloWorldAgent'
添加到根 settings.gradle
文件,使您的构建成为多项目构建
- 将
HelloWorldAgent/build.gradle
中的三个rootProject
改为project
- 将以下内容添加到您的根
build.gradle
evaluationDependsOn 'HelloWorldAgent'
test {
def jarTask = project('HelloWorldAgent').tasks.jar
dependsOn jarTask
afterEvaluate {
jvmArgs "-javaagent:$jarTask.archivePath"
}
}
你的测试仍然会失败,但你会看到转换器的输出。
开始研究 Java 字节码检测。
像往常一样,我从 Hello World 开始。
简短版:我构建了一个打印 Hello World
的项目,我现在想要检测 class 以便它打印 Hello ASM
, 反而。为此,我实施了一个虚拟代理,我稍后打算对其进行修改,以便它进行必要的更改。我如何修改 build.gradle
文件以便我可以 use/test 它?
[编辑:git clone https://bitbucket.org/RKor/helloasm.git
]
长版:
package helloasm;
import helloasm.instrumentation_targets.HelloWorld;
public class Main {
public static void main(String[] args) {
new HelloWorld().doSomething();
}
}
哪里
package helloasm.instrumentation_targets;
public class HelloWorld {
public String doSomething(){
String output = "Hello World";
System.out.println(output);
return output;
}
}
我将其设为 return 输出字符串,这样我们就可以在 JUnit 中将其与一个简单的测试用例进行实际比较
package helloasm.instrumentation_targets;
import org.junit.Test;
import static org.junit.Assert.*;
public class HelloWorldTest {
@Test
public void doSomething() throws Exception {
assertEquals("Hello ASM",new HelloWorld().doSomething());
}
}
现在很明显,测试会失败,程序会顽固地打印Hello World
。让我们改变它。
Java 字节码检测需要提供 .jar
格式的代理。
当我使用 Intellij 时,我创建了一个新模块 "HelloWorldAgent",并在 its build.gradle
文件中添加了
implementation "org.ow2.asm:asm:5.2"
作为依赖项。
现在我们添加一个代理
package helloasm.agents
import java.lang.instrument.Instrumentation;
public class Agent {
public static void premain(String args, Instrumentation instrumentation){
HelloWorldInterceptor transformer = new HelloWorldInterceptor();
instrumentation.addTransformer(transformer);
}
}
和变压器
package helloasm.agents
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
public class HelloWorldInterceptor implements ClassFileTransformer{
@Override
public byte[] transform(
ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer
) throws IllegalClassFormatException {
System.out.println(className + " loaded");
return classfileBuffer;
}
}
还没有做任何令人兴奋的事情,但在我继续之前我想先把它全部弄到 运行。
我们添加到子模块的build.gradle
:
jar {
archiveName = "${rootProject.name}-${rootProject.version}-agent.jar"
manifest {
attributes(
'Premain-Class': 'ch.ethz.koradir.helloasm.agents.Agent',
'Can-Redefine-Classes': 'true',
'Can-Retransform-Classes': 'true',
'Can-Set-Native-Method-Prefix': 'true',
'Implementation-Title': "HelloWorldInterceptor",
'Implementation-Version': rootProject.version
)
}
}
现在呢?回到根项目的 build.gradle
,我需要以某种方式指定
- 它应该首先将子模块
HelloWorldAgent
编译成一个 jar - 它应该 运行(或者可以选择 运行)带有
-javaagents
标志,提供那个 jar - 它应该使用检测代码执行(或可以选择执行)测试
我该怎么做?
- 从
HelloWorldAgent
中删除 - 通过将
include 'HelloWorldAgent'
添加到根settings.gradle
文件,使您的构建成为多项目构建 - 将
HelloWorldAgent/build.gradle
中的三个rootProject
改为project
- 将以下内容添加到您的根
build.gradle
settings.gradle
文件
evaluationDependsOn 'HelloWorldAgent'
test {
def jarTask = project('HelloWorldAgent').tasks.jar
dependsOn jarTask
afterEvaluate {
jvmArgs "-javaagent:$jarTask.archivePath"
}
}
你的测试仍然会失败,但你会看到转换器的输出。