Java

바이트코드 조작 정리

박성민 2021. 10. 30. 09:57

바이트코드 조작 툴 활용 예

프로그램 분석

바이트코드를 읽으면서 코드에서 버그를 찾을 수 있습니다. 또한, 코드의 복잡도를 계산할 수 있습니다.

  • 소스코드 없이 바이트코드만 있어도 이러한 것들이 분석 가능합니다.

클래스 파일 생성

원래 소스코드 대신에 실행할 프록시를 만들 수 있습니다.

  • 스프링 AOP, Hibernate의 Lazy Loading, Mock 프레임워크

또한, 특정한 API 호출을 제한(방어)할 수 있습니다. 그리고 스칼라와 같은 JVM 언어들을 컴파일을 할때 바이트코드를 생성해냄으로써 JVM 위에서 돌아갈 수 있게끔 해줍니다.

그밖에

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

  • 프로파일러
    • 프로파일러란 실행중인 애플리케이션이 메모리를 얼만큼 쓰고있는지, 쓰레드가 몇 개인지, 어떤 쓰레드들이 활성화되어 있고 어떤게 가장 오래 일하고 있는지 등의 각종 성능 분석 툴입니다.
    • 이러한 툴들이 javaagent로 바이트코드를 조작해서 자기들이 원하는 정보를 추출하거나 코드를 변경해서 특정 값을 구할 때 사용합니다.
    • ex) Naver의 Pinpoint
  • 최적화
    • 바이트코드를 조작하여 불필요한 코드를 제거하거나 중복 코드를 제거하는 등의 최적화를 할 수 있습니다.
  • 로깅

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

@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {

    ...
}

컴포넌트 스캔으로 빈으로 등록할 후보 클래스 정보를 찾는데 사용합니다. 이때 사용하는 클래스가 ClassPathScanningCandidateComponentProvider입니다. 내부적으로 asm을 사용합니다.

MetadataReader metadataReader = this.getMetadataReaderFactory().getMetadataReader(type);
  • ClassPathScanningCandidateComponentProvider 코드 일부
  • type에서 메타 데이터를 읽어오는데 이때 사용하는게 SimpleMetadataReader 입니다.

SimpleMetadataReader에서 ClassReader와 Visitor 구현체를 이용해서 클래스 또는 메서드에 붙어있는 애노테이션 정보들을 추출해오는데 사용합니다. 즉, 프로그램을 분석하는 것입니다.

package org.springframework.asm;

...

public class ClassReader {

    ...
}
  • ClassReader가 asm에서 온 것입니다.

즉, 이렇게 빈으로 등록할 대상을 찾는데 사용하고 있습니다.

참고

바이트조작 라이브러리로는 ASM, Javassit, ByteBuddy, CGlib 등이 있습니다.

  • ASM은 고전, ByteBuddy는 비교적 최근 라이브러리입니다.
  • 스프링은 ASM을 사용중입니다.
  • Hibernate는 javassist에서 ByteBuddy로 갈아타고 있습니다.
  • Mockito는 CGLib에서 ByteBuddy로 갈아탔습니다.

참조