반응형

메이븐 JAR 플러그인 설정

<build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.1.2</version>
        <configuration>
          <archive>
            <index>true</index>
            <manifest>
              <addClasspath>true</addClasspath>
            </manifest>
            <manifestEntries>
              <mode>development</mode>
              <url>${project.url}</url>
              <key>value</key>
              <!-- 자바 에이전트 설정 -->
              <Premain-Class>me.devhistory.MasulsaAgent</Premain-Class>
              <Can-Redefine-Classes>true</Can-Redefine-Classes>
              <Can-Retransform-Classes>true</Can-Retransform-Classes>
              <!-- //자바 에이전트 설정 -->
            </manifestEntries>
          </archive>
        </configuration>
      </plugin>
    </plugins>
  </build>

 

Bytebuddy를 이용한 바이트 코드 조작(자바 에이전트)

public class MasulsaAgent {

    public static void premain(String agentArgs, Instrumentation inst) {
        new AgentBuilder.Default()
                .type(ElementMatchers.any())
                .transform(new AgentBuilder.Transformer() {
                    @Override
                    public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule) {
                        return builder.method(named("pullOut")).intercept(FixedValue.value("Rabitt!"));
                    }
                }).installOn(inst);
    }
}
  • Bytebuddy의 Agentbuilder를 사용하여 구현
  • Agent를 jar로 패키징하여 jar 파일 안에다 자바 에이전트와 관련된 값을 넣어주어 동작 방식을 설정해야한다.
  • 값은 maven jar plugin manifest 를 이용하여 빌드 옵션으로 추가할 수 있다.

 

우리가 구현한 것은 Premain

  • Premain: 애플리케이션을 실행할 때 옵션을 줘서 에이전트 붙이는 방식
  • Agent: 이미 프로세스에서 돌고 있는데 거기다 에이전트를 붙이는 방식

 

메이븐으로 패키징 하면 JAR에 있는 매니패스토 파일에 다음과 같이 자바 에이전트 관련 설정이 된 것을 확인할 수 있다. (Can-Redefine-Classes, Can-Retransform-Classes, Premain-Class)

Manifest-Version: 1.0
Created-By: Maven Archiver 3.4.0
Build-Jdk-Spec: 14
Class-Path: byte-buddy-1.11.0.jar
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Premain-Class: me.devhistory.MasulsaAgent
key: value
mode: development
url:

 

자바 에이전트를 패키징 하여 jar를 만들었다면 사용해보자.

우측 상단의 Edit Configurations 를 클릭하여 창을 띄우고 Modify options를 클릭하여 Add VM options을 누른다. 그리고 VM options으로 "-javaagent:패키징한 jar 경로" 를 입력한다.

  • 예) javaagent:F:\all_workspace\the_java_workspace\MasulsaJavaAgent\target\MasulsaAgent-1.0-SNAPSHOT.jar

 

자바 에이전트는 클래스 로딩 때 동작하게 되며 Masulsa 클래스에서 호출한 pullOut 메소드는 자바 에이전트를 거쳐서 변경된 바이트 코드를 읽어서 보여준다.

  • 자바 에이전트 방식은 파일시스템의 .class 파일을 건드리지 않고 바이트 코드를 조작한다.(.class 파일이 변경되지 않고 메모리가 변경된다.)
  • .class 파일을 파일 시스템에서 직접 조작하는 것과 달리 비침투적(Transparent)이다.(기존 코드를 건드리지 않는다.)

Moja.class

public class Moja {
    public Moja() {
    }

    public String pullOut() {
        return "";
    }
}
public class Masulsa {
    public Masulsa() {
    }

    public static void main(String[] args) {
        System.out.println((new Moja()).pullOut()); //Rabbit!
    }
}

Moja.class의 pullOut 메소드에서 반환되는 문자열은 없다. 하지만 Masulsa에서 호출한 pullOut 메소드는 자바 에이전트에서 바이트 코드를 조작한 결과인 Rabbit!를 출력하고 있다.


[참고자료]

더 자바, 코드를 조작하는 다양한 방법, 백기선

반응형

+ Recent posts