나는 작년 lombok
의 원리를 궁금해하다가, inflearn에서 백기선 님의 강의인 '더 자바, "코드를 조작하는 다양한 방법" 이라는 인강을 접하게 되었다.
아래의 내용은 inflearn, 백기선님의 '더 자바, 코드를 조작하는 다양한 방법' 인강의 내용을 바탕으로 했으며, 지극히 개인적으로 공부한 내용을 정리하고자 함😉
(바이트 조작의 예시)
spring
의 component scan
(asm 사용)public class Moja {
public String pullOut() {
return "";
}
}
Moja.java
에서 pullOut()
메서드에서는 빈 스트링을 return 하고 있다.
public class Masulsa {
public static void main(String[] args) {
System.out.println(new Moja().pullOut());
}
}
따라서 Masulsa.java
에서 pullOut()
메서드를 호출하면 당연히 빈 스트링이 출력된다.
Moja
를 조작하여 "rabbit!"을 출력해보자.
(ByteBuddy를 사용해서 class 파일을 조작해보자)
public class Masulsa {
public static void main(String[] args) {
try {
new ByteBuddy().redefine(Moja.class)
.method(named("pullOut")) // 조작할 메서드 명 적어주기
.intercept(FixedValue.value("Rabbit!")) // RETURN 할 값 적어주기
.make()
.saveIn(new File("project\\inflearn-code\\the-java\\target\\classes\\")); // class 파일 경로 넣어주기 (해당 경로에 조작된 코드 저장하겠다.)
} catch (IOException e) {
e.printStackTrace();
}
}
}
해당 코드를 동작시키면 Moja.class
의 코드 (바이트코드)가 아래와 같이 조작된다.
public class Moja {
public Moja() {
}
public String pullOut() {
return "Rabbit!";
}
}
조작하고나서 System.out.println(new Moja().pullOut());
을 호출하면, "Rabbit!"이 출력되게 된다.
javaAgent 와 byteBuddy 사용하여 javaAgent
를 만들어보자
MasulsaAgent.java
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("Rabbit by MasulsaAgent"));
}
}).installOn(inst);
}
}
실행 시
-javaAgent: {MasulsaAgent.jar 파일}을 설정하여 실행 (javaAgent 적용)
🙆♀️ 결과
따라서, System.out.println(new Moja().pullOut());
을 호출한다면, "Rabbit by MasulsaAgent"가 출력되게 된다.
premain
메서드가 포함된 jar로 만들어진 javaagent를 사용할 때에 JVM에서 해당 메서드를 먼저 호출하려고 시도한다.클래스 로드가 클래스를 읽어올 때 javaagent를 거쳐서 변경된 바이트코드를 읽어들여 사용한다.