如何为字节码检测设置 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
  • 中删除 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"
    }
}

你的测试仍然会失败,但你会看到转换器的输出。