수색…


에이전트로 클래스 수정하기

먼저, 사용중인 에이전트가 Manifest.mf에 다음 속성을 가지고 있는지 확인하십시오.

Can-Redefine-Classes: true
Can-Retransform-Classes: true

Java 에이전트를 시작하면 에이전트가 Instrumentation 클래스에 액세스하게됩니다. Instrumentation을 사용하면 addTransformer (ClassFileTransformer transformer)를 호출 할 수 있습니다. ClassFileTransformers를 사용하면 클래스의 바이트를 다시 쓸 수 있습니다. 이 클래스에는 클래스를로드하는 ClassLoader, 클래스의 이름, 클래스의 java.lang.Class 인스턴스, ProtectionDomain 및 마지막으로 클래스 자체의 바이트를 제공하는 단 하나의 메서드 만 있습니다.

다음과 같이 보입니다.

byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, 
          ProtectionDomain protectionDomain, byte[] classfileBuffer)

순전히 바이트에서 클래스를 수정하는 데는 시간이 오래 걸릴 수 있습니다. 이 문제를 해결하기 위해 클래스 바이트를보다 유용한 것으로 변환하는 데 사용할 수있는 라이브러리가 있습니다.

이 예제에서는 ASM을 사용할 것이지만 Javassist와 BCEL 같은 다른 대안은 비슷한 기능을 가지고 있습니다.

ClassNode getNode(byte[] bytes) {
    // Create a ClassReader that will parse the byte array into a ClassNode
    ClassReader cr = new ClassReader(bytes);
    ClassNode cn = new ClassNode();
    try {
        // This populates the ClassNode
        cr.accept(cn, ClassReader.EXPAND_FRAMES);
        cr = null;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return cn;
}

여기에서 ClassNode 객체를 변경할 수 있습니다. 따라서 필드 / 메소드 액세스를 매우 쉽게 변경할 수 있습니다. 또한 메소드의 바이트 코드를 수정하는 ASM의 Tree API가 추가되었습니다.

편집이 끝나면 다음 메소드로 ClassNode를 다시 바이트로 변환 하고 변환 메소드에서 리턴 할 수 있습니다.

public static byte[] getNodeBytes(ClassNode cn, boolean useMaxs) {
    ClassWriter cw = new ClassWriter(useMaxs ? ClassWriter.COMPUTE_MAXS : ClassWriter.COMPUTE_FRAMES);
    cn.accept(cw);
    byte[] b = cw.toByteArray();
    return b;
}

런타임에 에이전트 추가

런타임에 에이전트를 JVM에 추가 할 수 있습니다. 에이전트를로드하려면 Attach API의 VirtualMachine.attatch (String id) 를 사용해야합니다. 다음 방법으로 컴파일 된 에이전트 jar를로드 할 수 있습니다.

public static void loadAgent(String agentPath) {
    String vmName = ManagementFactory.getRuntimeMXBean().getName();
    int index = vmName.indexOf('@');
    String pid = vmName.substring(0, index);
    try {
        File agentFile = new File(agentPath);
        VirtualMachine vm = VirtualMachine.attach(pid);
        vm.loadAgent(agentFile.getAbsolutePath(), "");
        VirtualMachine.attach(vm.id());
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

이렇게하면로드 된 에이전트에서 premain (String agentArgs, Instrumentation inst) 을 호출하지 않고 대신 agentmain (String agentArgs, Instrumentation inst) 을 호출합니다. 에이전트 Manifest.mf에서 에이전트 클래스 를 설정해야합니다.

기본 에이전트 설정

Premain 클래스에는 "premain (String agentArgs Instrumentation inst)" 메서드가 포함됩니다.

다음은 그 예입니다.

import java.lang.instrument.Instrumentation;

public class PremainExample {
    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println(agentArgs);
    }
}

jar 파일로 컴파일되면 Manifest를 열고 Premain-Class 특성을 갖도록하십시오.

다음은 그 예입니다.

Premain-Class: PremainExample

다른 Java 프로그램 "myProgram"과 함께 에이전트를 사용하려면 JVM 인수에 에이전트를 정의해야합니다.

java -javaagent:PremainAgent.jar -jar myProgram.jar


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow