본문 바로가기

JAVA

byte code 조작

반응형

아무것도 없는 Box에서 “Icecream”을 꺼내기

 

바이트코드 조작 라이브러리

ASM: https://asm.ow2.io/ 어려움..

Javassist: https://www.javassist.org/ 조금 덜 어려움..

ByteBuddy: https://bytebuddy.net/#/

 

 

ByteBuddy maven dependency 추가

<dependency>
  <groupId>net.bytebuddy</groupId>
  <artifactId>byte-buddy</artifactId>
  <version>1.10.1</version>
</dependency>

 

Box.java

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

 

App.java

public class App {
	
	public static void main( String[] args ) {
//		try {
//			new ByteBuddy()
//			.redefine(Box.class).method(ElementMatchers.named("pullOut")).intercept(FixedValue.value("icecream!"))
//			.make().saveIn(new File("/Users/jueunlee/Workspaces/Workspace_Spring/bytebuddy.ex/target/classes/"));
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
		System.out.println(new Box().pullOut());
	}

}

 

결과 화면

Box의 java 소스는 그대로이나, claas 파일의 바이트 코드를 변경시킴. Box 인스턴스 생성 시에는 이미 컴파일된 클래스 파일을 참조하므로, 변경된 바이트 코드가 실행됨. 그 결과 Icecream 🍦이 나온다!!

 

로딩 시에 생길 수 있는 이슈..

로더가 읽어드리는 정보 

FQCN .. packge, full package 경로, class name, + classloader!!

이 경우 box라는 클래스는 하나지만, JVM 안에서는 full package 경로, name 까지 같은 호환이 되지 않은 class가 두 개.. !!

 

많은 걸 할 수 있지 않을까ㅏㅏ?

 

한 번 읽은 Box.java 파일은 다시 클래스 로딩을 하지 않는다.

public class App {
	
	public static void main( String[] args ) {

		ClassLoader classLoader = Box.class.getClassLoader();
		TypePool typePool = TypePool.Default.of(classLoader);
		try {
			new ByteBuddy().redefine(typePool.describe("me.jueun.bytebuddy.ex.Box").resolve(), 
					ClassFileLocator.ForClassLoader.of(classLoader))
				.method(ElementMatchers.named("pullOut")).intercept(FixedValue.value("icecream!"))
				.make().saveIn(new File("/Users/jueunlee/Workspaces/Workspace_Spring/bytebuddy.ex/target/classes/"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println(new Box().pullOut());
	}

}

 

그러나 클래스 실행 순서에 의존적..

 

Javaagent JAR 파일 만들기

Javaagent 붙여서 사용하기

  • 클래스로더가 클래스를 읽어올 때 javaagent를 거쳐서 변경된 바이트코드를 읽어들여 사용한다. 

MasulsaAgent.java

public class MasulsaAgent {
  
  public static void premain( String agentAgrs, Instrumentation inst ) {
    
    new AgentBuilder.Default().type(ElementMatchers.any()).transform(( builder, typeDescription, classLoader, module ) -> builder.method(ElementMatchers.named("pullOut")).intercept(FixedValue.value("Icecream!"))).installOn(inst);
    
  }
}

 

 

pom.xml


<dependencies>
    <dependency>
      <groupId>net.bytebuddy</groupId>
      <artifactId>byte-buddy</artifactId>
      <version>1.10.1</version>
    </dependency>
  </dependencies>

  <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.jueun.javaagent.ex.MasulsaAgent</Premain-Class>
              <Can-Redefine-Classes>true</Can-Redefine-Classes>
              <Can-Retransform-Classes>true</Can-Retransform-Classes>
            </manifestEntries>
          </archive>
        </configuration>
      </plugin>
    </plugins>
  </build>
	

 

JVM option 으로 해당 jar파일을 사용하여 실행하도록...

Javaagent 적용

-javaagent:/Users/keesun/workspace/MasulsaJavaAgent/target//Users/jueunlee/Workspaces/Workspace_Spring/javaagent.ex/target/javaagent.ex-0.0.1-SNAPSHOT.jar

 

 

바이트코드 조작하여 할 수 있는 것!

 

프로그램 분석

  • 코드에서 버그 찾는 툴
  • 코드 복잡도 계산

클래스 파일 생성

  • 프록시 ( spring AOP, hibernate LAZY loading 객체, Mock 대상 객체 )
  • 특정 API 호출 접근 제한
  • 스칼라 같은 언어의 컴파일러

그밖에도 자바 소스 코드 건리지 않고 코드 변경이 필요한 여러 경우에 사용할 수 있다.

  • 프로파일러 (newrelic) 분산 환경에서의 프로파일러 (pinpoint 같은..?)
    애플리케이션이 메모리는 얼마나 쓰는 지, 쓰레드는 몇 개인 지, 어떤 쓰레드가 바쁜 지, 얼마나 일 하고 있는 지...
  • 최적화
  • 로깅
  • ...

스프링이 컴포넌트 스캔을 하는 방법 (asm)

  • 컴포넌트 스캔으로 빈으로 등록할 후보 클래스 정보를 찾는데 사용
  • ClassPathScanningCandidateComponentProvider -> SimpleMetadataReader
  • ClassReader와 Visitor 사용해서 클래스에 있는 메타 정보를 읽어온다.

참고


반응형

'JAVA' 카테고리의 다른 글

make DI Framework by reflection  (0) 2019.09.29
Reflection  (0) 2019.09.29
Byte Code  (0) 2019.09.28
Class Loader  (0) 2019.09.28
JVM  (0) 2019.09.28